From 1c18da934a92092eb594b1b0b91fd406bcd39e52 Mon Sep 17 00:00:00 2001
From: Zane <1638944816@qq.com>
Date: Fri, 6 Feb 2026 13:00:17 +0800
Subject: [PATCH 1/2] 00
---
.obsidian/workspace.json | 113 ++-
.../海量渲染战斗/GPU动画工程/GPU蒙皮动画.md | 23 +-
1Project/海量渲染战斗/GPU动画工程/bindpose.md | 5 +
2Areas/游戏开发/物理碰撞/物理碰撞算法.md | 7 +
skills/json-canvas/SKILL.md | 656 ++++++++++++++++++
skills/obsidian-bases/SKILL.md | 651 +++++++++++++++++
skills/obsidian-markdown/SKILL.md | 620 +++++++++++++++++
7 files changed, 2007 insertions(+), 68 deletions(-)
create mode 100644 1Project/海量渲染战斗/GPU动画工程/bindpose.md
create mode 100644 2Areas/游戏开发/物理碰撞/物理碰撞算法.md
create mode 100644 skills/json-canvas/SKILL.md
create mode 100644 skills/obsidian-bases/SKILL.md
create mode 100644 skills/obsidian-markdown/SKILL.md
diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json
index fd57b28..de2c86a 100644
--- a/.obsidian/workspace.json
+++ b/.obsidian/workspace.json
@@ -4,16 +4,16 @@
"type": "split",
"children": [
{
- "id": "023535fd6fa2e67d",
+ "id": "1538f6ad5b15def5",
"type": "tabs",
"children": [
{
- "id": "c3a1fa114f02064b",
+ "id": "afc535d48add9e90",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
- "file": "1Project/租房讨回押金/租新房注意事项.md",
+ "file": "2Areas/游戏开发/pcei速度预估.md",
"mode": "source",
"source": false,
"backlinks": true,
@@ -24,11 +24,11 @@
"showSearch": false,
"searchQuery": "",
"backlinkCollapsed": false,
- "unlinkedCollapsed": false
+ "unlinkedCollapsed": true
}
},
"icon": "lucide-file",
- "title": "租新房注意事项"
+ "title": "pcei速度预估"
}
}
]
@@ -63,7 +63,7 @@
"state": {
"type": "search",
"state": {
- "query": "tag:#flashcards/政治/近代史",
+ "query": "",
"matchingCase": false,
"explainSearch": false,
"collapseAll": false,
@@ -74,16 +74,6 @@
"title": "搜索"
}
},
- {
- "id": "8f2786b27c7d1630",
- "type": "leaf",
- "state": {
- "type": "starred",
- "state": {},
- "icon": "lucide-file",
- "title": "插件不再活动"
- }
- },
{
"id": "8c8dfe109a7f116f",
"type": "leaf",
@@ -107,8 +97,7 @@
"title": "文件列表"
}
}
- ],
- "currentTab": 4
+ ]
}
],
"direction": "horizontal",
@@ -147,8 +136,8 @@
"state": {
"type": "calendar",
"state": {},
- "icon": "calendar-with-checkmark",
- "title": "Calendar"
+ "icon": "lucide-ghost",
+ "title": "calendar"
}
},
{
@@ -210,12 +199,11 @@
}
],
"direction": "horizontal",
- "width": 200,
- "collapsed": true
+ "width": 200
},
"left-ribbon": {
"hiddenItems": {
- "daily-notes:打开/创建今天的日记": true,
+ "bases:创建新数据库": false,
"templates:插入模板": false,
"canvas:新建白板": false,
"switcher:打开快速切换": true,
@@ -224,45 +212,50 @@
"copilot:Open Copilot Chat": false
}
},
- "active": "c3a1fa114f02064b",
+ "active": "f2ac97a7addb8678",
"lastOpenFiles": [
+ "2Areas/游戏开发/索引.md",
+ "skills/obsidian-markdown/SKILL.md",
+ "skills/obsidian-bases/SKILL.md",
+ "skills/json-canvas/SKILL.md",
+ "skills/obsidian-markdown",
+ "skills/obsidian-bases",
+ "skills/json-canvas",
+ "skills",
+ "未命名.md",
+ "2Areas/游戏开发/pcei速度预估.md",
+ "2Areas/游戏开发/物理碰撞/物理碰撞算法.md",
+ "1Project/海量渲染战斗/GPU动画工程/GPU蒙皮动画.md",
+ "2Areas/游戏开发/物理碰撞",
+ "1Project/海量渲染战斗/GPU动画工程/GPU顶点动画.md",
+ "1Project/海量渲染战斗/GPU动画工程/bindpose.md",
+ "1Project/GPU地形/Houdini PCG.md",
+ "1Project/GPU地形/GPU Driven Terrain 跟学与实现.md",
+ "渲染/软渲染/图片/9702b7cbafa93d6e3d0cd59e7cf37e31.png",
+ "渲染/软渲染/图片/86aa46af36c4c3b1f1efef2a1ccc6310.png",
+ "渲染/软渲染/图片/62ad22d3e981954fc083018fb850413a.png",
+ "渲染/软渲染/图片/23d61a2b7376ea3149d74d6bd4ea9430.png",
+ "2Areas/年度复盘/2025.md",
+ "1Project/ZYGame/州府前端流程.md",
+ "1Project/海量渲染战斗/GPU动画工程/TODO转为Job组织数据.md",
+ "1Project/海量渲染战斗/GPU动画工程/TODO将Time存进buffer中,做成不同的实例可以使用不同的动画时间.md",
+ "1Project/海量渲染战斗/GPU动画工程/TODO不同动作存进Buffer中.md",
+ "1Project/海量渲染战斗/GPU动画工程/TODO 不同动作间的混合.md",
+ "1Project/海量渲染战斗/GPU动画.md",
+ "1Project/海量渲染战斗/GPU动画工程",
+ "1Project/海量渲染战斗/批量渲染gpu动画.md",
+ "1Project/海量渲染战斗/基于GPU蒙皮动画的Spine实现.md",
+ "1Project/海量渲染战斗/基于GPU动画和ComputeShader的大批量动画渲染Demo(一)之GPU顶点动画.md",
+ "1Project/ZYGame",
+ "1Project/GPU地形/ComputeShader生成无限世界地形.md",
+ "2Areas/游戏开发/fgui&ugui合批记录.md",
+ "1Project/图文合批/字体字形图片申请.md",
+ "1Project/图文合批/图文使用不同的Tex2DArr.md",
+ "1Project/UE",
+ "1Project/海量渲染战斗",
+ "1Project/GPU地形",
"wallhaven-rq75r7.jpg",
- "1Project/租房讨回押金/租新房注意事项.md",
- "1Project/租房讨回押金/处理流程.md",
- "1Project/宠物宇宙/地图编辑器/流程组件.md",
- "3Resources/书本/毛选/第一章.md",
- "1Project/中原精英/RoadMap.md",
- "1Project/学习/GAS.md",
- "1Project/学习/开发文章/将开发文章阅读并分类.md",
- "1Project/宠物宇宙/版本需求.md",
- "1Project/宠物宇宙/地图编辑器/需要做的优化.md",
- "1Project/宠物宇宙/地图编辑器/需要支持的地图功能.md",
- "4Archives/宠物宇宙/用户卡及游戏信息处理过程设计文档.md",
- "1Project/宠物宇宙/皮肤导入.md",
- "1Project/框架搭建.md",
- "1Project/构建新框架/管道模式/依赖环节的流程管线.md",
- "2Areas/开发感悟/接口与抽象类区别.md",
"1Project/构建新框架/管道模式/未命名.canvas",
- "4Archives/宠物宇宙",
- "4Archives/AI for Obsidian/20250315 - 测试 Obsidian+ AI 的能力边界/第五个用法|成为插件助手/Dataview 插件/测试用文档/项目复盘会议.md",
- "4Archives/AI for Obsidian/20250315 - 测试 Obsidian+ AI 的能力边界/第五个用法|成为插件助手/Dataview 插件/测试用文档/项目A计划.md",
- "4Archives/AI for Obsidian/20250315 - 测试 Obsidian+ AI 的能力边界/第五个用法|成为插件助手/Dataview 插件/测试用文档/财务季度报告.md",
- "4Archives/AI for Obsidian/20250315 - 测试 Obsidian+ AI 的能力边界/第五个用法|成为插件助手/Dataview 插件/测试用文档/简易待办事项.md",
- "4Archives/AI for Obsidian/20250315 - 测试 Obsidian+ AI 的能力边界/第五个用法|成为插件助手/Dataview 插件/测试用文档/用户反馈分析.md",
- "4Archives/AI for Obsidian/20250315 - 测试 Obsidian+ AI 的能力边界/第五个用法|成为插件助手/Dataview 插件/测试用文档/每周例会记录.md",
- "4Archives/AI for Obsidian/20250315 - 测试 Obsidian+ AI 的能力边界/第五个用法|成为插件助手/Dataview 插件/测试用文档/技术架构设计.md",
- "4Archives/AI for Obsidian/20250315 - 测试 Obsidian+ AI 的能力边界/第五个用法|成为插件助手/Dataview 插件/测试用文档/市场调研报告.md",
- "4Archives/AI for Obsidian/20250315 - 测试 Obsidian+ AI 的能力边界/第五个用法|成为插件助手/Dataview 插件/测试用文档/团队建设活动.md",
- "4Archives/AI for Obsidian/20250315 - 测试 Obsidian+ AI 的能力边界/第五个用法|成为插件助手/Dataview 插件/测试用文档/产品功能需求.md",
- "4Archives/AI for Obsidian/20250315 - 测试 Obsidian+ AI 的能力边界/第四个用法|成为对弈的棋手/正方观点/观点9.md.edtz",
- "4Archives/AI for Obsidian/20250315 - 测试 Obsidian+ AI 的能力边界/第四个用法|成为对弈的棋手/正方观点/观点8.md.edtz",
- "4Archives/AI for Obsidian/20250315 - 测试 Obsidian+ AI 的能力边界/第四个用法|成为对弈的棋手/正方观点/观点7.md.edtz",
- "4Archives/AI for Obsidian/20250315 - 测试 Obsidian+ AI 的能力边界/第四个用法|成为对弈的棋手/正方观点/观点6.md.edtz",
- "4Archives/AI for Obsidian/20250315 - 测试 Obsidian+ AI 的能力边界/第四个用法|成为对弈的棋手/正方观点/观点5.md.edtz",
- "4Archives/AI for Obsidian/20250315 - 测试 Obsidian+ AI 的能力边界/第四个用法|成为对弈的棋手/正方观点/观点4.md.edtz",
- "4Archives/AI for Obsidian/20250315 - 测试 Obsidian+ AI 的能力边界/第四个用法|成为对弈的棋手/正方观点/观点3.md.edtz",
- "4Archives/AI for Obsidian/20250315 - 测试 Obsidian+ AI 的能力边界/第四个用法|成为对弈的棋手/正方观点/观点2.md.edtz",
- "4Archives/AI for Obsidian/20250315 - 测试 Obsidian+ AI 的能力边界/第四个用法|成为对弈的棋手/正方观点/观点10.md.edtz",
"2Areas/如何记录笔记/P.A.R.A/Pasted image 20250614192838.png",
"1Project/战斗编辑器/随想/动作时间轴 随想.canvas",
"2Areas/开发感悟/管道模式/未命名.canvas",
@@ -273,8 +266,6 @@
"InBox/TODO.canvas",
"渲染/软渲染/图片/Pasted image 20250609171235.png",
"InBox/渲染/GPU/Pasted image 20230413163234.png",
- "InBox/Mesh/图片/Pasted image 20221021171104.png",
- "InBox/渲染/软渲染/图片/Pasted image 20230613102109.png",
"未命名 1.canvas",
"并行计算/GPU.canvas",
"并行计算/CPU.canvas",
diff --git a/1Project/海量渲染战斗/GPU动画工程/GPU蒙皮动画.md b/1Project/海量渲染战斗/GPU动画工程/GPU蒙皮动画.md
index 49de22c..7874e5a 100644
--- a/1Project/海量渲染战斗/GPU动画工程/GPU蒙皮动画.md
+++ b/1Project/海量渲染战斗/GPU动画工程/GPU蒙皮动画.md
@@ -22,13 +22,22 @@
你的当前顶点动画管线可以直接作为“LOD 动画层”保留,不与 GPU Skinning 冲突,只要切换 Renderer/Material 即可。
#### 蒙皮动画需要记录的数据有哪些?
- 一张原始网格的Vector3数据贴图
-- 收集骨骼和骨骼索引
-- 重映射后的骨骼索引
- - 如果之前
-- 一个动作
- - 该动作中骨骼每帧的坐标旋转
- - 会在Shader中计算**混合骨骼**后的TRS * 原始网格的TRS
- -
+- 收集[[bindpose]]
+- 收集全局骨骼索引
+- 根据不同的蒙皮组件需要的骨骼,重新映射索引
+ - 为什么需要这个?
+ - 身体的骨骼索引,索引分别是1、2、3、4
+ - 腰
+ - 肩膀
+ - 手臂
+ - 手掌
+ - 而手臂为,索引为3、4
+ - 手臂
+ - 手掌
+- 该动作中每个网格的顶点计算:
+ - Root在世界空间坐标的TRS
+ - 每根骨骼还原[[bindpose]]
+ - **混合骨骼**后的TRS * 原始网格的TRS
#### 蒙皮动画不同部位索引的问题
- 蒙皮的骨骼索引在cpu中无问题,在gpu中有问题
diff --git a/1Project/海量渲染战斗/GPU动画工程/bindpose.md b/1Project/海量渲染战斗/GPU动画工程/bindpose.md
new file mode 100644
index 0000000..3dfd12e
--- /dev/null
+++ b/1Project/海量渲染战斗/GPU动画工程/bindpose.md
@@ -0,0 +1,5 @@
+动作文件在导入Unity后会将网格顶点绑定在**蒙皮节点空间**下的坐标储存在bindpose中。
+- 例如:如果根节点在(0,0,0),骨骼在(0,0,10),网格顶点在骨骼空间坐标为(0,0,1),在导入之后,顶点坐标会被保存为(0,0,11);
+
+
+- GPU蒙皮动作中,需要将顶点还原为**骨骼节点空间下**坐标后再重新从root开始算
\ No newline at end of file
diff --git a/2Areas/游戏开发/物理碰撞/物理碰撞算法.md b/2Areas/游戏开发/物理碰撞/物理碰撞算法.md
new file mode 100644
index 0000000..55ff5fb
--- /dev/null
+++ b/2Areas/游戏开发/物理碰撞/物理碰撞算法.md
@@ -0,0 +1,7 @@
+# AABB
+
+# SAT 分离轴定理
+
+# GJK 明可夫斯基差
+- 两个形状每个顶点两两相差得出新的形状,如果原点位于形状内,则有碰撞
+ - 再次优化,有选择的相减,是否有最简模型
\ No newline at end of file
diff --git a/skills/json-canvas/SKILL.md b/skills/json-canvas/SKILL.md
new file mode 100644
index 0000000..0ddd0e4
--- /dev/null
+++ b/skills/json-canvas/SKILL.md
@@ -0,0 +1,656 @@
+---
+name: json-canvas
+description: Create and edit JSON Canvas files (.canvas) with nodes, edges, groups, and connections. Use when working with .canvas files, creating visual canvases, mind maps, flowcharts, or when the user mentions Canvas files in Obsidian.
+---
+
+# JSON Canvas Skill
+
+This skill enables skills-compatible agents to create and edit valid JSON Canvas files (`.canvas`) used in Obsidian and other applications.
+
+## Overview
+
+JSON Canvas is an open file format for infinite canvas data. Canvas files use the `.canvas` extension and contain valid JSON following the [JSON Canvas Spec 1.0](https://jsoncanvas.org/spec/1.0/).
+
+## File Structure
+
+A canvas file contains two top-level arrays:
+
+```json
+{
+ "nodes": [],
+ "edges": []
+}
+```
+
+- `nodes` (optional): Array of node objects
+- `edges` (optional): Array of edge objects connecting nodes
+
+## Nodes
+
+Nodes are objects placed on the canvas. There are four node types:
+- `text` - Text content with Markdown
+- `file` - Reference to files/attachments
+- `link` - External URL
+- `group` - Visual container for other nodes
+
+### Z-Index Ordering
+
+Nodes are ordered by z-index in the array:
+- First node = bottom layer (displayed below others)
+- Last node = top layer (displayed above others)
+
+### Generic Node Attributes
+
+All nodes share these attributes:
+
+| Attribute | Required | Type | Description |
+|-----------|----------|------|-------------|
+| `id` | Yes | string | Unique identifier for the node |
+| `type` | Yes | string | Node type: `text`, `file`, `link`, or `group` |
+| `x` | Yes | integer | X position in pixels |
+| `y` | Yes | integer | Y position in pixels |
+| `width` | Yes | integer | Width in pixels |
+| `height` | Yes | integer | Height in pixels |
+| `color` | No | canvasColor | Node color (see Color section) |
+
+### Text Nodes
+
+Text nodes contain Markdown content.
+
+```json
+{
+ "id": "6f0ad84f44ce9c17",
+ "type": "text",
+ "x": 0,
+ "y": 0,
+ "width": 400,
+ "height": 200,
+ "text": "# Hello World\n\nThis is **Markdown** content."
+}
+```
+
+#### Newline Escaping (Common Pitfall)
+
+In JSON, newline characters inside strings **must** be represented as `\n`. Do **not** use the literal sequence `\\n` in a `.canvas` file—Obsidian will render it as the characters `\` and `n` instead of a line break.
+
+Examples:
+
+```json
+{ "type": "text", "text": "Line 1\nLine 2" }
+```
+
+```json
+{ "type": "text", "text": "Line 1\\nLine 2" }
+```
+
+| Attribute | Required | Type | Description |
+|-----------|----------|------|-------------|
+| `text` | Yes | string | Plain text with Markdown syntax |
+
+### File Nodes
+
+File nodes reference files or attachments (images, videos, PDFs, notes, etc.).
+
+```json
+{
+ "id": "a1b2c3d4e5f67890",
+ "type": "file",
+ "x": 500,
+ "y": 0,
+ "width": 400,
+ "height": 300,
+ "file": "Attachments/diagram.png"
+}
+```
+
+```json
+{
+ "id": "b2c3d4e5f6789012",
+ "type": "file",
+ "x": 500,
+ "y": 400,
+ "width": 400,
+ "height": 300,
+ "file": "Notes/Project Overview.md",
+ "subpath": "#Implementation"
+}
+```
+
+| Attribute | Required | Type | Description |
+|-----------|----------|------|-------------|
+| `file` | Yes | string | Path to file within the system |
+| `subpath` | No | string | Link to heading or block (starts with `#`) |
+
+### Link Nodes
+
+Link nodes display external URLs.
+
+```json
+{
+ "id": "c3d4e5f678901234",
+ "type": "link",
+ "x": 1000,
+ "y": 0,
+ "width": 400,
+ "height": 200,
+ "url": "https://obsidian.md"
+}
+```
+
+| Attribute | Required | Type | Description |
+|-----------|----------|------|-------------|
+| `url` | Yes | string | External URL |
+
+### Group Nodes
+
+Group nodes are visual containers for organizing other nodes.
+
+```json
+{
+ "id": "d4e5f6789012345a",
+ "type": "group",
+ "x": -50,
+ "y": -50,
+ "width": 1000,
+ "height": 600,
+ "label": "Project Overview",
+ "color": "4"
+}
+```
+
+```json
+{
+ "id": "e5f67890123456ab",
+ "type": "group",
+ "x": 0,
+ "y": 700,
+ "width": 800,
+ "height": 500,
+ "label": "Resources",
+ "background": "Attachments/background.png",
+ "backgroundStyle": "cover"
+}
+```
+
+| Attribute | Required | Type | Description |
+|-----------|----------|------|-------------|
+| `label` | No | string | Text label for the group |
+| `background` | No | string | Path to background image |
+| `backgroundStyle` | No | string | Background rendering style |
+
+#### Background Styles
+
+| Value | Description |
+|-------|-------------|
+| `cover` | Fills entire width and height of node |
+| `ratio` | Maintains aspect ratio of background image |
+| `repeat` | Repeats image as pattern in both directions |
+
+## Edges
+
+Edges are lines connecting nodes.
+
+```json
+{
+ "id": "f67890123456789a",
+ "fromNode": "6f0ad84f44ce9c17",
+ "toNode": "a1b2c3d4e5f67890"
+}
+```
+
+```json
+{
+ "id": "0123456789abcdef",
+ "fromNode": "6f0ad84f44ce9c17",
+ "fromSide": "right",
+ "fromEnd": "none",
+ "toNode": "b2c3d4e5f6789012",
+ "toSide": "left",
+ "toEnd": "arrow",
+ "color": "1",
+ "label": "leads to"
+}
+```
+
+| Attribute | Required | Type | Default | Description |
+|-----------|----------|------|---------|-------------|
+| `id` | Yes | string | - | Unique identifier for the edge |
+| `fromNode` | Yes | string | - | Node ID where connection starts |
+| `fromSide` | No | string | - | Side where edge starts |
+| `fromEnd` | No | string | `none` | Shape at edge start |
+| `toNode` | Yes | string | - | Node ID where connection ends |
+| `toSide` | No | string | - | Side where edge ends |
+| `toEnd` | No | string | `arrow` | Shape at edge end |
+| `color` | No | canvasColor | - | Line color |
+| `label` | No | string | - | Text label for the edge |
+
+### Side Values
+
+| Value | Description |
+|-------|-------------|
+| `top` | Top edge of node |
+| `right` | Right edge of node |
+| `bottom` | Bottom edge of node |
+| `left` | Left edge of node |
+
+### End Shapes
+
+| Value | Description |
+|-------|-------------|
+| `none` | No endpoint shape |
+| `arrow` | Arrow endpoint |
+
+## Colors
+
+The `canvasColor` type can be specified in two ways:
+
+### Hex Colors
+
+```json
+{
+ "color": "#FF0000"
+}
+```
+
+### Preset Colors
+
+```json
+{
+ "color": "1"
+}
+```
+
+| Preset | Color |
+|--------|-------|
+| `"1"` | Red |
+| `"2"` | Orange |
+| `"3"` | Yellow |
+| `"4"` | Green |
+| `"5"` | Cyan |
+| `"6"` | Purple |
+
+Note: Specific color values for presets are intentionally undefined, allowing applications to use their own brand colors.
+
+## Complete Examples
+
+### Simple Canvas with Text and Connections
+
+```json
+{
+ "nodes": [
+ {
+ "id": "8a9b0c1d2e3f4a5b",
+ "type": "text",
+ "x": 0,
+ "y": 0,
+ "width": 300,
+ "height": 150,
+ "text": "# Main Idea\n\nThis is the central concept."
+ },
+ {
+ "id": "1a2b3c4d5e6f7a8b",
+ "type": "text",
+ "x": 400,
+ "y": -100,
+ "width": 250,
+ "height": 100,
+ "text": "## Supporting Point A\n\nDetails here."
+ },
+ {
+ "id": "2b3c4d5e6f7a8b9c",
+ "type": "text",
+ "x": 400,
+ "y": 100,
+ "width": 250,
+ "height": 100,
+ "text": "## Supporting Point B\n\nMore details."
+ }
+ ],
+ "edges": [
+ {
+ "id": "3c4d5e6f7a8b9c0d",
+ "fromNode": "8a9b0c1d2e3f4a5b",
+ "fromSide": "right",
+ "toNode": "1a2b3c4d5e6f7a8b",
+ "toSide": "left"
+ },
+ {
+ "id": "4d5e6f7a8b9c0d1e",
+ "fromNode": "8a9b0c1d2e3f4a5b",
+ "fromSide": "right",
+ "toNode": "2b3c4d5e6f7a8b9c",
+ "toSide": "left"
+ }
+ ]
+}
+```
+
+### Project Board with Groups
+
+```json
+{
+ "nodes": [
+ {
+ "id": "5e6f7a8b9c0d1e2f",
+ "type": "group",
+ "x": 0,
+ "y": 0,
+ "width": 300,
+ "height": 500,
+ "label": "To Do",
+ "color": "1"
+ },
+ {
+ "id": "6f7a8b9c0d1e2f3a",
+ "type": "group",
+ "x": 350,
+ "y": 0,
+ "width": 300,
+ "height": 500,
+ "label": "In Progress",
+ "color": "3"
+ },
+ {
+ "id": "7a8b9c0d1e2f3a4b",
+ "type": "group",
+ "x": 700,
+ "y": 0,
+ "width": 300,
+ "height": 500,
+ "label": "Done",
+ "color": "4"
+ },
+ {
+ "id": "8b9c0d1e2f3a4b5c",
+ "type": "text",
+ "x": 20,
+ "y": 50,
+ "width": 260,
+ "height": 80,
+ "text": "## Task 1\n\nImplement feature X"
+ },
+ {
+ "id": "9c0d1e2f3a4b5c6d",
+ "type": "text",
+ "x": 370,
+ "y": 50,
+ "width": 260,
+ "height": 80,
+ "text": "## Task 2\n\nReview PR #123",
+ "color": "2"
+ },
+ {
+ "id": "0d1e2f3a4b5c6d7e",
+ "type": "text",
+ "x": 720,
+ "y": 50,
+ "width": 260,
+ "height": 80,
+ "text": "## Task 3\n\n~~Setup CI/CD~~"
+ }
+ ],
+ "edges": []
+}
+```
+
+### Research Canvas with Files and Links
+
+```json
+{
+ "nodes": [
+ {
+ "id": "1e2f3a4b5c6d7e8f",
+ "type": "text",
+ "x": 300,
+ "y": 200,
+ "width": 400,
+ "height": 200,
+ "text": "# Research Topic\n\n## Key Questions\n\n- How does X affect Y?\n- What are the implications?",
+ "color": "5"
+ },
+ {
+ "id": "2f3a4b5c6d7e8f9a",
+ "type": "file",
+ "x": 0,
+ "y": 0,
+ "width": 250,
+ "height": 150,
+ "file": "Literature/Paper A.pdf"
+ },
+ {
+ "id": "3a4b5c6d7e8f9a0b",
+ "type": "file",
+ "x": 0,
+ "y": 200,
+ "width": 250,
+ "height": 150,
+ "file": "Notes/Meeting Notes.md",
+ "subpath": "#Key Insights"
+ },
+ {
+ "id": "4b5c6d7e8f9a0b1c",
+ "type": "link",
+ "x": 0,
+ "y": 400,
+ "width": 250,
+ "height": 100,
+ "url": "https://example.com/research"
+ },
+ {
+ "id": "5c6d7e8f9a0b1c2d",
+ "type": "file",
+ "x": 750,
+ "y": 150,
+ "width": 300,
+ "height": 250,
+ "file": "Attachments/diagram.png"
+ }
+ ],
+ "edges": [
+ {
+ "id": "6d7e8f9a0b1c2d3e",
+ "fromNode": "2f3a4b5c6d7e8f9a",
+ "fromSide": "right",
+ "toNode": "1e2f3a4b5c6d7e8f",
+ "toSide": "left",
+ "label": "supports"
+ },
+ {
+ "id": "7e8f9a0b1c2d3e4f",
+ "fromNode": "3a4b5c6d7e8f9a0b",
+ "fromSide": "right",
+ "toNode": "1e2f3a4b5c6d7e8f",
+ "toSide": "left",
+ "label": "informs"
+ },
+ {
+ "id": "8f9a0b1c2d3e4f5a",
+ "fromNode": "4b5c6d7e8f9a0b1c",
+ "fromSide": "right",
+ "toNode": "1e2f3a4b5c6d7e8f",
+ "toSide": "left",
+ "toEnd": "arrow",
+ "color": "6"
+ },
+ {
+ "id": "9a0b1c2d3e4f5a6b",
+ "fromNode": "1e2f3a4b5c6d7e8f",
+ "fromSide": "right",
+ "toNode": "5c6d7e8f9a0b1c2d",
+ "toSide": "left",
+ "label": "visualized by"
+ }
+ ]
+}
+```
+
+### Flowchart
+
+```json
+{
+ "nodes": [
+ {
+ "id": "a0b1c2d3e4f5a6b7",
+ "type": "text",
+ "x": 200,
+ "y": 0,
+ "width": 150,
+ "height": 60,
+ "text": "**Start**",
+ "color": "4"
+ },
+ {
+ "id": "b1c2d3e4f5a6b7c8",
+ "type": "text",
+ "x": 200,
+ "y": 100,
+ "width": 150,
+ "height": 60,
+ "text": "Step 1:\nGather data"
+ },
+ {
+ "id": "c2d3e4f5a6b7c8d9",
+ "type": "text",
+ "x": 200,
+ "y": 200,
+ "width": 150,
+ "height": 80,
+ "text": "**Decision**\n\nIs data valid?",
+ "color": "3"
+ },
+ {
+ "id": "d3e4f5a6b7c8d9e0",
+ "type": "text",
+ "x": 400,
+ "y": 200,
+ "width": 150,
+ "height": 60,
+ "text": "Process data"
+ },
+ {
+ "id": "e4f5a6b7c8d9e0f1",
+ "type": "text",
+ "x": 0,
+ "y": 200,
+ "width": 150,
+ "height": 60,
+ "text": "Request new data",
+ "color": "1"
+ },
+ {
+ "id": "f5a6b7c8d9e0f1a2",
+ "type": "text",
+ "x": 400,
+ "y": 320,
+ "width": 150,
+ "height": 60,
+ "text": "**End**",
+ "color": "4"
+ }
+ ],
+ "edges": [
+ {
+ "id": "a6b7c8d9e0f1a2b3",
+ "fromNode": "a0b1c2d3e4f5a6b7",
+ "fromSide": "bottom",
+ "toNode": "b1c2d3e4f5a6b7c8",
+ "toSide": "top"
+ },
+ {
+ "id": "b7c8d9e0f1a2b3c4",
+ "fromNode": "b1c2d3e4f5a6b7c8",
+ "fromSide": "bottom",
+ "toNode": "c2d3e4f5a6b7c8d9",
+ "toSide": "top"
+ },
+ {
+ "id": "c8d9e0f1a2b3c4d5",
+ "fromNode": "c2d3e4f5a6b7c8d9",
+ "fromSide": "right",
+ "toNode": "d3e4f5a6b7c8d9e0",
+ "toSide": "left",
+ "label": "Yes",
+ "color": "4"
+ },
+ {
+ "id": "d9e0f1a2b3c4d5e6",
+ "fromNode": "c2d3e4f5a6b7c8d9",
+ "fromSide": "left",
+ "toNode": "e4f5a6b7c8d9e0f1",
+ "toSide": "right",
+ "label": "No",
+ "color": "1"
+ },
+ {
+ "id": "e0f1a2b3c4d5e6f7",
+ "fromNode": "e4f5a6b7c8d9e0f1",
+ "fromSide": "top",
+ "fromEnd": "none",
+ "toNode": "b1c2d3e4f5a6b7c8",
+ "toSide": "left",
+ "toEnd": "arrow"
+ },
+ {
+ "id": "f1a2b3c4d5e6f7a8",
+ "fromNode": "d3e4f5a6b7c8d9e0",
+ "fromSide": "bottom",
+ "toNode": "f5a6b7c8d9e0f1a2",
+ "toSide": "top"
+ }
+ ]
+}
+```
+
+## ID Generation
+
+Node and edge IDs must be unique strings. Obsidian generates 16-character hexadecimal IDs:
+
+```json
+"id": "6f0ad84f44ce9c17"
+"id": "a3b2c1d0e9f8g7h6"
+"id": "1234567890abcdef"
+```
+
+This format is a 16-character lowercase hex string (64-bit random value).
+
+## Layout Guidelines
+
+### Positioning
+
+- Coordinates can be negative (canvas extends infinitely)
+- `x` increases to the right
+- `y` increases downward
+- Position refers to top-left corner of node
+
+### Recommended Sizes
+
+| Node Type | Suggested Width | Suggested Height |
+|-----------|-----------------|------------------|
+| Small text | 200-300 | 80-150 |
+| Medium text | 300-450 | 150-300 |
+| Large text | 400-600 | 300-500 |
+| File preview | 300-500 | 200-400 |
+| Link preview | 250-400 | 100-200 |
+| Group | Varies | Varies |
+
+### Spacing
+
+- Leave 20-50px padding inside groups
+- Space nodes 50-100px apart for readability
+- Align nodes to grid (multiples of 10 or 20) for cleaner layouts
+
+## Validation Rules
+
+1. All `id` values must be unique across nodes and edges
+2. `fromNode` and `toNode` must reference existing node IDs
+3. Required fields must be present for each node type
+4. `type` must be one of: `text`, `file`, `link`, `group`
+5. `backgroundStyle` must be one of: `cover`, `ratio`, `repeat`
+6. `fromSide`, `toSide` must be one of: `top`, `right`, `bottom`, `left`
+7. `fromEnd`, `toEnd` must be one of: `none`, `arrow`
+8. Color presets must be `"1"` through `"6"` or valid hex color
+
+## References
+
+- [JSON Canvas Spec 1.0](https://jsoncanvas.org/spec/1.0/)
+- [JSON Canvas GitHub](https://github.com/obsidianmd/jsoncanvas)
diff --git a/skills/obsidian-bases/SKILL.md b/skills/obsidian-bases/SKILL.md
new file mode 100644
index 0000000..4b27f7e
--- /dev/null
+++ b/skills/obsidian-bases/SKILL.md
@@ -0,0 +1,651 @@
+---
+name: obsidian-bases
+description: Create and edit Obsidian Bases (.base files) with views, filters, formulas, and summaries. Use when working with .base files, creating database-like views of notes, or when the user mentions Bases, table views, card views, filters, or formulas in Obsidian.
+---
+
+# Obsidian Bases Skill
+
+This skill enables skills-compatible agents to create and edit valid Obsidian Bases (`.base` files) including views, filters, formulas, and all related configurations.
+
+## Overview
+
+Obsidian Bases are YAML-based files that define dynamic views of notes in an Obsidian vault. A Base file can contain multiple views, global filters, formulas, property configurations, and custom summaries.
+
+## File Format
+
+Base files use the `.base` extension and contain valid YAML. They can also be embedded in Markdown code blocks.
+
+## Complete Schema
+
+```yaml
+# Global filters apply to ALL views in the base
+filters:
+ # Can be a single filter string
+ # OR a recursive filter object with and/or/not
+ and: []
+ or: []
+ not: []
+
+# Define formula properties that can be used across all views
+formulas:
+ formula_name: 'expression'
+
+# Configure display names and settings for properties
+properties:
+ property_name:
+ displayName: "Display Name"
+ formula.formula_name:
+ displayName: "Formula Display Name"
+ file.ext:
+ displayName: "Extension"
+
+# Define custom summary formulas
+summaries:
+ custom_summary_name: 'values.mean().round(3)'
+
+# Define one or more views
+views:
+ - type: table | cards | list | map
+ name: "View Name"
+ limit: 10 # Optional: limit results
+ groupBy: # Optional: group results
+ property: property_name
+ direction: ASC | DESC
+ filters: # View-specific filters
+ and: []
+ order: # Properties to display in order
+ - file.name
+ - property_name
+ - formula.formula_name
+ summaries: # Map properties to summary formulas
+ property_name: Average
+```
+
+## Filter Syntax
+
+Filters narrow down results. They can be applied globally or per-view.
+
+### Filter Structure
+
+```yaml
+# Single filter
+filters: 'status == "done"'
+
+# AND - all conditions must be true
+filters:
+ and:
+ - 'status == "done"'
+ - 'priority > 3'
+
+# OR - any condition can be true
+filters:
+ or:
+ - 'file.hasTag("book")'
+ - 'file.hasTag("article")'
+
+# NOT - exclude matching items
+filters:
+ not:
+ - 'file.hasTag("archived")'
+
+# Nested filters
+filters:
+ or:
+ - file.hasTag("tag")
+ - and:
+ - file.hasTag("book")
+ - file.hasLink("Textbook")
+ - not:
+ - file.hasTag("book")
+ - file.inFolder("Required Reading")
+```
+
+### Filter Operators
+
+| Operator | Description |
+|----------|-------------|
+| `==` | equals |
+| `!=` | not equal |
+| `>` | greater than |
+| `<` | less than |
+| `>=` | greater than or equal |
+| `<=` | less than or equal |
+| `&&` | logical and |
+| `\|\|` | logical or |
+| ! | logical not |
+
+## Properties
+
+### Three Types of Properties
+
+1. **Note properties** - From frontmatter: `note.author` or just `author`
+2. **File properties** - File metadata: `file.name`, `file.mtime`, etc.
+3. **Formula properties** - Computed values: `formula.my_formula`
+
+### File Properties Reference
+
+| Property | Type | Description |
+|----------|------|-------------|
+| `file.name` | String | File name |
+| `file.basename` | String | File name without extension |
+| `file.path` | String | Full path to file |
+| `file.folder` | String | Parent folder path |
+| `file.ext` | String | File extension |
+| `file.size` | Number | File size in bytes |
+| `file.ctime` | Date | Created time |
+| `file.mtime` | Date | Modified time |
+| `file.tags` | List | All tags in file |
+| `file.links` | List | Internal links in file |
+| `file.backlinks` | List | Files linking to this file |
+| `file.embeds` | List | Embeds in the note |
+| `file.properties` | Object | All frontmatter properties |
+
+### The `this` Keyword
+
+- In main content area: refers to the base file itself
+- When embedded: refers to the embedding file
+- In sidebar: refers to the active file in main content
+
+## Formula Syntax
+
+Formulas compute values from properties. Defined in the `formulas` section.
+
+```yaml
+formulas:
+ # Simple arithmetic
+ total: "price * quantity"
+
+ # Conditional logic
+ status_icon: 'if(done, "✅", "⏳")'
+
+ # String formatting
+ formatted_price: 'if(price, price.toFixed(2) + " dollars")'
+
+ # Date formatting
+ created: 'file.ctime.format("YYYY-MM-DD")'
+
+ # Calculate days since created (use .days for Duration)
+ days_old: '(now() - file.ctime).days'
+
+ # Calculate days until due date
+ days_until_due: 'if(due_date, (date(due_date) - today()).days, "")'
+```
+
+## Functions Reference
+
+### Global Functions
+
+| Function | Signature | Description |
+|----------|-----------|-------------|
+| `date()` | `date(string): date` | Parse string to date. Format: `YYYY-MM-DD HH:mm:ss` |
+| `duration()` | `duration(string): duration` | Parse duration string |
+| `now()` | `now(): date` | Current date and time |
+| `today()` | `today(): date` | Current date (time = 00:00:00) |
+| `if()` | `if(condition, trueResult, falseResult?)` | Conditional |
+| `min()` | `min(n1, n2, ...): number` | Smallest number |
+| `max()` | `max(n1, n2, ...): number` | Largest number |
+| `number()` | `number(any): number` | Convert to number |
+| `link()` | `link(path, display?): Link` | Create a link |
+| `list()` | `list(element): List` | Wrap in list if not already |
+| `file()` | `file(path): file` | Get file object |
+| `image()` | `image(path): image` | Create image for rendering |
+| `icon()` | `icon(name): icon` | Lucide icon by name |
+| `html()` | `html(string): html` | Render as HTML |
+| `escapeHTML()` | `escapeHTML(string): string` | Escape HTML characters |
+
+### Any Type Functions
+
+| Function | Signature | Description |
+|----------|-----------|-------------|
+| `isTruthy()` | `any.isTruthy(): boolean` | Coerce to boolean |
+| `isType()` | `any.isType(type): boolean` | Check type |
+| `toString()` | `any.toString(): string` | Convert to string |
+
+### Date Functions & Fields
+
+**Fields:** `date.year`, `date.month`, `date.day`, `date.hour`, `date.minute`, `date.second`, `date.millisecond`
+
+| Function | Signature | Description |
+|----------|-----------|-------------|
+| `date()` | `date.date(): date` | Remove time portion |
+| `format()` | `date.format(string): string` | Format with Moment.js pattern |
+| `time()` | `date.time(): string` | Get time as string |
+| `relative()` | `date.relative(): string` | Human-readable relative time |
+| `isEmpty()` | `date.isEmpty(): boolean` | Always false for dates |
+
+### Duration Type
+
+When subtracting two dates, the result is a **Duration** type (not a number). Duration has its own properties and methods.
+
+**Duration Fields:**
+| Field | Type | Description |
+|-------|------|-------------|
+| `duration.days` | Number | Total days in duration |
+| `duration.hours` | Number | Total hours in duration |
+| `duration.minutes` | Number | Total minutes in duration |
+| `duration.seconds` | Number | Total seconds in duration |
+| `duration.milliseconds` | Number | Total milliseconds in duration |
+
+**IMPORTANT:** Duration does NOT support `.round()`, `.floor()`, `.ceil()` directly. You must access a numeric field first (like `.days`), then apply number functions.
+
+```yaml
+# CORRECT: Calculate days between dates
+"(date(due_date) - today()).days" # Returns number of days
+"(now() - file.ctime).days" # Days since created
+
+# CORRECT: Round the numeric result if needed
+"(date(due_date) - today()).days.round(0)" # Rounded days
+"(now() - file.ctime).hours.round(0)" # Rounded hours
+
+# WRONG - will cause error:
+# "((date(due) - today()) / 86400000).round(0)" # Duration doesn't support division then round
+```
+
+### Date Arithmetic
+
+```yaml
+# Duration units: y/year/years, M/month/months, d/day/days,
+# w/week/weeks, h/hour/hours, m/minute/minutes, s/second/seconds
+
+# Add/subtract durations
+"date + \"1M\"" # Add 1 month
+"date - \"2h\"" # Subtract 2 hours
+"now() + \"1 day\"" # Tomorrow
+"today() + \"7d\"" # A week from today
+
+# Subtract dates returns Duration type
+"now() - file.ctime" # Returns Duration
+"(now() - file.ctime).days" # Get days as number
+"(now() - file.ctime).hours" # Get hours as number
+
+# Complex duration arithmetic
+"now() + (duration('1d') * 2)"
+```
+
+### String Functions
+
+**Field:** `string.length`
+
+| Function | Signature | Description |
+|----------|-----------|-------------|
+| `contains()` | `string.contains(value): boolean` | Check substring |
+| `containsAll()` | `string.containsAll(...values): boolean` | All substrings present |
+| `containsAny()` | `string.containsAny(...values): boolean` | Any substring present |
+| `startsWith()` | `string.startsWith(query): boolean` | Starts with query |
+| `endsWith()` | `string.endsWith(query): boolean` | Ends with query |
+| `isEmpty()` | `string.isEmpty(): boolean` | Empty or not present |
+| `lower()` | `string.lower(): string` | To lowercase |
+| `title()` | `string.title(): string` | To Title Case |
+| `trim()` | `string.trim(): string` | Remove whitespace |
+| `replace()` | `string.replace(pattern, replacement): string` | Replace pattern |
+| `repeat()` | `string.repeat(count): string` | Repeat string |
+| `reverse()` | `string.reverse(): string` | Reverse string |
+| `slice()` | `string.slice(start, end?): string` | Substring |
+| `split()` | `string.split(separator, n?): list` | Split to list |
+
+### Number Functions
+
+| Function | Signature | Description |
+|----------|-----------|-------------|
+| `abs()` | `number.abs(): number` | Absolute value |
+| `ceil()` | `number.ceil(): number` | Round up |
+| `floor()` | `number.floor(): number` | Round down |
+| `round()` | `number.round(digits?): number` | Round to digits |
+| `toFixed()` | `number.toFixed(precision): string` | Fixed-point notation |
+| `isEmpty()` | `number.isEmpty(): boolean` | Not present |
+
+### List Functions
+
+**Field:** `list.length`
+
+| Function | Signature | Description |
+|----------|-----------|-------------|
+| `contains()` | `list.contains(value): boolean` | Element exists |
+| `containsAll()` | `list.containsAll(...values): boolean` | All elements exist |
+| `containsAny()` | `list.containsAny(...values): boolean` | Any element exists |
+| `filter()` | `list.filter(expression): list` | Filter by condition (uses `value`, `index`) |
+| `map()` | `list.map(expression): list` | Transform elements (uses `value`, `index`) |
+| `reduce()` | `list.reduce(expression, initial): any` | Reduce to single value (uses `value`, `index`, `acc`) |
+| `flat()` | `list.flat(): list` | Flatten nested lists |
+| `join()` | `list.join(separator): string` | Join to string |
+| `reverse()` | `list.reverse(): list` | Reverse order |
+| `slice()` | `list.slice(start, end?): list` | Sublist |
+| `sort()` | `list.sort(): list` | Sort ascending |
+| `unique()` | `list.unique(): list` | Remove duplicates |
+| `isEmpty()` | `list.isEmpty(): boolean` | No elements |
+
+### File Functions
+
+| Function | Signature | Description |
+|----------|-----------|-------------|
+| `asLink()` | `file.asLink(display?): Link` | Convert to link |
+| `hasLink()` | `file.hasLink(otherFile): boolean` | Has link to file |
+| `hasTag()` | `file.hasTag(...tags): boolean` | Has any of the tags |
+| `hasProperty()` | `file.hasProperty(name): boolean` | Has property |
+| `inFolder()` | `file.inFolder(folder): boolean` | In folder or subfolder |
+
+### Link Functions
+
+| Function | Signature | Description |
+|----------|-----------|-------------|
+| `asFile()` | `link.asFile(): file` | Get file object |
+| `linksTo()` | `link.linksTo(file): boolean` | Links to file |
+
+### Object Functions
+
+| Function | Signature | Description |
+|----------|-----------|-------------|
+| `isEmpty()` | `object.isEmpty(): boolean` | No properties |
+| `keys()` | `object.keys(): list` | List of keys |
+| `values()` | `object.values(): list` | List of values |
+
+### Regular Expression Functions
+
+| Function | Signature | Description |
+|----------|-----------|-------------|
+| `matches()` | `regexp.matches(string): boolean` | Test if matches |
+
+## View Types
+
+### Table View
+
+```yaml
+views:
+ - type: table
+ name: "My Table"
+ order:
+ - file.name
+ - status
+ - due_date
+ summaries:
+ price: Sum
+ count: Average
+```
+
+### Cards View
+
+```yaml
+views:
+ - type: cards
+ name: "Gallery"
+ order:
+ - file.name
+ - cover_image
+ - description
+```
+
+### List View
+
+```yaml
+views:
+ - type: list
+ name: "Simple List"
+ order:
+ - file.name
+ - status
+```
+
+### Map View
+
+Requires latitude/longitude properties and the Maps community plugin.
+
+```yaml
+views:
+ - type: map
+ name: "Locations"
+ # Map-specific settings for lat/lng properties
+```
+
+## Default Summary Formulas
+
+| Name | Input Type | Description |
+|------|------------|-------------|
+| `Average` | Number | Mathematical mean |
+| `Min` | Number | Smallest number |
+| `Max` | Number | Largest number |
+| `Sum` | Number | Sum of all numbers |
+| `Range` | Number | Max - Min |
+| `Median` | Number | Mathematical median |
+| `Stddev` | Number | Standard deviation |
+| `Earliest` | Date | Earliest date |
+| `Latest` | Date | Latest date |
+| `Range` | Date | Latest - Earliest |
+| `Checked` | Boolean | Count of true values |
+| `Unchecked` | Boolean | Count of false values |
+| `Empty` | Any | Count of empty values |
+| `Filled` | Any | Count of non-empty values |
+| `Unique` | Any | Count of unique values |
+
+## Complete Examples
+
+### Task Tracker Base
+
+```yaml
+filters:
+ and:
+ - file.hasTag("task")
+ - 'file.ext == "md"'
+
+formulas:
+ days_until_due: 'if(due, (date(due) - today()).days, "")'
+ is_overdue: 'if(due, date(due) < today() && status != "done", false)'
+ priority_label: 'if(priority == 1, "🔴 High", if(priority == 2, "🟡 Medium", "🟢 Low"))'
+
+properties:
+ status:
+ displayName: Status
+ formula.days_until_due:
+ displayName: "Days Until Due"
+ formula.priority_label:
+ displayName: Priority
+
+views:
+ - type: table
+ name: "Active Tasks"
+ filters:
+ and:
+ - 'status != "done"'
+ order:
+ - file.name
+ - status
+ - formula.priority_label
+ - due
+ - formula.days_until_due
+ groupBy:
+ property: status
+ direction: ASC
+ summaries:
+ formula.days_until_due: Average
+
+ - type: table
+ name: "Completed"
+ filters:
+ and:
+ - 'status == "done"'
+ order:
+ - file.name
+ - completed_date
+```
+
+### Reading List Base
+
+```yaml
+filters:
+ or:
+ - file.hasTag("book")
+ - file.hasTag("article")
+
+formulas:
+ reading_time: 'if(pages, (pages * 2).toString() + " min", "")'
+ status_icon: 'if(status == "reading", "📖", if(status == "done", "✅", "📚"))'
+ year_read: 'if(finished_date, date(finished_date).year, "")'
+
+properties:
+ author:
+ displayName: Author
+ formula.status_icon:
+ displayName: ""
+ formula.reading_time:
+ displayName: "Est. Time"
+
+views:
+ - type: cards
+ name: "Library"
+ order:
+ - cover
+ - file.name
+ - author
+ - formula.status_icon
+ filters:
+ not:
+ - 'status == "dropped"'
+
+ - type: table
+ name: "Reading List"
+ filters:
+ and:
+ - 'status == "to-read"'
+ order:
+ - file.name
+ - author
+ - pages
+ - formula.reading_time
+```
+
+### Project Notes Base
+
+```yaml
+filters:
+ and:
+ - file.inFolder("Projects")
+ - 'file.ext == "md"'
+
+formulas:
+ last_updated: 'file.mtime.relative()'
+ link_count: 'file.links.length'
+
+summaries:
+ avgLinks: 'values.filter(value.isType("number")).mean().round(1)'
+
+properties:
+ formula.last_updated:
+ displayName: "Updated"
+ formula.link_count:
+ displayName: "Links"
+
+views:
+ - type: table
+ name: "All Projects"
+ order:
+ - file.name
+ - status
+ - formula.last_updated
+ - formula.link_count
+ summaries:
+ formula.link_count: avgLinks
+ groupBy:
+ property: status
+ direction: ASC
+
+ - type: list
+ name: "Quick List"
+ order:
+ - file.name
+ - status
+```
+
+### Daily Notes Index
+
+```yaml
+filters:
+ and:
+ - file.inFolder("Daily Notes")
+ - '/^\d{4}-\d{2}-\d{2}$/.matches(file.basename)'
+
+formulas:
+ word_estimate: '(file.size / 5).round(0)'
+ day_of_week: 'date(file.basename).format("dddd")'
+
+properties:
+ formula.day_of_week:
+ displayName: "Day"
+ formula.word_estimate:
+ displayName: "~Words"
+
+views:
+ - type: table
+ name: "Recent Notes"
+ limit: 30
+ order:
+ - file.name
+ - formula.day_of_week
+ - formula.word_estimate
+ - file.mtime
+```
+
+## Embedding Bases
+
+Embed in Markdown files:
+
+```markdown
+![[MyBase.base]]
+
+
+![[MyBase.base#View Name]]
+```
+
+## YAML Quoting Rules
+
+- Use single quotes for formulas containing double quotes: `'if(done, "Yes", "No")'`
+- Use double quotes for simple strings: `"My View Name"`
+- Escape nested quotes properly in complex expressions
+
+## Common Patterns
+
+### Filter by Tag
+```yaml
+filters:
+ and:
+ - file.hasTag("project")
+```
+
+### Filter by Folder
+```yaml
+filters:
+ and:
+ - file.inFolder("Notes")
+```
+
+### Filter by Date Range
+```yaml
+filters:
+ and:
+ - 'file.mtime > now() - "7d"'
+```
+
+### Filter by Property Value
+```yaml
+filters:
+ and:
+ - 'status == "active"'
+ - 'priority >= 3'
+```
+
+### Combine Multiple Conditions
+```yaml
+filters:
+ or:
+ - and:
+ - file.hasTag("important")
+ - 'status != "done"'
+ - and:
+ - 'priority == 1'
+ - 'due != ""'
+```
+
+## References
+
+- [Bases Syntax](https://help.obsidian.md/bases/syntax)
+- [Functions](https://help.obsidian.md/bases/functions)
+- [Views](https://help.obsidian.md/bases/views)
+- [Formulas](https://help.obsidian.md/formulas)
diff --git a/skills/obsidian-markdown/SKILL.md b/skills/obsidian-markdown/SKILL.md
new file mode 100644
index 0000000..2fb45c5
--- /dev/null
+++ b/skills/obsidian-markdown/SKILL.md
@@ -0,0 +1,620 @@
+---
+name: obsidian-markdown
+description: Create and edit Obsidian Flavored Markdown with wikilinks, embeds, callouts, properties, and other Obsidian-specific syntax. Use when working with .md files in Obsidian, or when the user mentions wikilinks, callouts, frontmatter, tags, embeds, or Obsidian notes.
+---
+
+# Obsidian Flavored Markdown Skill
+
+This skill enables skills-compatible agents to create and edit valid Obsidian Flavored Markdown, including all Obsidian-specific syntax extensions.
+
+## Overview
+
+Obsidian uses a combination of Markdown flavors:
+- [CommonMark](https://commonmark.org/)
+- [GitHub Flavored Markdown](https://github.github.com/gfm/)
+- [LaTeX](https://www.latex-project.org/) for math
+- Obsidian-specific extensions (wikilinks, callouts, embeds, etc.)
+
+## Basic Formatting
+
+### Paragraphs and Line Breaks
+
+```markdown
+This is a paragraph.
+
+This is another paragraph (blank line between creates separate paragraphs).
+
+For a line break within a paragraph, add two spaces at the end
+or use Shift+Enter.
+```
+
+### Headings
+
+```markdown
+# Heading 1
+## Heading 2
+### Heading 3
+#### Heading 4
+##### Heading 5
+###### Heading 6
+```
+
+### Text Formatting
+
+| Style | Syntax | Example | Output |
+|-------|--------|---------|--------|
+| Bold | `**text**` or `__text__` | `**Bold**` | **Bold** |
+| Italic | `*text*` or `_text_` | `*Italic*` | *Italic* |
+| Bold + Italic | `***text***` | `***Both***` | ***Both*** |
+| Strikethrough | `~~text~~` | `~~Striked~~` | ~~Striked~~ |
+| Highlight | `==text==` | `==Highlighted==` | ==Highlighted== |
+| Inline code | `` `code` `` | `` `code` `` | `code` |
+
+### Escaping Formatting
+
+Use backslash to escape special characters:
+```markdown
+\*This won't be italic\*
+\#This won't be a heading
+1\. This won't be a list item
+```
+
+Common characters to escape: `\*`, `\_`, `\#`, `` \` ``, `\|`, `\~`
+
+## Internal Links (Wikilinks)
+
+### Basic Links
+
+```markdown
+[[Note Name]]
+[[Note Name.md]]
+[[Note Name|Display Text]]
+```
+
+### Link to Headings
+
+```markdown
+[[Note Name#Heading]]
+[[Note Name#Heading|Custom Text]]
+[[#Heading in same note]]
+[[##Search all headings in vault]]
+```
+
+### Link to Blocks
+
+```markdown
+[[Note Name#^block-id]]
+[[Note Name#^block-id|Custom Text]]
+```
+
+Define a block ID by adding `^block-id` at the end of a paragraph:
+```markdown
+This is a paragraph that can be linked to. ^my-block-id
+```
+
+For lists and quotes, add the block ID on a separate line:
+```markdown
+> This is a quote
+> With multiple lines
+
+^quote-id
+```
+
+### Search Links
+
+```markdown
+[[##heading]] Search for headings containing "heading"
+[[^^block]] Search for blocks containing "block"
+```
+
+## Markdown-Style Links
+
+```markdown
+[Display Text](Note%20Name.md)
+[Display Text](Note%20Name.md#Heading)
+[Display Text](https://example.com)
+[Note](obsidian://open?vault=VaultName&file=Note.md)
+```
+
+Note: Spaces must be URL-encoded as `%20` in Markdown links.
+
+## Embeds
+
+### Embed Notes
+
+```markdown
+![[Note Name]]
+![[Note Name#Heading]]
+![[Note Name#^block-id]]
+```
+
+### Embed Images
+
+```markdown
+![[image.png]]
+![[image.png|640x480]] Width x Height
+![[image.png|300]] Width only (maintains aspect ratio)
+```
+
+### External Images
+
+```markdown
+
+
+```
+
+### Embed Audio
+
+```markdown
+![[audio.mp3]]
+![[audio.ogg]]
+```
+
+### Embed PDF
+
+```markdown
+![[document.pdf]]
+![[document.pdf#page=3]]
+![[document.pdf#height=400]]
+```
+
+### Embed Lists
+
+```markdown
+![[Note#^list-id]]
+```
+
+Where the list has been defined with a block ID:
+```markdown
+- Item 1
+- Item 2
+- Item 3
+
+^list-id
+```
+
+### Embed Search Results
+
+````markdown
+```query
+tag:#project status:done
+```
+````
+
+## Callouts
+
+### Basic Callout
+
+```markdown
+> [!note]
+> This is a note callout.
+
+> [!info] Custom Title
+> This callout has a custom title.
+
+> [!tip] Title Only
+```
+
+### Foldable Callouts
+
+```markdown
+> [!faq]- Collapsed by default
+> This content is hidden until expanded.
+
+> [!faq]+ Expanded by default
+> This content is visible but can be collapsed.
+```
+
+### Nested Callouts
+
+```markdown
+> [!question] Outer callout
+> > [!note] Inner callout
+> > Nested content
+```
+
+### Supported Callout Types
+
+| Type | Aliases | Description |
+|------|---------|-------------|
+| `note` | - | Blue, pencil icon |
+| `abstract` | `summary`, `tldr` | Teal, clipboard icon |
+| `info` | - | Blue, info icon |
+| `todo` | - | Blue, checkbox icon |
+| `tip` | `hint`, `important` | Cyan, flame icon |
+| `success` | `check`, `done` | Green, checkmark icon |
+| `question` | `help`, `faq` | Yellow, question mark |
+| `warning` | `caution`, `attention` | Orange, warning icon |
+| `failure` | `fail`, `missing` | Red, X icon |
+| `danger` | `error` | Red, zap icon |
+| `bug` | - | Red, bug icon |
+| `example` | - | Purple, list icon |
+| `quote` | `cite` | Gray, quote icon |
+
+### Custom Callouts (CSS)
+
+```css
+.callout[data-callout="custom-type"] {
+ --callout-color: 255, 0, 0;
+ --callout-icon: lucide-alert-circle;
+}
+```
+
+## Lists
+
+### Unordered Lists
+
+```markdown
+- Item 1
+- Item 2
+ - Nested item
+ - Another nested
+- Item 3
+
+* Also works with asterisks
++ Or plus signs
+```
+
+### Ordered Lists
+
+```markdown
+1. First item
+2. Second item
+ 1. Nested numbered
+ 2. Another nested
+3. Third item
+
+1) Alternative syntax
+2) With parentheses
+```
+
+### Task Lists
+
+```markdown
+- [ ] Incomplete task
+- [x] Completed task
+- [ ] Task with sub-tasks
+ - [ ] Subtask 1
+ - [x] Subtask 2
+```
+
+## Quotes
+
+```markdown
+> This is a blockquote.
+> It can span multiple lines.
+>
+> And include multiple paragraphs.
+>
+> > Nested quotes work too.
+```
+
+## Code
+
+### Inline Code
+
+```markdown
+Use `backticks` for inline code.
+Use double backticks for ``code with a ` backtick inside``.
+```
+
+### Code Blocks
+
+````markdown
+```
+Plain code block
+```
+
+```javascript
+// Syntax highlighted code block
+function hello() {
+ console.log("Hello, world!");
+}
+```
+
+```python
+# Python example
+def greet(name):
+ print(f"Hello, {name}!")
+```
+````
+
+### Nesting Code Blocks
+
+Use more backticks or tildes for the outer block:
+
+`````markdown
+````markdown
+Here's how to create a code block:
+```js
+console.log("Hello")
+```
+````
+`````
+
+## Tables
+
+```markdown
+| Header 1 | Header 2 | Header 3 |
+|----------|----------|----------|
+| Cell 1 | Cell 2 | Cell 3 |
+| Cell 4 | Cell 5 | Cell 6 |
+```
+
+### Alignment
+
+```markdown
+| Left | Center | Right |
+|:---------|:--------:|---------:|
+| Left | Center | Right |
+```
+
+### Using Pipes in Tables
+
+Escape pipes with backslash:
+```markdown
+| Column 1 | Column 2 |
+|----------|----------|
+| [[Link\|Display]] | ![[Image\|100]] |
+```
+
+## Math (LaTeX)
+
+### Inline Math
+
+```markdown
+This is inline math: $e^{i\pi} + 1 = 0$
+```
+
+### Block Math
+
+```markdown
+$$
+\begin{vmatrix}
+a & b \\
+c & d
+\end{vmatrix} = ad - bc
+$$
+```
+
+### Common Math Syntax
+
+```markdown
+$x^2$ Superscript
+$x_i$ Subscript
+$\frac{a}{b}$ Fraction
+$\sqrt{x}$ Square root
+$\sum_{i=1}^{n}$ Summation
+$\int_a^b$ Integral
+$\alpha, \beta$ Greek letters
+```
+
+## Diagrams (Mermaid)
+
+````markdown
+```mermaid
+graph TD
+ A[Start] --> B{Decision}
+ B -->|Yes| C[Do this]
+ B -->|No| D[Do that]
+ C --> E[End]
+ D --> E
+```
+````
+
+### Sequence Diagrams
+
+````markdown
+```mermaid
+sequenceDiagram
+ Alice->>Bob: Hello Bob
+ Bob-->>Alice: Hi Alice
+```
+````
+
+### Linking in Diagrams
+
+````markdown
+```mermaid
+graph TD
+ A[Biology]
+ B[Chemistry]
+ A --> B
+ class A,B internal-link;
+```
+````
+
+## Footnotes
+
+```markdown
+This sentence has a footnote[^1].
+
+[^1]: This is the footnote content.
+
+You can also use named footnotes[^note].
+
+[^note]: Named footnotes still appear as numbers.
+
+Inline footnotes are also supported.^[This is an inline footnote.]
+```
+
+## Comments
+
+```markdown
+This is visible %%but this is hidden%% text.
+
+%%
+This entire block is hidden.
+It won't appear in reading view.
+%%
+```
+
+## Horizontal Rules
+
+```markdown
+---
+***
+___
+- - -
+* * *
+```
+
+## Properties (Frontmatter)
+
+Properties use YAML frontmatter at the start of a note:
+
+```yaml
+---
+title: My Note Title
+date: 2024-01-15
+tags:
+ - project
+ - important
+aliases:
+ - My Note
+ - Alternative Name
+cssclasses:
+ - custom-class
+status: in-progress
+rating: 4.5
+completed: false
+due: 2024-02-01T14:30:00
+---
+```
+
+### Property Types
+
+| Type | Example |
+|------|---------|
+| Text | `title: My Title` |
+| Number | `rating: 4.5` |
+| Checkbox | `completed: true` |
+| Date | `date: 2024-01-15` |
+| Date & Time | `due: 2024-01-15T14:30:00` |
+| List | `tags: [one, two]` or YAML list |
+| Links | `related: "[[Other Note]]"` |
+
+### Default Properties
+
+- `tags` - Note tags
+- `aliases` - Alternative names for the note
+- `cssclasses` - CSS classes applied to the note
+
+## Tags
+
+```markdown
+#tag
+#nested/tag
+#tag-with-dashes
+#tag_with_underscores
+
+In frontmatter:
+---
+tags:
+ - tag1
+ - nested/tag2
+---
+```
+
+Tags can contain:
+- Letters (any language)
+- Numbers (not as first character)
+- Underscores `_`
+- Hyphens `-`
+- Forward slashes `/` (for nesting)
+
+## HTML Content
+
+Obsidian supports HTML within Markdown:
+
+```markdown
+