-
Notifications
You must be signed in to change notification settings - Fork 0
docs: design system P2–P4 components, IA v2, xmind-import skill #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
28367b4
d4e6ce9
a827fa2
1f0ffff
384ae22
fbf92f2
fc2feb9
e38c4bc
5a0491c
87c9557
45fc907
06e0e7e
df39e1c
75e2150
81c6939
bfcca71
142b860
8aab208
abd1520
527b8fc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| --- | ||
| name: xmind-import | ||
| description: Use this skill whenever the user shares an XMind share URL (app.xmind.com/share/...) and wants to import, convert, or export the mind map — even if they don't say "xmind-import" explicitly. Triggers on: "convert this XMind link", "import my mind map", "turn this XMind into Mermaid", "parse this XMind", or when the user pastes an app.xmind.com/share URL alongside any request involving diagrams or markdown. Fetches the mind map JSON via Playwright MCP, converts the tree structure to a Mermaid flowchart LR diagram, and writes it to a markdown file or prints it to the conversation. | ||
| --- | ||
|
|
||
| # XMind Import | ||
|
|
||
| Fetch a public XMind share link, extract the mind map JSON, and convert it to a `flowchart LR` Mermaid diagram. | ||
|
|
||
| ## Usage | ||
|
|
||
| ``` | ||
| /xmind-import <xmind-share-url> [output-path] | ||
| ``` | ||
|
|
||
| **Examples:** | ||
|
|
||
| ``` | ||
| /xmind-import https://app.xmind.com/share/PKjJEIHD docs/functional-map/functional-map.md | ||
| /xmind-import https://app.xmind.com/share/ABC123 | ||
| ``` | ||
|
|
||
| If `output-path` is omitted, print the Mermaid source to the conversation. | ||
|
|
||
| --- | ||
|
|
||
| ## Steps | ||
|
|
||
| ### Step 1 — Extract the file ID from the URL | ||
|
|
||
| Parse the share URL to get `fileId`: | ||
|
|
||
| ``` | ||
| https://app.xmind.com/share/{fileId} | ||
| ↑ this part | ||
| ``` | ||
|
|
||
| For example, `https://app.xmind.com/share/PKjJEIHD` → `fileId = PKjJEIHD`. | ||
|
|
||
| ### Step 2 — Navigate with Playwright | ||
|
|
||
| Use `mcp__plugin_playwright_playwright__browser_navigate` to open the share page. This establishes the session cookies that allow the API call in Step 3 to succeed without authentication. | ||
|
|
||
| ``` | ||
| url: https://app.xmind.com/share/{fileId} ← substitute real fileId | ||
| ``` | ||
|
|
||
| ### Step 3 — Wait for the SPA to load | ||
|
|
||
| Use `mcp__plugin_playwright_playwright__browser_wait_for` with `time: 3`. | ||
|
|
||
| The page title changes from the generic XMind branding to the mind map name (e.g., "Label Suite - Xmind AI") once the JavaScript has fully loaded. | ||
|
|
||
| ### Step 4 — Fetch, parse, and convert in one browser call | ||
|
|
||
| Use `mcp__plugin_playwright_playwright__browser_evaluate` to fetch the XMind content API, walk the topic tree, and produce Mermaid output — all in a single call. Substitute the actual `fileId` into the fetch URL string before running. | ||
|
|
||
| ```js | ||
| async () => { | ||
| const FILE_ID = 'PKjJEIHD'; // ← replace with actual fileId | ||
|
|
||
| const res = await fetch(`/api/drive/share/${FILE_ID}/content`, { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| body: JSON.stringify({}) | ||
| }); | ||
|
|
||
| if (!res.ok) return `ERROR: API returned ${res.status}`; | ||
|
|
||
| const data = await res.json(); | ||
| const sheet = data[0]; | ||
| const mapTitle = sheet.title ?? 'Mind Map'; | ||
| const root = sheet.rootTopic; | ||
|
|
||
| const lines = ['flowchart LR']; | ||
| let counter = 0; | ||
|
|
||
| function sanitize(title) { | ||
| return (title ?? '').replace(/"/g, "'").replace(/\n/g, ' ').trim(); | ||
| } | ||
|
|
||
| function walk(topic, parentId) { | ||
| const id = 'n' + counter++; | ||
| const label = sanitize(topic.title); | ||
| if (parentId === null) { | ||
| lines.push(` ${id}(["${label}"])`); | ||
| } else { | ||
| lines.push(` ${parentId} --> ${id}["${label}"]`); | ||
| } | ||
| if (topic.children?.attached) { | ||
| for (const child of topic.children.attached) { | ||
| walk(child, id); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| walk(root, null); | ||
| return JSON.stringify({ title: mapTitle, mermaid: lines.join('\n') }); | ||
| } | ||
| ``` | ||
|
|
||
| The function returns a JSON string with two fields: | ||
| - `title` — the mind map name (use this as the markdown heading) | ||
| - `mermaid` — the full `flowchart LR` source | ||
|
|
||
| ### Step 5 — Write or print output | ||
|
|
||
| Parse the JSON string returned by Step 4, then: | ||
|
|
||
| **If an output path was given**, write a markdown file using the `Write` tool with this structure: | ||
|
|
||
| ``` | ||
| # {title from Step 4} | ||
|
|
||
| ```mermaid | ||
| {mermaid from Step 4} | ||
| ` `` | ||
| ``` | ||
|
|
||
| *(Note: close the fence with three backticks — the above uses a space before the last ` `` ` only to avoid nesting issues in this document.)* | ||
|
|
||
| **If no output path was given**, print the mermaid source in the conversation inside a `mermaid` code fence. | ||
|
|
||
| --- | ||
|
|
||
| ## Error Handling | ||
|
|
||
| | Situation | Action | | ||
| |-----------|--------| | ||
| | API returns non-200 (e.g., 401, 403) | The map is private or requires login. Inform the user — public share links work without auth, private ones do not. | | ||
| | `rootTopic` is missing | The XMind API format may have changed. Print the raw API response so the user can inspect it. | | ||
| | Playwright tools unavailable | Inform the user that this skill requires the Playwright MCP plugin to be installed. `WebFetch` cannot load XMind's SPA content. | | ||
| | `title` field missing in response | Fall back to the browser page title (available via `browser_evaluate`: `() => document.title`). | | ||
|
|
||
| --- | ||
|
|
||
| ## Notes | ||
|
|
||
| - **Auth**: Public share links work without login. The browser session cookie from Step 2 is what grants access to the content API. | ||
| - **Layout**: `flowchart LR` is a horizontal left-to-right tree. XMind's balanced layout (branches on both sides) cannot be reproduced in Mermaid — all branches appear on the right. | ||
| - **Folded nodes**: XMind `"branch": "folded"` is purely visual. All children are present in the JSON and will appear in the output. | ||
| - **`fileId` substitution**: Always replace the `FILE_ID` constant in the evaluate script with the actual ID from the URL before running — do not leave it as `'PKjJEIHD'` which is the example from this skill's documentation. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,7 +47,8 @@ Evaluation results must be fair and reproducible. | |
|
|
||
| ### VI. English-First | ||
| - Code, comments, docstrings, commit messages, and variable/function names are always written in English | ||
| - Traditional Chinese is permitted in `docs/`, `specs/`, `design/prototype/`, and `design/wireframes/` directories to accelerate research documentation and UI iteration | ||
| - Traditional Chinese is permitted in `docs/`, `specs/`, `design/prototype/`, `design/wireframes/`, and `design/system/inventory.md` to accelerate research documentation and UI iteration | ||
| - `design/system/MASTER.md` must be written in English only — it is consumed by AI agents and requires accurate token parsing | ||
|
Comment on lines
+50
to
+51
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Constitution amended without version bump or date update, violating its own governance rules Lines 50–51 add new rules to Principle VI (English-First): Prompt for agentsWas this helpful? React with 👍 or 👎 to provide feedback. |
||
| - The only fully Chinese file outside those directories is `README.zh-TW.md` | ||
|
|
||
| ## Governance | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -63,7 +63,7 @@ | |
|
|
||
| <!-- ── Top Navbar ── --> | ||
| <!-- Desktop: px-20 (80px) | Mobile: px-4 (16px) — from wireframe --> | ||
| <header class="h-14 bg-white border-b border-slate-200 flex items-center px-4 md:px-20 shrink-0 z-[200]" role="banner"> | ||
| <header class="h-16 bg-white border-b border-slate-200 flex items-center px-4 md:px-20 shrink-0 z-[200]" role="banner"> | ||
| <div class="flex items-center gap-3 flex-1"> | ||
| <a href="dashboard.html" class="flex items-center gap-2.5 group focus:outline-none" aria-label="Label Suite — 回首頁"> | ||
| <div class="w-8 h-8 bg-primary rounded-lg flex items-center justify-center group-hover:bg-secondary transition-colors duration-200"> | ||
|
|
@@ -142,20 +142,6 @@ <h2 class="text-xl md:text-2xl font-bold text-ink" data-i18n="page-title">個人 | |
| <p class="text-sm text-slate-500 mt-1 leading-relaxed" data-i18n="page-desc">管理您的帳號資訊與顯示偏好。</p> | ||
| </div> | ||
|
|
||
| <!-- ── Toast (hidden by default) ── --> | ||
| <div id="toast" role="status" aria-live="polite" | ||
| class="hidden mb-5 flex items-center gap-2 text-sm rounded-lg px-4 py-3 border"> | ||
| <svg id="toast-icon" class="w-4 h-4 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"></svg> | ||
| <span id="toast-text" class="flex-1"></span> | ||
| <button type="button" onclick="document.getElementById('toast').classList.add('hidden')" | ||
| class="ml-2 shrink-0 opacity-60 hover:opacity-100 cursor-pointer transition-opacity duration-150 focus:outline-none focus:ring-2 focus:ring-current rounded" | ||
| aria-label="關閉通知" data-i18n-aria-label="aria-toast-close"> | ||
| <svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"> | ||
| <line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/> | ||
| </svg> | ||
| </button> | ||
| </div> | ||
|
|
||
| <!-- ── Profile Card ── --> | ||
| <!-- Desktop: p-8 rounded-2xl (padding 32, radius 16) | Mobile: p-4 rounded-xl (padding 16, radius 12) --> | ||
| <div class="bg-white border border-slate-200 rounded-xl md:rounded-2xl p-4 md:p-8 flex flex-col gap-5 md:gap-6"> | ||
|
|
@@ -322,6 +308,23 @@ <h2 class="text-xl md:text-2xl font-bold text-ink" data-i18n="page-title">個人 | |
| </nav> | ||
|
|
||
|
|
||
| <!-- ── Toast — fixed bottom-right (per design system spec) ── --> | ||
| <div id="toast" | ||
| class="hidden fixed bottom-6 right-6 z-[400] max-w-sm w-full" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Toast container overflows left viewport edge on mobile due to The toast container uses Fix optionsRemove Prompt for agentsWas this helpful? React with 👍 or 👎 to provide feedback. |
||
| aria-live="polite" aria-atomic="true"> | ||
| <div id="toast-inner" class="flex items-center gap-2 text-sm rounded-lg px-4 py-3 border shadow-md"> | ||
| <svg id="toast-icon" class="w-4 h-4 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"></svg> | ||
| <span id="toast-text" class="flex-1"></span> | ||
| <button type="button" onclick="document.getElementById('toast').classList.add('hidden')" | ||
| class="ml-2 shrink-0 opacity-60 hover:opacity-100 cursor-pointer transition-opacity duration-150 focus:outline-none focus:ring-2 focus:ring-current rounded" | ||
| aria-label="關閉通知" data-i18n-aria-label="aria-toast-close"> | ||
| <svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"> | ||
| <line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/> | ||
| </svg> | ||
| </button> | ||
| </div> | ||
| </div> | ||
|
|
||
| <!-- ── i18n + Logic ── --> | ||
| <script> | ||
| const STRINGS = { | ||
|
|
@@ -533,19 +536,20 @@ <h2 class="text-xl md:text-2xl font-bold text-ink" data-i18n="page-title">個人 | |
| } | ||
|
|
||
| function showToast(type) { | ||
| const toast = document.getElementById('toast'); | ||
| const toast = document.getElementById('toast'); | ||
| const inner = document.getElementById('toast-inner'); | ||
| const toastTxt = document.getElementById('toast-text'); | ||
| const toastIcon = document.getElementById('toast-icon'); | ||
| const s = STRINGS[currentLang]; | ||
|
|
||
| toast.classList.remove('hidden', 'bg-green-50', 'border-green-200', 'text-green-700', | ||
| 'bg-red-50', 'border-red-200', 'text-red-700'); | ||
| inner.classList.remove('bg-green-50', 'border-green-200', 'text-green-700', | ||
| 'bg-red-50', 'border-red-200', 'text-red-700'); | ||
| if (type === 'success') { | ||
| toast.classList.add('bg-green-50', 'border-green-200', 'text-green-700'); | ||
| inner.classList.add('bg-green-50', 'border-green-200', 'text-green-700'); | ||
| toastTxt.textContent = s['toast-success']; | ||
| toastIcon.innerHTML = `<polyline points="20 6 9 17 4 12"/>`; | ||
| } else { | ||
| toast.classList.add('bg-red-50', 'border-red-200', 'text-red-700'); | ||
| inner.classList.add('bg-red-50', 'border-red-200', 'text-red-700'); | ||
| toastTxt.textContent = s['toast-error']; | ||
| toastIcon.innerHTML = `<circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/>`; | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.