@markdstage/markdstage 0.1.3 → 2.3.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.3.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
@@ -87,6 +87,13 @@ The themed slide is displayed and updates automatically
87
87
  It does not load or watch that file. AI may call `export_pdf` with another
88
88
  `outputPath`. Hidden print mode renders every page, then headless Edge/Chrome
89
89
  produces a 16:9 PDF with backgrounds, images, highlighted code, and Mermaid.
90
+ - **Hybrid editable PowerPoint export is available from the P control.** It
91
+ preserves supported text, lists, links, tables, raster images, and Architecture
92
+ DSL objects as native PowerPoint content. Architecture nodes, groups, and
93
+ connector-label pills are visible AutoShapes with integrated text; icons use a
94
+ transparent foreground picture layer. Mermaid and unsupported styling stay
95
+ visible as background artwork and are listed in the export report. AI may call
96
+ `export_pptx` with another workspace-confined `.pptx` path.
90
97
  - Use the **16:9 control** to letterbox the current slide inside the canvas with
91
98
  the same fixed 1280×720 typography, spacing, diagram limits, and clipping used
92
99
  by PDF output. This preview is local to the canvas and does not change deck
@@ -724,6 +731,8 @@ Architecture diagrams can be moved directly over the rendered result. For decks
724
731
  imported through 📂, edits write back to the source `architecture` fence and
725
732
  survive re-import. Decks supplied directly through open / `load_deck` cannot be
726
733
  reversibly mapped to a source file, so they save only to canvas deck state.
734
+ In the standalone CLI, `markdstage present slides.md --watch` enables the same
735
+ source-backed editing workflow; `present` without `--watch` is read-only.
727
736
 
728
737
  **Placement editing is a stable part of Architecture DSL v1.**
729
738
 
@@ -743,6 +752,7 @@ Enter mode with:
743
752
  | Method | Purpose |
744
753
  | --- | --- |
745
754
  | Canvas action `edit_architecture` with `{ "enabled": true }` | Normal agent-controlled operation |
755
+ | Pencil control during CLI `present --watch` | User-controlled live authoring; the browser starts with editing off |
746
756
  | Renderer URL `?architectureEdit=1` | Local debugging |
747
757
 
748
758
  `reset` disables editing. The query parameter also updates server state through
@@ -765,8 +775,10 @@ region announces results.
765
775
 
766
776
  #### Dedicated Architecture Editor
767
777
 
768
- `architecture-editor` is a separate canvas for comprehensive editing of an
769
- **existing** Markdown `architecture` block. It does not insert new blocks.
778
+ `architecture-editor` is a dedicated surface for comprehensive editing of an
779
+ **existing** Markdown `architecture` block. The Canvas Extension opens a
780
+ separate canvas; CLI watch mode opens a browser window. It does not insert new
781
+ blocks.
770
782
 
771
783
  - Add, delete, duplicate, reorder, and reparent nodes, groups, images, and
772
784
  connectors.
@@ -793,11 +805,11 @@ region announces results.
793
805
  - Changes remain in an in-memory draft until explicit **Save** or `save`.
794
806
  - External file changes produce a conflict. Call `reload` with
795
807
  `{ "discard": true }` to discard the draft explicitly.
796
- - Saving reloads any MarkdStage canvas showing the same Markdown while
797
- preserving page and theme.
808
+ - Saving reloads any MarkdStage canvas or watched CLI presentation showing the
809
+ same Markdown while preserving the current page and theme.
798
810
 
799
- Only source-backed decks imported through 📂 enable **Advanced editing**.
800
- Agents can open the editor directly:
811
+ Source-backed decks imported through 📂 and CLI `present --watch` enable
812
+ **Advanced editing**. Agents can open the editor canvas directly:
801
813
 
802
814
  ```json
803
815
  {
@@ -843,8 +855,8 @@ requires authoring it again.
843
855
 
844
856
  #### Presenter and print never include edit UI
845
857
 
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
858
+ For presenter, preview, print, and capture output, edit UI is not constructed in
859
+ the DOM. When mode is disabled, the server also rejects `POST /edit` with
848
860
  `409 edit_mode_disabled`. `npm run test:editing` locks both behaviors.
849
861
 
850
862
  #### Known editing tradeoff
@@ -908,7 +920,7 @@ Canvas buttons and keyboard continue to work when pen input is unavailable.
908
920
  Start with `open_canvas` (`canvasId: "MarkdStage"`) and complete-deck input:
909
921
  `{ slides: string[], index?: number, theme?: "dark" | "light" | "microsoft" |
910
922
  "custom", sourceName?: string }`. Pass the source Markdown filename in
911
- `sourceName`; the printer saves `<source-name-without-extension>.pdf`. This is
923
+ `sourceName`; the PDF and PowerPoint controls derive output names from it. This is
912
924
  metadata for resolution and output naming only and never reads or watches the
913
925
  file. The open handler applies the deck before returning its URL. Omit input
914
926
  when only refocusing an existing canvas. Any non-empty input without `slides`
@@ -926,6 +938,7 @@ fails with `invalid_input`; pass the complete `slides` array or call
926
938
  | `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
939
  | `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
940
  | `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. |
941
+ | `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
942
  | `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
943
  | `reset` | No input. Clear deck/slide state, disable editing, and return to the waiting view. |
931
944
 
@@ -940,11 +953,12 @@ fails with `invalid_input`; pass the complete `slides` array or call
940
953
  | --- | --- |
941
954
  | `GET /state` | Return current `markdown`, `index`, `total`, `theme`, `mode`, `architectureEdit`, source/watch state, `version`, and `deckVersion`. |
942
955
  | `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`. |
956
+ | `GET /export-data` | Return the token-bound deck snapshot to print, inspection, capture, or PowerPoint mode. |
957
+ | `POST /export-status` | Let headless output modes report rendering status and fixed-layout diagnostics. |
945
958
  | `POST /navigate` | Accept absolute `{ index }` or relative `{ delta }`, update position, and notify all clients through SSE. |
946
959
  | `POST /present` | Start the presenter from ⛶; accepts same-origin POST only. |
947
960
  | `POST /export` | Start source-named PDF export from the printer icon; accepts same-origin POST only. |
961
+ | `POST /export-pptx` | Start source-named hybrid PowerPoint export from the P control; accepts same-origin POST only. |
948
962
  | `POST /edit` | Write an edited fence with `{ index, block, source }`; returns `409 edit_mode_disabled` when editing is off. |
949
963
  | `POST /edit-mode` | Set `{ enabled }`, including for `?architectureEdit=1`; same-origin POST only. |
950
964
  | `POST /architecture-editor/open` | Convert source-backed slide/block indexes to a file-wide block index and open Architecture Editor. |
@@ -958,7 +972,7 @@ fails with `invalid_input`; pass the complete `slides` array or call
958
972
  ```text
959
973
  .github/extensions/markdstage/
960
974
  extension.mjs # Canvas declaration, loopback server, and actions
961
- architecture-canvas.mjs # Architecture Editor state, validation, save, conflicts
975
+ architecture-canvas.mjs # Canvas adapter for the shared Architecture Editor
962
976
  architecture-editor/
963
977
  index.html # Full diagram-editor canvas shell
964
978
  editor.css # Workspace, tree, and inspector styles
@@ -979,6 +993,9 @@ fails with `invalid_input`; pass the complete `slides` array or call
979
993
  architecture-edit.mjs # DOM-independent move/detach/Undo/Redo/serialization
980
994
  architecture-editor.mjs # Placement UI and Advanced editing entry point
981
995
  architecture-document.mjs # Full-editor command/session API
996
+ runtime/
997
+ architecture-editor-server.mjs # Shared detailed-editor state and routes
998
+ architecture-source.mjs # Atomic Architecture block persistence and conflicts
982
999
  schema/
983
1000
  architecture-v1.schema.json # Architecture DSL v1 JSON Schema (draft 2020-12)
984
1001
  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
+ }