@markdstage/markdstage 0.1.3 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,12 +1,11 @@
1
1
  # MarkdStage CLI
2
2
 
3
- Present, validate, inspect, capture, and export MarkdStage Markdown decks from a
4
- terminal — no GitHub Copilot canvas required.
3
+ Present, validate, inspect, capture, and export MarkdStage Markdown decks from a terminal — no GitHub Copilot canvas required.
5
4
 
6
5
  The CLI reuses the very same Markdown parser, renderer, Architecture DSL
7
- validation, theme handling, and PDF/PNG pipeline as the MarkdStage canvas
6
+ validation, theme handling, and PDF/PNG/PowerPoint pipeline as the MarkdStage canvas
8
7
  Extension, so a deck looks identical in Copilot, MarkdStage Desktop, the CLI, and
9
- the exported PDF.
8
+ exported PDF, or hybrid editable PowerPoint deck.
10
9
 
11
10
  ## Requirements
12
11
 
@@ -17,6 +16,7 @@ the exported PDF.
17
16
  ## Install
18
17
 
19
18
  ```console
19
+ npx @markdstage/markdstage presentation slides.md
20
20
  npx @markdstage/markdstage present slides.md
21
21
  npm install --global @markdstage/markdstage
22
22
  ```
@@ -24,11 +24,13 @@ npm install --global @markdstage/markdstage
24
24
  ## Commands
25
25
 
26
26
  ```console
27
+ markdstage presentation slides.md
27
28
  markdstage present slides.md --watch
28
29
  markdstage validate slides.md --json
29
30
  markdstage inspect slides.md --json
30
31
  markdstage capture slides.md --pages 2,4
31
32
  markdstage export slides.md --output slides.pdf
33
+ markdstage export slides.md --output slides.pptx
32
34
  markdstage guide architecture-dsl
33
35
  markdstage skill install --target codex
34
36
  markdstage skill install --target claude
@@ -36,11 +38,12 @@ markdstage skill install --target claude
36
38
 
37
39
  | Command | Description |
38
40
  | --- | --- |
39
- | `present` | Serves the deck on loopback and opens the MarkdStage presenter window: navigation, presenter view, next-slide preview, speaker notes, overview, custom themes, Mermaid, Architecture DSL, and local assets. `--watch` reloads on save while preserving the current slide and keeping the last valid deck when a save is broken. `--no-open` serves the deck only. |
41
+ | `presentation` | Opens the presenter view with the current slide, next-slide preview, speaker notes, and navigation. Select **Start presentation** to open the synchronized audience-facing window; select **End presentation** to close it. `--watch` reloads on save, and `--no-open` serves the presenter URL without launching a browser. |
42
+ | `present` | Serves the deck on loopback and opens the MarkdStage presenter window: navigation, presenter view, next-slide preview, speaker notes, overview, custom themes, Mermaid, Architecture DSL, and local assets. `--watch` reloads on save while preserving the current slide, keeps the last valid deck when a save is broken, and enables Architecture editing. Without `--watch`, the source is read-only. `--no-open` serves the deck only. |
40
43
  | `validate` | Checks deck structure, Architecture DSL blocks, themes, and theme paths. |
41
44
  | `inspect` | Reports the same compact 1280x720 clipping diagnostics as the canvas `inspect_layout` action. `--slide <n>` limits it to one page, `--all` includes slides that fit, `--fail-on-issues` exits with code 5. |
42
45
  | `capture` | Writes 1280x720 PNG files. Without `--pages` only the slides reported as clipped are captured. |
43
- | `export` | Produces the same 16:9 PDF as the canvas Extension. |
46
+ | `export` | Produces the same 16:9 PDF or hybrid editable PowerPoint as the canvas Extension. The `--output` extension selects the format; PDF remains the default. |
44
47
  | `guide` | Prints the canonical `markdstage_guide` topics. |
45
48
  | `skill` | Installs or checks the portable Agent Skills for Codex (`.agents/skills/markdstage/`), Claude Code (`.claude/skills/markdstage/`), and GitHub Copilot (`.github/skills/markdstage/`). Locally modified files are never overwritten without `--force`. |
46
49
  | `help` | Shows the overview, or the help for one command. `markdstage help <command>` prints the same text as `markdstage <command> --help`. |
@@ -48,6 +51,18 @@ markdstage skill install --target claude
48
51
  Global options: `--workspace <dir>`, `--theme <name>`, `--theme-file <path>`,
49
52
  `--json`, `--help`, `--version`.
50
53
 
54
+ ## Architecture editing
55
+
56
+ Run `markdstage present slides.md --watch` for the live authoring workflow. The
57
+ browser starts in viewing mode. Select the pencil control to move Architecture
58
+ elements; those placement changes are saved atomically to the matching
59
+ `architecture` fence. Select **Advanced edit** to add, update, duplicate,
60
+ reparent, or delete elements in the detailed designer, then select **Save**.
61
+
62
+ The server rejects a save if the Markdown changed outside the editor. Successful
63
+ saves reload the watched deck without changing the current slide. Presenter,
64
+ capture, inspect, and export views contain no editing UI.
65
+
51
66
  ```console
52
67
  markdstage help
53
68
  markdstage help capture
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markdstage/markdstage",
3
- "version": "0.1.3",
3
+ "version": "2.4.0",
4
4
  "description": "Present, validate, inspect, capture, and export MarkdStage Markdown decks from the command line — no Copilot canvas required.",
5
5
  "license": "MIT",
6
6
  "author": "runceel",
@@ -25,6 +25,8 @@
25
25
  "slides",
26
26
  "presentation",
27
27
  "pdf",
28
+ "pptx",
29
+ "powerpoint",
28
30
  "cli",
29
31
  "agent-skills"
30
32
  ],
package/shared/README.md CHANGED
@@ -27,8 +27,8 @@ Canvas iframe (renderer/)
27
27
  | highlights language-tagged code fences with highlight.js
28
28
  | converts ```mermaid blocks with mermaid.run
29
29
  | converts validated ```architecture JSON DSL into a safe SVG DOM
30
- | provides ◀ ▶, ✎, 16:9 PDF preview, margin clicks, arrow keys, and the ☰ slide list
31
- | opens a synchronized external window with
30
+ | keepspage ▶, ☰, and visible in a compact control bar
31
+ | groups editing, presentation, preview, import, refresh, and export under ⋯
32
32
  v
33
33
  The themed slide is displayed and updates automatically
34
34
  ```
@@ -75,19 +75,30 @@ The themed slide is displayed and updates automatically
75
75
  - Put **speaker notes** in top-level HTML comments on each slide. Presenter view
76
76
  renders notes as Markdown and follows navigation. Notes are absent from
77
77
  regular slides, the external presenter, and PDF output.
78
- - The **canvas renderer** owns navigation controls, editing, the slide list,
79
- and current position. toggles the same placement mode as
80
- `edit_architecture`. For Markdown loaded with 📂, **Advanced editing** opens
78
+ - The **canvas renderer** owns the compact navigation controls, shape editing,
79
+ the slide list, and current position. **More controls > Shape editing**
80
+ toggles the same placement mode as `edit_architecture`. For Markdown loaded
81
+ with **More controls > Open Markdown**, **Advanced editing** opens
81
82
  the dedicated `architecture-editor` canvas. The agent only opens the deck and
82
83
  does not run an `ask_user` loop. Margin clicks are installed only in normal
83
84
  canvas and presenter modes, never print mode. `goto_slide` remains available
84
85
  for an explicit page request from chat.
85
- - **PDF Export is available from the printer icon.** When `sourceName` is passed
86
+ - **PDF Export is available from More controls > Export PDF.** When
87
+ `sourceName` is passed
86
88
  to open / `load_deck`, the printer saves `<source-name>.pdf` in the workspace.
87
89
  It does not load or watch that file. AI may call `export_pdf` with another
88
90
  `outputPath`. Hidden print mode renders every page, then headless Edge/Chrome
89
91
  produces a 16:9 PDF with backgrounds, images, highlighted code, and Mermaid.
90
- - Use the **16:9 control** to letterbox the current slide inside the canvas with
92
+ - **Hybrid editable PowerPoint export is available from More controls > Export
93
+ PowerPoint.** It
94
+ preserves supported text, lists, links, tables, raster images, and Architecture
95
+ DSL objects as native PowerPoint content. Architecture nodes, groups, and
96
+ connector-label pills are visible AutoShapes with integrated text; icons use a
97
+ transparent foreground picture layer. Mermaid and unsupported styling stay
98
+ visible as background artwork and are listed in the export report. AI may call
99
+ `export_pptx` with another workspace-confined `.pptx` path.
100
+ - Use **More controls > Output preview** to letterbox the current slide inside
101
+ the canvas with
91
102
  the same fixed 1280×720 typography, spacing, diagram limits, and clipping used
92
103
  by PDF output. This preview is local to the canvas and does not change deck
93
104
  state. A visible and accessible warning identifies content that would be
@@ -102,7 +113,8 @@ The themed slide is displayed and updates automatically
102
113
  element hints. Call `capture_slides` only when visual inspection is needed;
103
114
  PNGs are fixed 1280×720 files and the action returns paths instead of inline
104
115
  image data.
105
- - Open the **external presenter** with or `open_presenter`. Edge / Chrome /
116
+ - Open the **external presenter** with **More controls > External window** or
117
+ `open_presenter`. Edge / Chrome /
106
118
  Chromium starts in a dedicated temporary profile as a movable, resizable
107
119
  1280×720 app-mode window. It shares `/state`, `/navigate`, and SSE with the
108
120
  canvas, so keyboard and Surface Pen position remain synchronized. Move it to
@@ -117,12 +129,13 @@ The themed slide is displayed and updates automatically
117
129
  - Add language names such as `csharp`, `json`, or `diff` to code fences for
118
130
  highlight.js syntax highlighting.
119
131
 
120
- ## Markdown import (📂)
132
+ ## Markdown import
121
133
 
122
- Users can load Markdown directly from the canvas without AI. Press 📂 or `I` to
123
- open a filtered list of workspace `*.md` / `*.markdown` files. Selecting one
124
- loads it, splits it into slides, and replaces the deck. Import also works from
125
- the initial waiting view. The control is not shown in presenter or print mode.
134
+ Users can load Markdown directly from the canvas without AI. Select **More
135
+ controls > Open Markdown** or press `I` to open a filtered list of workspace
136
+ `*.md` / `*.markdown` files. Selecting one loads it, splits it into slides, and
137
+ replaces the deck. Import also works from the initial waiting view. The control
138
+ is not shown in presenter or print mode.
126
139
  The workspace root is the Git repository root when available, otherwise the
127
140
  folder opened for the current session.
128
141
 
@@ -721,9 +734,11 @@ should determine placement without writing every coordinate.
721
734
  ### Placement editing
722
735
 
723
736
  Architecture diagrams can be moved directly over the rendered result. For decks
724
- imported through 📂, edits write back to the source `architecture` fence and
737
+ imported through **More controls > Open Markdown**, edits write back to the source `architecture` fence and
725
738
  survive re-import. Decks supplied directly through open / `load_deck` cannot be
726
739
  reversibly mapped to a source file, so they save only to canvas deck state.
740
+ In the standalone CLI, `markdstage present slides.md --watch` enables the same
741
+ source-backed editing workflow; `present` without `--watch` is read-only.
727
742
 
728
743
  **Placement editing is a stable part of Architecture DSL v1.**
729
744
 
@@ -743,6 +758,7 @@ Enter mode with:
743
758
  | Method | Purpose |
744
759
  | --- | --- |
745
760
  | Canvas action `edit_architecture` with `{ "enabled": true }` | Normal agent-controlled operation |
761
+ | Pencil control during CLI `present --watch` | User-controlled live authoring; the browser starts with editing off |
746
762
  | Renderer URL `?architectureEdit=1` | Local debugging |
747
763
 
748
764
  `reset` disables editing. The query parameter also updates server state through
@@ -765,8 +781,10 @@ region announces results.
765
781
 
766
782
  #### Dedicated Architecture Editor
767
783
 
768
- `architecture-editor` is a separate canvas for comprehensive editing of an
769
- **existing** Markdown `architecture` block. It does not insert new blocks.
784
+ `architecture-editor` is a dedicated surface for comprehensive editing of an
785
+ **existing** Markdown `architecture` block. The Canvas Extension opens a
786
+ separate canvas; CLI watch mode opens a browser window. It does not insert new
787
+ blocks.
770
788
 
771
789
  - Add, delete, duplicate, reorder, and reparent nodes, groups, images, and
772
790
  connectors.
@@ -793,11 +811,11 @@ region announces results.
793
811
  - Changes remain in an in-memory draft until explicit **Save** or `save`.
794
812
  - External file changes produce a conflict. Call `reload` with
795
813
  `{ "discard": true }` to discard the draft explicitly.
796
- - Saving reloads any MarkdStage canvas showing the same Markdown while
797
- preserving page and theme.
814
+ - Saving reloads any MarkdStage canvas or watched CLI presentation showing the
815
+ same Markdown while preserving the current page and theme.
798
816
 
799
- Only source-backed decks imported through 📂 enable **Advanced editing**.
800
- Agents can open the editor directly:
817
+ Source-backed decks imported through **More controls > Open Markdown** and CLI `present --watch` enable
818
+ **Advanced editing**. Agents can open the editor canvas directly:
801
819
 
802
820
  ```json
803
821
  {
@@ -843,8 +861,8 @@ requires authoring it again.
843
861
 
844
862
  #### Presenter and print never include edit UI
845
863
 
846
- For `?present=1` and `?print=1`, edit UI is not constructed in the DOM. When
847
- mode is disabled, the server also rejects `POST /edit` with
864
+ For presenter, preview, print, and capture output, edit UI is not constructed in
865
+ the DOM. When mode is disabled, the server also rejects `POST /edit` with
848
866
  `409 edit_mode_disabled`. `npm run test:editing` locks both behaviors.
849
867
 
850
868
  #### Known editing tradeoff
@@ -895,7 +913,8 @@ special layout and are never auto-enlarged.
895
913
  | Long press | `Win+F18` | Previous slide |
896
914
 
897
915
  `Win+F19` double press and pen connection/removal/docking do not launch
898
- MarkdStage. Start the presenter through or explicit `open_presenter`.
916
+ MarkdStage. Start the presenter through **More controls > External window** or
917
+ explicit `open_presenter`.
899
918
 
900
919
  The Windows setting that lets apps override shortcut-button behavior may remain
901
920
  enabled. This implementation listens directly for Windows pen shortcuts rather
@@ -908,7 +927,7 @@ Canvas buttons and keyboard continue to work when pen input is unavailable.
908
927
  Start with `open_canvas` (`canvasId: "MarkdStage"`) and complete-deck input:
909
928
  `{ slides: string[], index?: number, theme?: "dark" | "light" | "microsoft" |
910
929
  "custom", sourceName?: string }`. Pass the source Markdown filename in
911
- `sourceName`; the printer saves `<source-name-without-extension>.pdf`. This is
930
+ `sourceName`; the PDF and PowerPoint controls derive output names from it. This is
912
931
  metadata for resolution and output naming only and never reads or watches the
913
932
  file. The open handler applies the deck before returning its URL. Omit input
914
933
  when only refocusing an existing canvas. Any non-empty input without `slides`
@@ -926,6 +945,7 @@ fails with `invalid_input`; pass the complete `slides` array or call
926
945
  | `inspect_layout` | `{ index?: number, includeFits?: boolean }`. Render the registered in-memory PDF snapshot with the fixed 1280×720 output layout; this does not inspect the source file on disk. Omit `index` for one preferred whole-deck inspection. Serialize targeted calls because PDF, layout, and PNG jobs are exclusive. By default, return only clipped pages; `includeFits` includes successful pages. Returns dimensions, issue counts, overflow measurements, nested scroll containers, and a bounded list of element hints. Requires Edge, Chrome, or Chromium. |
927
946
  | `capture_slides` | `{ indexes?: number[], outputDirectory?: string, theme?: "dark" | "light" | "microsoft" | "custom" }`. Generate PDF-equivalent 1280×720 PNGs for at most 10 zero-based indexes. When `indexes` is omitted, inspect the deck and capture only clipped pages. Paths stay inside the workspace; results contain paths and layout summaries, not image bytes. Requires Edge, Chrome, or Chromium. |
928
947
  | `export_pdf` | `{ outputPath?: string, theme?: "dark" | "light" | "microsoft" | "custom" }`. Export one 16:9 page per slide. Relative paths use workspace root; default is `markdstage.pdf`. Theme affects PDF only. Reject paths outside workspace and non-`.pdf` files. Temporary slide replacement and the automatic back cover are included. Returns `{ ok, path, total, theme, bytes }`. Requires Edge, Chrome, or Chromium. |
948
+ | `export_pptx` | `{ outputPath?: string, theme?: "dark" | "light" | "microsoft" | "custom" }`. Export a hybrid editable 16:9 PowerPoint deck. Supported text, lists, links, tables, raster images, and Architecture DSL objects remain native. Architecture nodes, groups, and connector-label pills are visible AutoShapes with integrated text; icons are foreground pictures. Mermaid and unsupported visuals become reported background fallbacks. Relative paths use workspace root; default is `markdstage.pptx`. Reject paths outside workspace and non-`.pptx` files. Temporary slide replacement and the automatic back cover are included. Returns `{ ok, path, total, theme, bytes, format, fallbackCount, fallbacks }`. Requires Edge, Chrome, or Chromium. |
929
949
  | `edit_architecture` | `{ enabled: boolean }`. Toggle placement editing. Imported decks also write to the source fence; direct decks write to canvas state. Presenter/print omit UI. Mode is not persisted and `reset` disables it. Returns `{ ok, enabled, version }`. |
930
950
  | `reset` | No input. Clear deck/slide state, disable editing, and return to the waiting view. |
931
951
 
@@ -940,16 +960,17 @@ fails with `invalid_input`; pass the complete `slides` array or call
940
960
  | --- | --- |
941
961
  | `GET /state` | Return current `markdown`, `index`, `total`, `theme`, `mode`, `architectureEdit`, source/watch state, `version`, and `deckVersion`. |
942
962
  | `GET /deck` | Return all `slides` and `deckVersion`; the ☰ list fetches only when the version changes. |
943
- | `GET /export-data` | Return the token-bound deck snapshot to print, inspection, or capture mode. |
944
- | `POST /export-status` | Let print/capture mode report rendering status and fixed-layout diagnostics to PDF export, `inspect_layout`, or `capture_slides`. |
963
+ | `GET /export-data` | Return the token-bound deck snapshot to print, inspection, capture, or PowerPoint mode. |
964
+ | `POST /export-status` | Let headless output modes report rendering status and fixed-layout diagnostics. |
945
965
  | `POST /navigate` | Accept absolute `{ index }` or relative `{ delta }`, update position, and notify all clients through SSE. |
946
- | `POST /present` | Start the presenter from ⛶; accepts same-origin POST only. |
947
- | `POST /export` | Start source-named PDF export from the printer icon; accepts same-origin POST only. |
966
+ | `POST /present` | Start the presenter from **More controls > External window**; accepts same-origin POST only. |
967
+ | `POST /export` | Start source-named PDF export from **More controls > Export PDF**; accepts same-origin POST only. |
968
+ | `POST /export-pptx` | Start source-named hybrid PowerPoint export from **More controls > Export PowerPoint**; accepts same-origin POST only. |
948
969
  | `POST /edit` | Write an edited fence with `{ index, block, source }`; returns `409 edit_mode_disabled` when editing is off. |
949
970
  | `POST /edit-mode` | Set `{ enabled }`, including for `?architectureEdit=1`; same-origin POST only. |
950
971
  | `POST /architecture-editor/open` | Convert source-backed slide/block indexes to a file-wide block index and open Architecture Editor. |
951
972
  | `GET /events` | SSE nudge for low-latency `version` changes. |
952
- | `GET /markdown-files` | Return bounded workspace-relative `*.md` / `*.markdown` paths for 📂, excluding `.git`, `node_modules`, and dot-prefixed entries. |
973
+ | `GET /markdown-files` | Return bounded workspace-relative `*.md` / `*.markdown` paths for **Open Markdown**, excluding `.git`, `node_modules`, and dot-prefixed entries. |
953
974
  | `POST /import` | Load and split `{ path, sourceMode?: "snapshot" | "live" }`; reject paths outside workspace, wrong extensions, and oversized files. Same-origin POST only. |
954
975
  | `POST /source-mode` | Switch a source-backed deck with `{ mode: "snapshot" | "live" }`; entering live mode loads the latest source. |
955
976
 
@@ -958,13 +979,13 @@ fails with `invalid_input`; pass the complete `slides` array or call
958
979
  ```text
959
980
  .github/extensions/markdstage/
960
981
  extension.mjs # Canvas declaration, loopback server, and actions
961
- architecture-canvas.mjs # Architecture Editor state, validation, save, conflicts
982
+ architecture-canvas.mjs # Canvas adapter for the shared Architecture Editor
962
983
  architecture-editor/
963
984
  index.html # Full diagram-editor canvas shell
964
985
  editor.css # Workspace, tree, and inspector styles
965
986
  editor.js # Diagram commands, draft, and explicit-save UI
966
987
  copilot-extension.json # Manifest for Gist sharing
967
- markdown-deck.mjs # Raw Markdown splitting for 📂 import
988
+ markdown-deck.mjs # Raw Markdown splitting for canvas import
968
989
  scripts/
969
990
  markdown-blocks.mjs # Scan and replace architecture fences
970
991
  markdown-files.mjs # Scan workspace Markdown
@@ -979,6 +1000,9 @@ fails with `invalid_input`; pass the complete `slides` array or call
979
1000
  architecture-edit.mjs # DOM-independent move/detach/Undo/Redo/serialization
980
1001
  architecture-editor.mjs # Placement UI and Advanced editing entry point
981
1002
  architecture-document.mjs # Full-editor command/session API
1003
+ runtime/
1004
+ architecture-editor-server.mjs # Shared detailed-editor state and routes
1005
+ architecture-source.mjs # Atomic Architecture block persistence and conflicts
982
1006
  schema/
983
1007
  architecture-v1.schema.json # Architecture DSL v1 JSON Schema (draft 2020-12)
984
1008
  README.md # Schema use, versioning, and migration policy
@@ -0,0 +1,155 @@
1
+ :root{
2
+ color-scheme:dark;
3
+ --editor-panel:#17191d;
4
+ --editor-panel-2:#20232a;
5
+ --editor-border:#3a3f49;
6
+ --editor-fg:#f2f4f8;
7
+ --editor-muted:#a8b0bd;
8
+ --editor-accent:#67a7ff;
9
+ --editor-accent-soft:#17365d;
10
+ --editor-danger:#ff8f8f;
11
+ --editor-grid:rgba(255,255,255,.07);
12
+ }
13
+ :root[data-theme="light"],:root[data-theme="microsoft"]{
14
+ color-scheme:light;
15
+ --editor-panel:#f5f7fa;
16
+ --editor-panel-2:#fff;
17
+ --editor-border:#c8ced8;
18
+ --editor-fg:#202124;
19
+ --editor-muted:#606775;
20
+ --editor-accent:#0f6cbd;
21
+ --editor-accent-soft:#dcecff;
22
+ --editor-danger:#b3261e;
23
+ --editor-grid:rgba(0,0,0,.08);
24
+ }
25
+ *{box-sizing:border-box;}
26
+ html,body{height:100%;margin:0;overflow:hidden;}
27
+ body{display:grid;grid-template-rows:auto minmax(0,1fr) auto;
28
+ color:var(--editor-fg);background:var(--editor-panel);font:14px/1.4 "Segoe UI",sans-serif;}
29
+ button,input,select,textarea{font:inherit;}
30
+ button{min-height:30px;padding:4px 9px;border:1px solid var(--editor-border);
31
+ border-radius:6px;color:var(--editor-fg);background:var(--editor-panel-2);cursor:pointer;}
32
+ button:hover:not(:disabled){border-color:var(--editor-accent);background:var(--editor-accent-soft);}
33
+ button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{
34
+ outline:2px solid var(--editor-accent);outline-offset:1px;}
35
+ button:disabled{opacity:.42;cursor:default;}
36
+ .editor-header{display:flex;align-items:center;gap:16px;padding:8px 12px;
37
+ border-bottom:1px solid var(--editor-border);background:var(--editor-panel-2);}
38
+ .editor-title{display:flex;align-items:center;gap:8px;min-width:230px;white-space:nowrap;}
39
+ .editor-title #sourceLabel{max-width:28vw;overflow:hidden;text-overflow:ellipsis;color:var(--editor-muted);}
40
+ .dirty-badge{padding:2px 7px;border-radius:999px;color:#fff;background:#b26a00;font-size:12px;}
41
+ .editor-actions{display:flex;align-items:center;gap:6px;min-width:0;overflow-x:auto;padding-bottom:2px;}
42
+ .toolbar-separator{width:1px;height:24px;background:var(--editor-border);flex:0 0 auto;}
43
+ .save-button{font-weight:700;color:#fff;background:#0f6cbd;border-color:#0f6cbd;}
44
+ .snap-toggle{display:flex;align-items:center;gap:4px;white-space:nowrap;color:var(--editor-muted);}
45
+ .editor-layout{display:grid;grid-template-columns:230px minmax(0,1fr) 300px;min-height:0;}
46
+ .editor-sidebar{min-height:0;overflow:auto;background:var(--editor-panel-2);}
47
+ .editor-sidebar h2{position:sticky;top:0;z-index:2;margin:0;padding:10px 12px;
48
+ border-bottom:1px solid var(--editor-border);background:var(--editor-panel-2);font-size:13px;}
49
+ .element-panel{border-right:1px solid var(--editor-border);}
50
+ .inspector-panel{border-left:1px solid var(--editor-border);}
51
+ .element-tree{padding:8px 0;}
52
+ .tree-item{display:flex;align-items:center;gap:7px;width:100%;min-height:30px;padding:4px 10px;
53
+ border:0;border-radius:0;text-align:left;background:transparent;}
54
+ .tree-item[aria-selected="true"]{background:var(--editor-accent-soft);color:var(--editor-accent);}
55
+ .tree-icon{width:18px;text-align:center;color:var(--editor-muted);}
56
+ .tree-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
57
+ .editor-viewport{position:relative;min-width:0;min-height:0;overflow:auto;
58
+ background-color:#101217;
59
+ background-image:linear-gradient(var(--editor-grid) 1px,transparent 1px),
60
+ linear-gradient(90deg,var(--editor-grid) 1px,transparent 1px);
61
+ background-size:20px 20px;}
62
+ :root[data-theme="light"] .editor-viewport,:root[data-theme="microsoft"] .editor-viewport{
63
+ background-color:#e9edf3;}
64
+ .canvas-surface{display:flex;align-items:flex-start;justify-content:center;width:max-content;min-width:100%;min-height:100%;
65
+ padding:48px;transform-origin:top center;}
66
+ .canvas-surface>.architecture-diagram{flex:0 0 auto;width:960px;margin:0;padding:18px;
67
+ border:1px solid var(--editor-border);border-radius:10px;background:var(--surface);
68
+ box-shadow:0 12px 36px rgba(0,0,0,.3);}
69
+ .canvas-surface .architecture-svg{width:100%;max-height:none;overflow:visible;touch-action:none;}
70
+ .canvas-surface [data-editor-ref]{cursor:pointer;outline:none;}
71
+ .canvas-surface [data-editor-ref][data-editor-movable="true"]{cursor:grab;}
72
+ .canvas-surface [data-editor-selected="true"]>rect,
73
+ .canvas-surface [data-editor-selected="true"]>ellipse{
74
+ stroke:var(--editor-accent);stroke-width:7;filter:drop-shadow(0 0 7px var(--editor-accent));}
75
+ .canvas-surface [data-architecture-type="connector"][data-editor-selected="true"] path{
76
+ stroke:var(--editor-accent);stroke-width:8;}
77
+ .editor-resize-handle{fill:var(--surface);stroke:var(--editor-accent);stroke-width:4;cursor:nwse-resize;}
78
+ .editor-resize-handle[data-corner="ne"],.editor-resize-handle[data-corner="sw"]{cursor:nesw-resize;}
79
+ .connector-source>rect,.connector-source>ellipse{stroke:#f9c74f!important;stroke-width:8!important;}
80
+ .inspector{display:flex;flex-direction:column;gap:10px;padding:12px;}
81
+ .inspector-section{display:grid;grid-template-columns:minmax(88px,1fr) minmax(0,1.35fr);
82
+ gap:7px 10px;padding-bottom:11px;border-bottom:1px solid var(--editor-border);}
83
+ .inspector-section h3{grid-column:1/-1;margin:0 0 3px;font-size:12px;color:var(--editor-muted);
84
+ text-transform:uppercase;letter-spacing:.06em;}
85
+ .inspector label{align-self:center;color:var(--editor-muted);font-size:12px;}
86
+ .inspector input,.inspector select,.inspector textarea{width:100%;min-width:0;padding:5px 7px;
87
+ border:1px solid var(--editor-border);border-radius:5px;color:var(--editor-fg);
88
+ background:var(--editor-panel);}
89
+ .inspector textarea{min-height:72px;resize:vertical;font-family:"Cascadia Code",Consolas,monospace;}
90
+ .inspector input[type="checkbox"]{width:auto;justify-self:start;}
91
+ .inspector-help{grid-column:1/-1;margin:0;color:var(--editor-muted);font-size:12px;}
92
+ .editor-footer{display:flex;justify-content:space-between;gap:12px;padding:6px 12px;
93
+ border-top:1px solid var(--editor-border);background:var(--editor-panel-2);
94
+ color:var(--editor-muted);font-size:12px;}
95
+ .editor-footer [data-kind="error"]{color:var(--editor-danger);font-weight:600;}
96
+ .context-menu{position:fixed;z-index:1000;min-width:220px;max-width:min(320px,calc(100vw - 16px));
97
+ padding:5px;border:1px solid var(--editor-border);border-radius:8px;color:var(--editor-fg);
98
+ background:var(--editor-panel-2);box-shadow:0 12px 32px rgba(0,0,0,.38);}
99
+ .context-menu[hidden]{display:none;}
100
+ .context-menu-item{display:flex;align-items:center;justify-content:space-between;gap:20px;
101
+ width:100%;min-height:32px;padding:5px 9px;border:0;border-radius:5px;text-align:left;
102
+ color:var(--editor-fg);background:transparent;}
103
+ .context-menu-item:hover:not(:disabled),.context-menu-item:focus-visible{
104
+ outline:0;background:var(--editor-accent-soft);color:var(--editor-accent);}
105
+ .context-menu-item:disabled{opacity:.45;cursor:default;}
106
+ .context-menu-item[data-danger="true"]{color:var(--editor-danger);}
107
+ .context-menu-shortcut{color:var(--editor-muted);font-size:12px;white-space:nowrap;}
108
+ .context-menu-submenu-host{position:relative;}
109
+ .context-submenu{position:fixed;}
110
+ .context-menu-submenu-indicator{margin-left:auto;color:var(--editor-muted);font-size:18px;line-height:1;}
111
+ .context-menu-check{width:14px;color:var(--editor-accent);text-align:center;}
112
+ .context-submenu .context-menu-item{justify-content:flex-start;gap:8px;}
113
+ .context-menu-separator{height:1px;margin:4px 6px;border:0;background:var(--editor-border);}
114
+ .inspector-action-row{grid-column:2;display:flex;gap:6px;}
115
+ .inspector-action-row button{width:100%;}
116
+ .asset-dialog{width:min(900px,calc(100vw - 40px));height:min(680px,calc(100vh - 40px));
117
+ padding:0;border:1px solid var(--editor-border);border-radius:12px;color:var(--editor-fg);
118
+ background:var(--editor-panel-2);box-shadow:0 24px 80px rgba(0,0,0,.55);}
119
+ .asset-dialog::backdrop{background:rgba(0,0,0,.65);}
120
+ .asset-dialog[open]{display:grid;grid-template-rows:auto auto minmax(0,1fr) auto;}
121
+ .asset-dialog-header,.asset-dialog-tools,.asset-dialog-footer{display:flex;align-items:center;gap:12px;
122
+ padding:12px 16px;border-bottom:1px solid var(--editor-border);}
123
+ .asset-dialog-header{justify-content:space-between;}
124
+ .asset-dialog-header h2{margin:0;font-size:18px;}
125
+ .asset-dialog-header p{margin:2px 0 0;color:var(--editor-muted);font-size:12px;}
126
+ .asset-dialog-header button{min-width:36px;font-size:20px;}
127
+ .asset-dialog-tools label{color:var(--editor-muted);font-size:12px;white-space:nowrap;}
128
+ .asset-dialog-tools input[type="search"]{min-width:160px;flex:1;padding:6px 9px;border:1px solid var(--editor-border);
129
+ border-radius:6px;color:var(--editor-fg);background:var(--editor-panel);}
130
+ .asset-dialog-body{display:grid;grid-template-columns:minmax(0,1fr) 280px;min-height:0;}
131
+ .asset-list{display:grid;grid-template-columns:repeat(auto-fill,minmax(132px,1fr));align-content:start;
132
+ gap:8px;min-height:0;padding:12px;overflow:auto;border-right:1px solid var(--editor-border);}
133
+ .asset-list-empty{grid-column:1/-1;margin:20px;color:var(--editor-muted);text-align:center;}
134
+ .asset-option{display:grid;grid-template-rows:96px auto;gap:6px;min-width:0;padding:7px;text-align:left;}
135
+ .asset-option[aria-selected="true"]{border-color:var(--editor-accent);background:var(--editor-accent-soft);}
136
+ .asset-option img{width:100%;height:96px;object-fit:contain;border-radius:4px;background:repeating-conic-gradient(#d7dae0 0 25%,#fff 0 50%) 50%/16px 16px;}
137
+ .asset-option span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;}
138
+ .asset-preview{display:flex;flex-direction:column;gap:8px;min-width:0;padding:16px;}
139
+ .asset-preview-frame{display:grid;place-items:center;min-height:240px;border:1px solid var(--editor-border);
140
+ border-radius:8px;overflow:hidden;background:repeating-conic-gradient(#d7dae0 0 25%,#fff 0 50%) 50%/18px 18px;}
141
+ #assetPreviewImageHost{display:contents;}
142
+ .asset-preview-frame img{display:block;width:100%;height:100%;max-height:360px;object-fit:contain;}
143
+ .asset-preview-frame span{padding:12px;color:#424750;background:rgba(255,255,255,.72);text-align:center;}
144
+ .asset-preview p{margin:0;overflow-wrap:anywhere;color:var(--editor-muted);font-size:12px;}
145
+ .asset-dialog-footer{justify-content:space-between;border-top:1px solid var(--editor-border);border-bottom:0;}
146
+ .asset-dialog-footer>span{min-width:0;color:var(--editor-muted);font-size:12px;}
147
+ .asset-dialog-footer>span[data-kind="error"]{color:var(--editor-danger);font-weight:600;}
148
+ .asset-dialog-footer>div{display:flex;gap:8px;}
149
+ @media(max-width:900px){
150
+ .editor-layout{grid-template-columns:180px minmax(0,1fr);}
151
+ .inspector-panel{position:absolute;right:0;top:52px;bottom:28px;width:280px;z-index:5;
152
+ box-shadow:-10px 0 24px rgba(0,0,0,.25);}
153
+ .asset-dialog-body{grid-template-columns:minmax(0,1fr);}
154
+ .asset-preview{display:none;}
155
+ }