Initial OneMind Obsidian plugin

This commit is contained in:
pikaqiu 2026-05-16 21:34:04 +08:00
commit 473102fc1d
12 changed files with 4363 additions and 0 deletions

9
.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
node_modules/
.DS_Store
*.tgz
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.npm/
.cache/
dist/

62
README.md Normal file
View file

@ -0,0 +1,62 @@
# OneMind
OneMind is an editor-first Obsidian mind map plugin. It opens the current Markdown note as a clean SVG + DOM mind map and writes edits back to plain Markdown.
## MVP Features
- Open the active Markdown note in a full-page OneMind view.
- Switch from Markdown to OneMind and focus the node matching the current heading.
- Copy a selected node as a standard `obsidian://open` URI with a heading anchor.
- Parse headings and unordered lists into a tree.
- Render a horizontal mind map with SVG Bezier links and DOM nodes.
- Choose between right-facing and balanced two-sided layouts.
- Add child nodes with `Tab`, add sibling nodes with `Enter`.
- Duplicate nodes and restructure with `Alt/Option + Arrow keys`.
- Use the node context menu for common edit, copy, tag, export, and delete actions.
- Long-press nodes on touch devices to open the same context menu.
- Edit nodes with `F2` or double click.
- Add, replace, or clear node emoji icons; icons are stored as leading emoji in Markdown text.
- Show Markdown tags such as `#todo` or `#idea` as node badges.
- Add or clear Markdown tag badges from selected nodes, including multi-selections.
- Filter the visible mind map by tag from tag badges or the command palette.
- See visible/total node counts, selected count, zoom, filter, and save state in the canvas status bar.
- Copy a selected branch as Markdown and paste Markdown from the clipboard as child or sibling nodes.
- Delete nodes with `Delete` or `Backspace`.
- Multi-select nodes with `Ctrl/Cmd + click`, range-select with `Shift + click`, and select all with `Ctrl/Cmd + A`.
- Apply delete, collapse, and emoji actions to the current multi-selection.
- Drag nodes onto another node to make them children, or between nearby nodes to reorder siblings.
- Collapse or expand nodes with `Space`.
- Expand or collapse the full map from toolbar actions or commands.
- Search nodes with `/`, then use `Enter` and `Shift + Enter` to move through matches.
- Navigate large maps with the collapsible outline panel.
- Move selection with arrow keys.
- Pan with blank-canvas drag.
- Zoom with mouse wheel or `Ctrl/Cmd +` and `Ctrl/Cmd -`.
- Fit to view with `Ctrl/Cmd + Shift + F`, focus selection with `Ctrl/Cmd + F`.
- Basic local undo and redo with `Ctrl/Cmd + Z` and `Ctrl/Cmd + Shift + Z`.
- Inline rendering for `**bold**`, `` `code` ``, and `[[wikilinks]]`.
- Draw dashed cross-branch association links from local wikilinks like `[[#Target]]` or `[[Target]]`.
- Export the current mind map as `.onemind.svg` or `.onemind.png` beside the source note.
- Export only the selected branch as SVG or PNG from commands.
- Configure PNG export scale and transparent background.
- Preserve frontmatter and leading non-map Markdown when writing edits back.
- Resolve external Markdown changes with a conflict prompt when local mind map edits are still unsaved.
- Responsive toolbar treatment for narrow/mobile panes.
- Chinese and English UI language setting.
- Settings for layout direction, auto-save delay, default expand depth, animation, visual branch color pickers, and export behavior.
- Obsidian commands for node editing actions, ready for custom hotkeys.
## Development
```bash
npm install
npm run dev
```
For a production build:
```bash
npm run build
```
Copy or symlink this folder into an Obsidian vault under `.obsidian/plugins/onemind`, then enable the plugin from Obsidian settings.

44
esbuild.config.mjs Normal file
View file

@ -0,0 +1,44 @@
import esbuild from "esbuild";
import process from "node:process";
import builtins from "builtin-modules";
const prod = process.argv[2] === "production";
const context = await esbuild.context({
banner: {
js: "/* OneMind - Obsidian mind map plugin */"
},
entryPoints: ["main.ts"],
bundle: true,
external: [
"obsidian",
"electron",
"@codemirror/autocomplete",
"@codemirror/collab",
"@codemirror/commands",
"@codemirror/language",
"@codemirror/lint",
"@codemirror/search",
"@codemirror/state",
"@codemirror/view",
"@lezer/common",
"@lezer/highlight",
"@lezer/lr",
...builtins
],
format: "cjs",
target: "es2018",
logLevel: "info",
sourcemap: prod ? false : "inline",
treeShaking: true,
outfile: "main.js",
minify: prod
});
if (prod) {
await context.rebuild();
await context.dispose();
} else {
await context.watch();
console.log("Watching for changes...");
}

13
main.js Normal file

File diff suppressed because one or more lines are too long

2711
main.ts Normal file

File diff suppressed because it is too large Load diff

10
manifest.json Normal file
View file

@ -0,0 +1,10 @@
{
"id": "onemind",
"name": "OneMind",
"version": "0.1.0",
"minAppVersion": "1.5.0",
"description": "Edit Markdown as a clean, keyboard-friendly mind map.",
"author": "",
"authorUrl": "",
"isDesktopOnly": false
}

302
ob思维导图插件.md Normal file
View file

@ -0,0 +1,302 @@
---
title: Obsidian 思维导图插件 - 产品方案
created: 2026-05-16
status: draft
tags:
- 产品方案
- obsidian插件
- 思维导图
---
# Obsidian 思维导图插件 - 产品方案
## 1. 产品概述
### 1.1 插件名称
**OneMind**
> "One" from ONE PIECE + Einstein's "Mind"。
> 像路飞寻找 One Piece 一样,每个思维导图都是一次寻找"思维宝藏"的旅程。
> 极简,好记,搜索友好。
### 1.2 一句话定位
一款以编辑体验为核心的 Obsidian 思维导图插件,让用户直接在极简风格的思维导图上思考和组织想法,所有内容实时同步为纯 Markdown 格式。
### 1.2 解决的核心问题
| 现有痛点 | 我们的解法 |
|---------|-----------|
| 现有插件只是 markdown 的只读可视化 | 编辑器优先,思维导图本身就是工作区 |
| 视觉粗糙、风格老旧 | Notion/Linear 式极简美学 |
| 编辑能力弱,几乎不能在导图上操作 | 完整的键盘+鼠标编辑体验 |
| 独立文件格式,破坏纯文本理念 | 纯 Markdown 双向同步 |
### 1.3 目标用户
- Obsidian 重度用户,习惯用标题和列表组织内容
- 不满足于线性文本,需要可视化思考的人群
- 对工具有审美要求,不愿接受丑陋界面的用户
### 1.4 发布目标
社区插件,上架 Obsidian Plugin Marketplace。
---
## 2. 核心功能MVP
### 2.1 视图切换
- 命令面板命令或图标按钮:在 Markdown 编辑视图和思维导图视图之间切换
- 思维导图视图是一个独立的全屏编辑视图(非侧边栏预览)
- 切换时保留光标/选中节点的上下文
### 2.2 Markdown ↔ 思维导图映射规则
```
# 中心主题 → 根节点
## 分支一 → 一级子节点
### 子节点 1.1 → 二级子节点
- 列表项 → 叶子节点
- **加粗** → 节点加粗样式
- [[链接]] → 可点击跳转的链接节点
- `代码` → 节点内行内代码样式
```
- 标题层级 (H1-H6) → 节点层级
- 无序列表 `- item` → 当前标题下的子节点
- 支持混合使用标题和列表
### 2.3 节点编辑
| 操作 | 快捷键 | 说明 |
|------|--------|------|
| 添加子节点 | `Tab` | 在选中节点下创建子节点,自动进入编辑模式 |
| 添加同级节点 | `Enter` | 在选中节点后创建同级节点,自动进入编辑模式 |
| 编辑节点文本 | `F2` 或双击 | 进入文本编辑模式 |
| 删除节点 | `Delete` / `Backspace` | 删除选中节点及其所有子节点 |
| 展开/折叠 | `Space` | 折叠/展开子节点 |
| 撤销/重做 | `Ctrl+Z` / `Ctrl+Shift+Z` | 撤销/重做操作 |
### 2.4 节点拖拽
- 拖拽节点到另一个节点上 → 成为其子节点
- 拖拽节点到两个节点之间 → 插入为同级节点
- 拖拽时显示视觉指示(高亮放置位置)
- 拖拽完成后实时更新 Markdown
### 2.5 画布操作
| 操作 | 方式 | 说明 |
|------|------|------|
| 平移 | 鼠标拖拽空白区域 / 方向键 | 移动画布视口 |
| 缩放 | 滚轮 / `Ctrl +/-` | 缩放画布 |
| 适应窗口 | `Ctrl+Shift+F` | 自动缩放以显示全部内容 |
| 聚焦选中 | `Ctrl+F` | 平移画布使选中节点居中 |
### 2.6 实时 Markdown 同步
- 在思维导图视图中的任何编辑,实时更新对应的 Markdown 源文件
- Markdown 文件在外部被修改时(如同步、其他编辑器),思维导图视图自动重新解析
- 同步策略:以 Markdown 为 source of truth导图编辑通过修改 Markdown 文本来实现
- 支持 Obsidian 原生撤销栈Undo/Redo 走 Obsidian 的 editor transaction
---
## 3. 视觉设计
### 3.1 设计原则
- **克制**:细线条、少颜色、多留白
- **清晰**:层次分明,一眼看出结构关系
- **流畅**:动画自然,过渡丝滑
### 3.2 设计规范
```
连线:
- 线宽1.5px
- 线型:贝塞尔曲线(水平方向展开)
- 颜色:跟随节点层级自动分配柔和色系
节点:
- 圆角8px
- 内边距8px 16px
- 根节点:较大,加粗字体
- 分支节点:适中
- 叶子节点:较小
字体:
- 使用 Obsidian 主题字体(跟随用户设置)
- 根节点18px 加粗
- 子节点14px 常规
配色:
- 跟随 Obsidian 主题(亮色/暗色自动适配)
- 分支颜色自动分配,色板预设(可配置)
- 默认色板:蓝、绿、橙、紫、粉 5色柔和色系
间距:
- 同级节点垂直间距16px
- 父子节点水平间距60px
- 根节点居中显示
```
### 3.3 布局方向
- **默认**:水平树形布局(根节点在左,分支向右展开)
- **可选**:水平双向布局(根节点居中,分支向两侧展开)
- 自动布局算法:每次编辑后重新计算节点位置,带有平滑过渡动画
---
## 4. 技术架构
### 4.1 渲染层
```
┌─────────────────────────────────┐
│ Obsidian View │
│ ┌───────────────────────────┐ │
│ │ Canvas Container │ │
│ │ │ │
│ │ ┌─────┐ ┌─────┐ │ │
│ │ │ DOM │───│ DOM │ │ │
│ │ │ 节点│ SVG│ 节点│ │ │
│ │ └─────┘连线└─────┘ │ │
│ │ │ │
│ └───────────────────────────┘ │
└─────────────────────────────────┘
```
- **SVG 层**:负责绘制节点之间的贝塞尔曲线连线
- **DOM 层**:负责渲染节点卡片(文本、样式、交互),叠加在 SVG 之上
- **容器**:可缩放、可平移的画布容器,使用 CSS `transform` 实现
### 4.2 数据流
```
Markdown 源文件
Markdown Parser ──→ Tree Data Model ──→ Layout Engine ──→ Renderer (SVG + DOM)
▲ │
│ ▼
Markdown Writer ◄── Tree Data Model ◄── User Interaction (编辑/拖拽)
```
- **Markdown Parser**:解析标题层级和列表结构,构建树形数据模型
- **Tree Data Model**:内存中的树形数据结构,每个节点包含 id、text、children、style 等属性
- **Layout Engine**:根据树结构计算每个节点的 x/y 坐标,实现自动布局算法
- **Renderer**:根据布局结果渲染 SVG 连线和 DOM 节点
- **Markdown Writer**:将树形数据模型序列化回 Markdown 文本
### 4.3 自动布局算法
- 采用紧凑树形布局Compact Tree Layout
- 递归计算每个子树的高度,均匀分配垂直空间
- 编辑操作后增量更新布局,非全量重排(性能考虑)
- 位置变化时使用 CSS transition 实现平滑动画过渡
### 4.4 Markdown 同步策略
- **编辑导图时**:修改 Tree Data Model → 序列化为 Markdown → 通过 Obsidian Editor API 写入文件
- **文件被外部修改时**:监听 `vault.on('modify')` 事件 → 重新解析 Markdown → 差量更新 Tree Data Model → 增量重渲染
- **冲突处理**:以 Markdown 文件为 source of truth如果用户正在编辑节点时文件被外部修改提示用户选择保留哪个版本
### 4.5 技术栈
| 模块 | 技术 | 说明 |
|------|------|------|
| 插件框架 | Obsidian API + TypeScript | 标准 Obsidian 插件开发 |
| 渲染 | SVG + DOM | 不依赖额外渲染库 |
| Markdown 解析 | 内置轻量解析器 | 只处理标题和列表层级 |
| 构建工具 | esbuild / Vite | Obsidian 插件标准构建 |
---
## 5. 插件设置
### 5.1 设置项
```
外观设置:
- 布局方向:水平向右 / 水平双向
- 分支颜色方案:预设色板选择 / 自定义色板
- 跟随 Obsidian 主题:开/关
行为设置:
- 自动保存延迟:输入后多久同步到 Markdown默认 300ms
- 展开折叠动画:开/关
- 展开深度:首次打开时默认展开到第几层(默认全部展开)
快捷键:
- 所有操作均支持自定义快捷键(走 Obsidian 原生快捷键系统)
```
---
## 6. 发布计划
### Phase 1核心 MVP
- [ ] 插件骨架:注册 View实现视图切换
- [ ] Markdown 解析器:标题+列表 → 树形结构
- [ ] 渲染引擎SVG 连线 + DOM 节点
- [ ] 自动布局算法
- [ ] 基础编辑:添加/删除/编辑节点文本
- [ ] Markdown 同步(单向:导图 → Markdown
- [ ] 缩放和平移
- [ ] 暗色/亮色主题适配
### Phase 2交互增强
- [ ] 节点拖拽排序
- [ ] 展开/折叠子节点
- [ ] Markdown 反向同步(文件修改 → 导图更新)
- [ ] 节点搜索和导航
- [ ] 快捷键完善
- [ ] 平滑动画过渡
### Phase 3体验打磨
- [ ] 多种布局方向支持
- [ ] 节点样式自定义(颜色、标记)
- [ ] 导出为 PNG/SVG
- [ ] 设置面板完善
- [ ] 中英文国际化
- [ ] 移动端适配
### Phase 4高级功能
- [ ] 节点间关联线(跨分支连接)
- [ ] 节点图标/标记系统
- [ ] 多选和批量操作
- [ ] 大纲缩略图导航
- [ ] 协同编辑支持(与 Obsidian Sync 兼容)
---
## 7. 竞品对比
| 能力 | 我们的插件 | Mind Map (Markmap) | Enhancing Mindmap | Canvas Mindmap | XMind (独立) |
|------|-----------|-------------------|------------------|----------------|-------------|
| 编辑体验 | ⭐⭐⭐ 完整编辑 | ❌ 只读 | ⭐ 基础编辑 | ⭐ 基础编辑 | ⭐⭐⭐ 完整 |
| 视觉美学 | ⭐⭐⭐ 极简 | ⭐ 一般 | ⭐⭐ 老旧 | ⭐ 一般 | ⭐⭐⭐ 精美 |
| Markdown 同步 | ⭐⭐⭐ 双向实时 | ⭐ 单向只读 | ⭐⭐ 基础 | ❌ 无 | ❌ 纯私有格式 |
| Obsidian 原生 | ⭐⭐⭐ 深度 | ⭐⭐ 中等 | ⭐⭐ 中等 | ⭐⭐⭐ Canvas | ❌ 独立应用 |
| 键盘操作 | ⭐⭐⭐ 完整 | ❌ 无 | ⭐⭐ 基础 | ⭐⭐ 基础 | ⭐⭐⭐ 完整 |
**核心差异化**:唯一一款同时做到"编辑器级体验 + 极简美学 + 纯 Markdown 同步"的 Obsidian 思维导图插件。
---
## 8. 开放问题
- [x] 插件名称OneMind
- [x] 支持 `Obsidian URI` 协议跳转到特定节点(如 `obsidian://open?vault=xxx&file=yyy#heading-zzz` 打开后自动定位到对应节点)
- [x] ~~是否支持 Dataview 查询结果作为节点内容~~ 暂不考虑
- [x] ~~是否支持 `callout` / `代码块` 在节点中的显示~~ 暂不考虑
- [x] 性能基准:支持 1000 节点的思维导图(布局计算和渲染均需在可接受时间内完成)

657
package-lock.json generated Normal file
View file

@ -0,0 +1,657 @@
{
"name": "onemind",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "onemind",
"version": "0.1.0",
"license": "MIT",
"devDependencies": {
"@types/node": "^20.11.30",
"builtin-modules": "^3.3.0",
"esbuild": "^0.25.12",
"obsidian": "^1.5.12",
"typescript": "^5.4.5"
}
},
"node_modules/@codemirror/state": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.0.tgz",
"integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@marijn/find-cluster-break": "^1.0.0"
}
},
"node_modules/@codemirror/view": {
"version": "6.38.6",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.6.tgz",
"integrity": "sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@codemirror/state": "^6.5.0",
"crelt": "^1.0.6",
"style-mod": "^4.1.0",
"w3c-keyname": "^2.2.4"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
"integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
"integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
"integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
"integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
"integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
"integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
"integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
"integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
"integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
"integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
"integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
"integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
"integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
"integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
"integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
"integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
"integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
"integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
"integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
"integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
"integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
"integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
"integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
"integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
"integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
"integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@marijn/find-cluster-break": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz",
"integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/@types/codemirror": {
"version": "5.60.8",
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.8.tgz",
"integrity": "sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/tern": "*"
}
},
"node_modules/@types/estree": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
"integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "20.19.41",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz",
"integrity": "sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/@types/tern": {
"version": "0.23.9",
"resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz",
"integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "*"
}
},
"node_modules/builtin-modules": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
"integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/crelt": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/esbuild": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
"integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.25.12",
"@esbuild/android-arm": "0.25.12",
"@esbuild/android-arm64": "0.25.12",
"@esbuild/android-x64": "0.25.12",
"@esbuild/darwin-arm64": "0.25.12",
"@esbuild/darwin-x64": "0.25.12",
"@esbuild/freebsd-arm64": "0.25.12",
"@esbuild/freebsd-x64": "0.25.12",
"@esbuild/linux-arm": "0.25.12",
"@esbuild/linux-arm64": "0.25.12",
"@esbuild/linux-ia32": "0.25.12",
"@esbuild/linux-loong64": "0.25.12",
"@esbuild/linux-mips64el": "0.25.12",
"@esbuild/linux-ppc64": "0.25.12",
"@esbuild/linux-riscv64": "0.25.12",
"@esbuild/linux-s390x": "0.25.12",
"@esbuild/linux-x64": "0.25.12",
"@esbuild/netbsd-arm64": "0.25.12",
"@esbuild/netbsd-x64": "0.25.12",
"@esbuild/openbsd-arm64": "0.25.12",
"@esbuild/openbsd-x64": "0.25.12",
"@esbuild/openharmony-arm64": "0.25.12",
"@esbuild/sunos-x64": "0.25.12",
"@esbuild/win32-arm64": "0.25.12",
"@esbuild/win32-ia32": "0.25.12",
"@esbuild/win32-x64": "0.25.12"
}
},
"node_modules/moment": {
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
"dev": true,
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/obsidian": {
"version": "1.12.3",
"resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.12.3.tgz",
"integrity": "sha512-HxWqe763dOqzXjnNiHmAJTRERN8KILBSqxDSEqbeSr7W8R8Jxezzbca+nz1LiiqXnMpM8lV2jzAezw3CZ4xNUw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/codemirror": "5.60.8",
"moment": "2.29.4"
},
"peerDependencies": {
"@codemirror/state": "6.5.0",
"@codemirror/view": "6.38.6"
}
},
"node_modules/style-mod": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz",
"integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
},
"node_modules/w3c-keyname": {
"version": "2.2.8",
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
"dev": true,
"license": "MIT",
"peer": true
}
}
}

24
package.json Normal file
View file

@ -0,0 +1,24 @@
{
"name": "onemind",
"version": "0.1.0",
"description": "An editor-first mind map plugin for Obsidian that syncs with plain Markdown.",
"main": "main.js",
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production"
},
"keywords": [
"obsidian",
"mind-map",
"markdown"
],
"author": "",
"license": "MIT",
"devDependencies": {
"@types/node": "^20.11.30",
"builtin-modules": "^3.3.0",
"esbuild": "^0.25.12",
"obsidian": "^1.5.12",
"typescript": "^5.4.5"
}
}

506
styles.css Normal file
View file

@ -0,0 +1,506 @@
.onemind-view {
--onemind-node-bg: var(--background-primary);
--onemind-node-border: var(--background-modifier-border);
--onemind-node-shadow: rgba(15, 23, 42, 0.08);
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
background: var(--background-primary);
}
.theme-dark .onemind-view {
--onemind-node-shadow: rgba(0, 0, 0, 0.28);
}
.onemind-toolbar {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 6px;
min-height: 40px;
padding: 6px 10px;
border-bottom: 1px solid var(--background-modifier-border);
background: var(--background-primary);
flex: 0 0 auto;
}
.onemind-search {
position: relative;
margin-left: 6px;
width: min(260px, 34vw);
min-width: 140px;
}
@media (max-width: 560px) {
.onemind-toolbar {
align-items: stretch;
}
.onemind-search {
order: 2;
width: 100%;
min-width: 100%;
margin-left: 0;
}
.onemind-node {
width: 168px;
padding-left: 12px;
padding-right: 10px;
}
.onemind-node.is-root {
width: 184px;
}
.onemind-outline {
left: 10px;
right: 10px;
top: 10px;
bottom: auto;
width: auto;
max-height: 42%;
min-width: 0;
}
}
.onemind-search-input {
width: 100%;
height: 28px;
padding: 4px 44px 4px 10px;
border: 1px solid var(--background-modifier-border);
border-radius: 6px;
background: var(--background-secondary);
color: var(--text-normal);
font-size: 13px;
}
.onemind-search-input:focus {
border-color: var(--interactive-accent);
box-shadow: 0 0 0 2px color-mix(in srgb, var(--interactive-accent) 18%, transparent);
}
.onemind-search-count {
position: absolute;
right: 8px;
top: 50%;
max-width: 36px;
transform: translateY(-50%);
color: var(--text-muted);
font-size: 11px;
line-height: 1;
text-align: right;
pointer-events: none;
}
.onemind-tool-button {
width: 28px;
height: 28px;
display: inline-flex;
align-items: center;
justify-content: center;
border: 1px solid transparent;
border-radius: 6px;
color: var(--text-muted);
}
.onemind-tool-button:hover {
border-color: var(--background-modifier-border);
color: var(--text-normal);
background: var(--background-modifier-hover);
}
.onemind-canvas {
position: relative;
flex: 1 1 auto;
overflow: hidden;
cursor: grab;
touch-action: none;
background:
radial-gradient(circle at 1px 1px, var(--background-modifier-border) 1px, transparent 0) 0 0 / 28px 28px,
var(--background-primary);
}
.onemind-canvas:active {
cursor: grabbing;
}
.onemind-status {
position: absolute;
left: 12px;
bottom: 12px;
max-width: min(520px, calc(100% - 24px));
padding: 5px 8px;
border: 1px solid var(--background-modifier-border);
border-radius: 6px;
background: color-mix(in srgb, var(--background-primary) 92%, transparent);
color: var(--text-muted);
box-shadow: 0 8px 20px var(--onemind-node-shadow);
font-size: 12px;
line-height: 1.2;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
pointer-events: none;
z-index: 25;
}
.onemind-outline {
position: absolute;
right: 12px;
top: 12px;
bottom: 12px;
width: min(280px, 32vw);
min-width: 210px;
display: flex;
flex-direction: column;
border: 1px solid var(--background-modifier-border);
border-radius: 8px;
background: color-mix(in srgb, var(--background-primary) 94%, transparent);
box-shadow: 0 12px 28px var(--onemind-node-shadow);
z-index: 30;
overflow: hidden;
}
.onemind-outline.is-hidden {
display: none;
}
.onemind-outline-header {
height: 36px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 4px 6px 4px 12px;
border-bottom: 1px solid var(--background-modifier-border);
color: var(--text-muted);
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0;
}
.onemind-outline-list {
overflow: auto;
padding: 6px;
}
.onemind-outline-row {
min-height: 28px;
display: flex;
align-items: center;
gap: 6px;
border-radius: 6px;
color: var(--text-muted);
font-size: 13px;
line-height: 1.25;
cursor: pointer;
}
.onemind-outline-row:hover {
background: var(--background-modifier-hover);
color: var(--text-normal);
}
.onemind-outline-row.is-selected {
background: color-mix(in srgb, var(--interactive-accent) 14%, transparent);
color: var(--text-normal);
}
.onemind-outline-emoji {
flex: 0 0 auto;
font-size: 14px;
}
.onemind-outline-label {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.onemind-outline-count {
flex: 0 0 auto;
margin-left: auto;
padding: 1px 5px;
border-radius: 999px;
background: var(--background-secondary);
color: var(--text-faint);
font-size: 11px;
}
.onemind-outline-filter {
display: flex;
align-items: center;
gap: 8px;
margin: 2px 2px 6px;
padding: 6px 8px;
border-radius: 6px;
background: color-mix(in srgb, var(--interactive-accent) 12%, transparent);
color: var(--text-normal);
font-size: 12px;
}
.onemind-outline-filter button {
margin-left: auto;
}
.onemind-scene {
position: absolute;
inset: 0;
transform-origin: 0 0;
}
.onemind-links,
.onemind-nodes {
position: absolute;
inset: 0;
overflow: visible;
}
.onemind-links path {
stroke-width: 1.5px;
stroke-linecap: round;
opacity: 0.78;
}
.onemind-links path.onemind-association-link {
stroke: var(--text-faint);
stroke-width: 1.2px;
stroke-dasharray: 5 5;
opacity: 0.72;
}
.onemind-node {
position: absolute;
width: 180px;
min-height: 42px;
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
padding: 8px 12px 8px 14px;
border: 1.5px solid var(--onemind-node-border);
border-radius: 8px;
background: var(--onemind-node-bg);
box-shadow: 0 8px 24px var(--onemind-node-shadow);
color: var(--text-normal);
font-size: 14px;
line-height: 1.35;
cursor: default;
touch-action: none;
transition: left 160ms ease, top 160ms ease, box-shadow 160ms ease, border-color 160ms ease, transform 160ms ease;
user-select: none;
}
.onemind-view.is-animation-disabled .onemind-node {
transition: none;
}
.onemind-node:hover {
transform: translateY(-1px);
}
.onemind-node.is-root {
width: 200px;
min-height: 48px;
font-size: 18px;
font-weight: 700;
}
.onemind-node.is-selected {
box-shadow: 0 0 0 3px color-mix(in srgb, var(--interactive-accent) 24%, transparent), 0 10px 26px var(--onemind-node-shadow);
}
.onemind-node.is-primary-selected {
border-width: 2px;
}
.onemind-node.is-dragging {
z-index: 20;
opacity: 0.88;
cursor: grabbing;
pointer-events: none;
transition: none;
box-shadow: 0 14px 34px var(--onemind-node-shadow);
}
.onemind-node.is-drop-child {
box-shadow: 0 0 0 3px color-mix(in srgb, var(--interactive-accent) 32%, transparent), 0 10px 26px var(--onemind-node-shadow);
}
.onemind-node.is-search-match {
background: color-mix(in srgb, var(--interactive-accent) 8%, var(--onemind-node-bg));
}
.onemind-node.is-active-search-match {
box-shadow: 0 0 0 3px color-mix(in srgb, var(--text-accent) 34%, transparent), 0 10px 26px var(--onemind-node-shadow);
}
.onemind-node-emoji {
width: 26px;
height: 26px;
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
border-radius: 6px;
background: var(--background-secondary);
font-size: 17px;
line-height: 1;
}
.onemind-node.is-root .onemind-node-emoji {
width: 30px;
height: 30px;
font-size: 20px;
}
.onemind-node-content {
min-width: 0;
flex: 1 1 auto;
overflow-wrap: anywhere;
}
.onemind-node-tags {
width: 100%;
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-top: 2px;
padding-left: 34px;
}
.onemind-node:not(.has-emoji) .onemind-node-tags {
padding-left: 0;
}
.onemind-node-tag {
max-width: 100%;
padding: 1px 6px;
border-radius: 999px;
background: color-mix(in srgb, var(--interactive-accent) 10%, var(--background-secondary));
color: var(--text-muted);
font-size: 11px;
line-height: 1.45;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.onemind-node-tag:hover,
.onemind-node-tag.is-active {
background: color-mix(in srgb, var(--interactive-accent) 22%, var(--background-secondary));
color: var(--text-normal);
}
.onemind-node-content code {
padding: 1px 4px;
border-radius: 4px;
background: var(--code-background);
color: var(--code-normal);
font-size: 0.92em;
}
.onemind-wikilink {
color: var(--link-color);
cursor: pointer;
}
.onemind-wikilink:hover {
text-decoration: underline;
}
.onemind-collapse {
width: 18px;
height: 18px;
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
border-radius: 5px;
color: var(--text-muted);
}
.onemind-collapse:hover {
background: var(--background-modifier-hover);
color: var(--text-normal);
}
.onemind-editor {
width: 100%;
min-height: 36px;
resize: none;
border: 0;
outline: 0;
background: transparent;
color: var(--text-normal);
font: inherit;
line-height: 1.35;
}
.onemind-drop-marker {
position: absolute;
height: 2px;
border-radius: 999px;
background: var(--interactive-accent);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--interactive-accent) 22%, transparent);
pointer-events: none;
z-index: 15;
}
.onemind-drop-marker::before {
content: "";
position: absolute;
left: -4px;
top: -3px;
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--interactive-accent);
}
.onemind-empty {
margin: auto;
color: var(--text-muted);
}
.onemind-emoji-suggestion {
display: inline-flex;
align-items: center;
justify-content: center;
width: 30px;
height: 30px;
font-size: 22px;
line-height: 1;
}
.onemind-modal-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
margin-top: 18px;
}
.onemind-tag-input {
width: 100%;
height: 36px;
padding: 6px 10px;
border: 1px solid var(--background-modifier-border);
border-radius: 6px;
background: var(--background-secondary);
color: var(--text-normal);
}
.onemind-tag-suggestion {
font-size: 13px;
color: var(--text-normal);
}
.onemind-settings-palette {
margin: 4px 0 10px;
padding-left: 12px;
border-left: 2px solid var(--background-modifier-border);
}

22
tsconfig.json Normal file
View file

@ -0,0 +1,22 @@
{
"compilerOptions": {
"baseUrl": ".",
"inlineSourceMap": true,
"inlineSources": true,
"module": "ESNext",
"target": "ES2018",
"allowJs": true,
"noImplicitAny": true,
"moduleResolution": "node",
"importHelpers": true,
"isolatedModules": true,
"strictNullChecks": true,
"lib": [
"DOM",
"ES2018"
]
},
"include": [
"**/*.ts"
]
}

3
versions.json Normal file
View file

@ -0,0 +1,3 @@
{
"0.1.0": "1.5.0"
}