@1agh/maude 0.16.0 → 0.17.2
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 +4 -0
- package/cli/cli-wrapper.cjs +0 -0
- package/cli/commands/design.mjs +264 -16
- package/package.json +12 -18
- package/plugins/design/dev-server/annotations-context-toolbar.tsx +8 -8
- package/plugins/design/dev-server/annotations-layer.tsx +8 -10
- package/plugins/design/dev-server/api.ts +41 -0
- package/plugins/design/dev-server/bin/_enumerate-artboards-playwright.mjs +40 -0
- package/plugins/design/dev-server/bin/_html-playwright.mjs +129 -0
- package/plugins/design/dev-server/bin/_pdf-playwright.mjs +105 -0
- package/plugins/design/dev-server/bin/_png-playwright.mjs +143 -0
- package/plugins/design/dev-server/bin/_pptx-playwright.mjs +98 -0
- package/plugins/design/dev-server/bin/_svg-playwright.mjs +141 -0
- package/plugins/design/dev-server/build.ts +118 -6
- package/plugins/design/dev-server/canvas-lib.tsx +12 -13
- package/plugins/design/dev-server/canvas-pipeline.ts +5 -0
- package/plugins/design/dev-server/canvas-shell.tsx +32 -4
- package/plugins/design/dev-server/client/app.jsx +18 -1
- package/plugins/design/dev-server/context-menu.tsx +36 -9
- package/plugins/design/dev-server/dist/client.bundle.js +11 -3
- package/plugins/design/dev-server/export-dialog.tsx +401 -0
- package/plugins/design/dev-server/exporters/_browser-bundles.ts +89 -0
- package/plugins/design/dev-server/exporters/canva-handoff-prompt.ts +74 -0
- package/plugins/design/dev-server/exporters/canva.ts +126 -0
- package/plugins/design/dev-server/exporters/html.ts +103 -0
- package/plugins/design/dev-server/exporters/index.ts +135 -0
- package/plugins/design/dev-server/exporters/pdf.ts +109 -0
- package/plugins/design/dev-server/exporters/png.ts +136 -0
- package/plugins/design/dev-server/exporters/pptx.ts +263 -0
- package/plugins/design/dev-server/exporters/scope.ts +196 -0
- package/plugins/design/dev-server/exporters/svg.ts +122 -0
- package/plugins/design/dev-server/exporters/zip.ts +109 -0
- package/plugins/design/dev-server/http.ts +80 -0
- package/plugins/design/dev-server/inspect.ts +1 -1
- package/plugins/design/dev-server/server.mjs +1 -1
- package/plugins/design/dev-server/test/compile-entry.test.ts +134 -0
- package/plugins/design/dev-server/test/exporters/canva.test.ts +64 -0
- package/plugins/design/dev-server/test/exporters/endpoint.test.ts +121 -0
- package/plugins/design/dev-server/test/exporters/history.test.ts +79 -0
- package/plugins/design/dev-server/test/exporters/html.test.ts +26 -0
- package/plugins/design/dev-server/test/exporters/pdf.test.ts +53 -0
- package/plugins/design/dev-server/test/exporters/png.test.ts +32 -0
- package/plugins/design/dev-server/test/exporters/pptx.test.ts +31 -0
- package/plugins/design/dev-server/test/exporters/scope.test.ts +0 -0
- package/plugins/design/dev-server/test/exporters/svg.test.ts +29 -0
- package/plugins/design/dev-server/test/exporters/zip.test.ts +105 -0
- package/plugins/design/dev-server/tool-palette.tsx +34 -16
- package/plugins/design/templates/_shell.html +33 -0
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
//
|
|
16
16
|
// Per DDR-009 (Bun runtime authoritative) + DDR-012 (React 19 unified) + DDR-014 (Lightning CSS).
|
|
17
17
|
|
|
18
|
-
import { existsSync, mkdirSync } from 'node:fs';
|
|
19
|
-
import { dirname, join, resolve } from 'node:path';
|
|
18
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
19
|
+
import { dirname, join, relative, resolve } from 'node:path';
|
|
20
20
|
import { fileURLToPath } from 'node:url';
|
|
21
21
|
|
|
22
22
|
import { browserslistToTargets, bundle as lcssBundle } from 'lightningcss';
|
|
@@ -122,14 +122,117 @@ async function buildCss(): Promise<{ outBytes: number; outPath: string }> {
|
|
|
122
122
|
|
|
123
123
|
// ---------- (c) Server binary (bun build --compile, per-platform) ----------
|
|
124
124
|
|
|
125
|
+
// Per-target oxc-parser binding embed. Bun 1.3.4+ regressed `--compile` NAPI
|
|
126
|
+
// native-binding embedding (see DDR-042-oxc-parser-bun-compile-workaround.md).
|
|
127
|
+
// `with { type: 'file' }` requires a literal string path, so we generate one
|
|
128
|
+
// thin entry file per --target whose only purpose is to (1) embed the matching
|
|
129
|
+
// platform binding as an asset, (2) set NAPI_RS_NATIVE_LIBRARY_PATH from the
|
|
130
|
+
// runtime virtual path, then (3) hand off to the real server.ts. ESM
|
|
131
|
+
// evaluation order guarantees the env var is set before any downstream module
|
|
132
|
+
// touches oxc-parser.
|
|
133
|
+
|
|
134
|
+
// oxc-parser's NAPI-RS binding packages use a richer slug than our build
|
|
135
|
+
// target slug — Linux needs the libc kind suffix, Windows needs the toolchain
|
|
136
|
+
// suffix. Mirrored from oxc-parser/src-js/bindings.js loader cases.
|
|
137
|
+
function oxcBindingSlug(slug: string): string {
|
|
138
|
+
if (slug === 'linux-x64') return 'linux-x64-gnu';
|
|
139
|
+
if (slug === 'linux-arm64') return 'linux-arm64-gnu';
|
|
140
|
+
if (slug === 'win32-x64') return 'win32-x64-msvc';
|
|
141
|
+
// darwin-arm64 / darwin-x64 / linux-x64-musl / linux-arm64-musl already match.
|
|
142
|
+
return slug;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// npm/bun's installer respects each package's `os`/`cpu` fields and skips
|
|
146
|
+
// platform sub-packages whose filter doesn't match the host (the bindings
|
|
147
|
+
// declare e.g. `"cpu": ["x64"]`). For cross-compile targets — most notably
|
|
148
|
+
// darwin-x64 built on a darwin-arm64 CI runner — this means the matching
|
|
149
|
+
// `@oxc-parser/binding-<oxcSlug>` is NOT in node_modules even though it's
|
|
150
|
+
// listed as a direct devDependency. Fetch + extract the tarball manually so
|
|
151
|
+
// `with { type: 'file' }` resolution succeeds during `bun build --compile`.
|
|
152
|
+
async function ensureBindingForTarget(oxcSlug: string): Promise<void> {
|
|
153
|
+
const bindingDir = join(ROOT, 'node_modules', '@oxc-parser', `binding-${oxcSlug}`);
|
|
154
|
+
const bindingFile = join(bindingDir, `parser.${oxcSlug}.node`);
|
|
155
|
+
if (existsSync(bindingFile)) return; // host install already placed it
|
|
156
|
+
|
|
157
|
+
// Read the installed oxc-parser version so the tarball URL stays in sync.
|
|
158
|
+
// Walk up from build.ts to find the workspace-visible oxc-parser package.json
|
|
159
|
+
// (pnpm hoists it under node_modules/.pnpm, then symlinks at workspace level).
|
|
160
|
+
let oxcVersion = '0.131.0';
|
|
161
|
+
const candidates = [
|
|
162
|
+
join(ROOT, 'node_modules', 'oxc-parser', 'package.json'),
|
|
163
|
+
join(ROOT, '..', '..', '..', 'node_modules', 'oxc-parser', 'package.json'),
|
|
164
|
+
];
|
|
165
|
+
for (const c of candidates) {
|
|
166
|
+
if (existsSync(c)) {
|
|
167
|
+
oxcVersion = JSON.parse(readFileSync(c, 'utf8')).version;
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
console.log(`[build] cross-compile: fetching @oxc-parser/binding-${oxcSlug}@${oxcVersion}`);
|
|
173
|
+
const tarballUrl = `https://registry.npmjs.org/@oxc-parser/binding-${oxcSlug}/-/binding-${oxcSlug}-${oxcVersion}.tgz`;
|
|
174
|
+
const response = await fetch(tarballUrl);
|
|
175
|
+
if (!response.ok) {
|
|
176
|
+
throw new Error(`failed to fetch ${tarballUrl}: HTTP ${response.status}`);
|
|
177
|
+
}
|
|
178
|
+
const tmpTgz = join(ROOT, `.binding-${oxcSlug}.tgz`);
|
|
179
|
+
await Bun.write(tmpTgz, await response.arrayBuffer());
|
|
180
|
+
mkdirSync(bindingDir, { recursive: true });
|
|
181
|
+
// npm pack output: `package/parser.<oxcSlug>.node` + `package/package.json`.
|
|
182
|
+
// --strip-components=1 drops the leading `package/` so files land in bindingDir.
|
|
183
|
+
const tarProc = Bun.spawn(['tar', 'xf', tmpTgz, '-C', bindingDir, '--strip-components=1'], {
|
|
184
|
+
stdout: 'inherit',
|
|
185
|
+
stderr: 'inherit',
|
|
186
|
+
});
|
|
187
|
+
const tarCode = await tarProc.exited;
|
|
188
|
+
if (tarCode !== 0)
|
|
189
|
+
throw new Error(`tar extraction failed for binding-${oxcSlug} (exit ${tarCode})`);
|
|
190
|
+
unlinkSync(tmpTgz);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function writeCompileEntry(target: PlatformTarget): string {
|
|
194
|
+
const slug = platformSlug(target);
|
|
195
|
+
const oxcSlug = oxcBindingSlug(slug);
|
|
196
|
+
const entryDir = join(DIST, '.compile-entries');
|
|
197
|
+
mkdirSync(entryDir, { recursive: true });
|
|
198
|
+
|
|
199
|
+
// ESM hoists `import` statements above top-level code, so the env-var
|
|
200
|
+
// assignment MUST live in a separate leaf module that's imported BEFORE
|
|
201
|
+
// server.ts — otherwise server.ts (and transitively oxc-parser) evaluates
|
|
202
|
+
// first and reads NAPI_RS_NATIVE_LIBRARY_PATH before we set it.
|
|
203
|
+
const initPath = join(entryDir, `init-oxc-${slug}.ts`);
|
|
204
|
+
const entryPath = join(entryDir, `server-${slug}.ts`);
|
|
205
|
+
const bindingSpec = `@oxc-parser/binding-${oxcSlug}/parser.${oxcSlug}.node`;
|
|
206
|
+
const initContent = `// AUTO-GENERATED by build.ts — do not edit by hand.
|
|
207
|
+
// Per-target oxc-parser binding embed (Bun 1.3.4+ --compile NAPI regression
|
|
208
|
+
// workaround — see .ai/decisions/DDR-042-oxc-parser-bun-compile-workaround.md).
|
|
209
|
+
// Side-effect module: must be imported BEFORE any oxc-parser usage.
|
|
210
|
+
import bindingPath from ${JSON.stringify(bindingSpec)} with { type: 'file' };
|
|
211
|
+
process.env.NAPI_RS_NATIVE_LIBRARY_PATH = bindingPath;
|
|
212
|
+
`;
|
|
213
|
+
writeFileSync(initPath, initContent);
|
|
214
|
+
|
|
215
|
+
// Forward slashes for the import specifier even on Windows hosts — ESM uses
|
|
216
|
+
// POSIX-style paths, not the host's filesystem separator.
|
|
217
|
+
const realServerRel = relative(entryDir, join(ROOT, 'server.ts')).split('\\').join('/');
|
|
218
|
+
const entryContent = `// AUTO-GENERATED by build.ts — do not edit by hand.
|
|
219
|
+
// Per-target compile entry — see DDR-042-oxc-parser-bun-compile-workaround.md.
|
|
220
|
+
import './init-oxc-${slug}.ts';
|
|
221
|
+
import ${JSON.stringify(realServerRel)};
|
|
222
|
+
`;
|
|
223
|
+
writeFileSync(entryPath, entryContent);
|
|
224
|
+
return entryPath;
|
|
225
|
+
}
|
|
226
|
+
|
|
125
227
|
async function buildServerBinary(target: PlatformTarget): Promise<{ outPath: string }> {
|
|
126
228
|
ensureDist();
|
|
127
229
|
const slug = platformSlug(target);
|
|
128
230
|
const ext = slug.startsWith('win32') ? '.exe' : '';
|
|
129
231
|
const outPath = join(DIST, `maude-${slug}${ext}`);
|
|
130
|
-
const
|
|
131
|
-
if (!existsSync(
|
|
132
|
-
//
|
|
232
|
+
const realEntry = join(ROOT, 'server.ts');
|
|
233
|
+
if (!existsSync(realEntry)) {
|
|
234
|
+
// Legacy fallback — pre-DDR-009 .mjs path. Doesn't use oxc-parser, so the
|
|
235
|
+
// 1.3.4+ regression doesn't apply; entry stub generator is skipped.
|
|
133
236
|
const legacy = join(ROOT, 'server.mjs');
|
|
134
237
|
if (!existsSync(legacy)) throw new Error(`Neither server.ts nor server.mjs exists in ${ROOT}`);
|
|
135
238
|
const proc = Bun.spawn(
|
|
@@ -150,6 +253,10 @@ async function buildServerBinary(target: PlatformTarget): Promise<{ outPath: str
|
|
|
150
253
|
throw new Error(`bun build --compile (legacy) failed for ${target} (exit ${code})`);
|
|
151
254
|
return { outPath };
|
|
152
255
|
}
|
|
256
|
+
// Make sure the target's NAPI binding is on disk even if bun/npm's os/cpu
|
|
257
|
+
// filter skipped it (cross-compile case — see ensureBindingForTarget docs).
|
|
258
|
+
await ensureBindingForTarget(oxcBindingSlug(slug));
|
|
259
|
+
const entry = writeCompileEntry(target);
|
|
153
260
|
const proc = Bun.spawn(
|
|
154
261
|
[
|
|
155
262
|
'bun',
|
|
@@ -262,6 +369,11 @@ async function main() {
|
|
|
262
369
|
}
|
|
263
370
|
}
|
|
264
371
|
|
|
265
|
-
|
|
372
|
+
// Only run the build when invoked directly — importers (e.g. the test suite,
|
|
373
|
+
// which calls `writeCompileEntry` from build.ts) should not trigger a dev
|
|
374
|
+
// build as a side effect.
|
|
375
|
+
if (import.meta.main) {
|
|
376
|
+
await main();
|
|
377
|
+
}
|
|
266
378
|
|
|
267
379
|
export { buildClient, buildCss, buildServerBinary, PLATFORM_MATRIX, type PlatformTarget };
|
|
@@ -1182,7 +1182,6 @@ function DesignCanvasInner({ children, controls }: DesignCanvasProps) {
|
|
|
1182
1182
|
);
|
|
1183
1183
|
|
|
1184
1184
|
const showMiniMap = controls?.minimap !== false;
|
|
1185
|
-
const showToolbar = controls?.toolbar !== false;
|
|
1186
1185
|
|
|
1187
1186
|
// Drag-state bus (Phase 4.2). Single source of truth: only one artboard
|
|
1188
1187
|
// drag is active at a time. DCArtboards write here when their local drag
|
|
@@ -1492,15 +1491,15 @@ const OVERLAY_CSS = `
|
|
|
1492
1491
|
bottom: 16px;
|
|
1493
1492
|
width: 196px;
|
|
1494
1493
|
height: 132px;
|
|
1495
|
-
background: var(--bg-1, rgba(255,255,255,0.98));
|
|
1496
|
-
border: 1px solid var(--
|
|
1497
|
-
border-radius:
|
|
1498
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
1494
|
+
background: var(--bg-2, var(--bg-1, rgba(255,255,255,0.98)));
|
|
1495
|
+
border: 1px solid var(--fg-0, #1c1917);
|
|
1496
|
+
border-radius: 0;
|
|
1497
|
+
font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
|
1499
1498
|
font-size: 10px;
|
|
1500
|
-
color: rgba(40,30,20,0.7);
|
|
1499
|
+
color: var(--fg-1, rgba(40,30,20,0.7));
|
|
1501
1500
|
z-index: 6;
|
|
1502
1501
|
user-select: none;
|
|
1503
|
-
box-shadow:
|
|
1502
|
+
box-shadow: 4px 4px 0 var(--fg-0, #1c1917);
|
|
1504
1503
|
overflow: hidden;
|
|
1505
1504
|
}
|
|
1506
1505
|
.dc-mm-hd {
|
|
@@ -1534,15 +1533,15 @@ const OVERLAY_CSS = `
|
|
|
1534
1533
|
transform: translateX(-50%);
|
|
1535
1534
|
display: flex;
|
|
1536
1535
|
align-items: stretch;
|
|
1537
|
-
background: rgba(255,255,255,0.94);
|
|
1538
|
-
border: 1px solid
|
|
1539
|
-
border-radius:
|
|
1536
|
+
background: var(--bg-2, rgba(255,255,255,0.94));
|
|
1537
|
+
border: 1px solid var(--fg-0, #1c1917);
|
|
1538
|
+
border-radius: 0;
|
|
1540
1539
|
overflow: hidden;
|
|
1541
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
1540
|
+
font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
|
1542
1541
|
font-size: 11px;
|
|
1543
|
-
color: rgba(40,30,20,0.85);
|
|
1542
|
+
color: var(--fg-1, rgba(40,30,20,0.85));
|
|
1544
1543
|
z-index: 6;
|
|
1545
|
-
box-shadow:
|
|
1544
|
+
box-shadow: 4px 4px 0 var(--fg-0, #1c1917);
|
|
1546
1545
|
}
|
|
1547
1546
|
.dc-zoom-tb button {
|
|
1548
1547
|
appearance: none;
|
|
@@ -16,6 +16,11 @@
|
|
|
16
16
|
// Toolchain matches scripts/migrate-canvases.ts (codemod) and canvas-edit.ts
|
|
17
17
|
// (AST-aware /design:edit element edits) — same oxc-parser + magic-string pair
|
|
18
18
|
// in three call sites, one mental model.
|
|
19
|
+
//
|
|
20
|
+
// `bun build --compile` requires a per-target entry stub that sets
|
|
21
|
+
// NAPI_RS_NATIVE_LIBRARY_PATH before this file's transitive imports load
|
|
22
|
+
// oxc-parser. The pipeline itself is unaware of the workaround — it's a
|
|
23
|
+
// build-layer concern. See DDR-042-oxc-parser-bun-compile-workaround.md.
|
|
19
24
|
|
|
20
25
|
import MagicString from 'magic-string';
|
|
21
26
|
import { parseSync } from 'oxc-parser';
|
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
type MenuItem,
|
|
52
52
|
useContextMenu,
|
|
53
53
|
} from './context-menu.tsx';
|
|
54
|
+
import { ExportDialogProvider } from './export-dialog.tsx';
|
|
54
55
|
import { type HoverTarget, resolveHoverTarget, useInputRouter } from './input-router.tsx';
|
|
55
56
|
import { ToolPalette } from './tool-palette.tsx';
|
|
56
57
|
import {
|
|
@@ -203,9 +204,11 @@ function CanvasCore({
|
|
|
203
204
|
);
|
|
204
205
|
|
|
205
206
|
return (
|
|
206
|
-
<
|
|
207
|
-
<
|
|
208
|
-
|
|
207
|
+
<ExportDialogProvider>
|
|
208
|
+
<ContextMenuProvider registry={registry}>
|
|
209
|
+
<CanvasRouter hostRef={hostRef}>{children}</CanvasRouter>
|
|
210
|
+
</ContextMenuProvider>
|
|
211
|
+
</ExportDialogProvider>
|
|
209
212
|
);
|
|
210
213
|
}
|
|
211
214
|
|
|
@@ -263,6 +266,23 @@ function buildRegistry(deps: {
|
|
|
263
266
|
onSelect: () => controller?.reset(),
|
|
264
267
|
};
|
|
265
268
|
|
|
269
|
+
// Phase 6.5 — context-menu → ExportDialog. Each entry dispatches a custom
|
|
270
|
+
// event the dialog provider listens for; this avoids prop-drilling the
|
|
271
|
+
// dialog handle through every menu callback. The scope arg prefills the
|
|
272
|
+
// dialog's scope dropdown so the user lands on the right resolution.
|
|
273
|
+
const exportItem = (id: string, label: string, scope: string, shortcut?: string): MenuItem => ({
|
|
274
|
+
id,
|
|
275
|
+
label,
|
|
276
|
+
shortcut,
|
|
277
|
+
onSelect: () => {
|
|
278
|
+
try {
|
|
279
|
+
window.dispatchEvent(new CustomEvent('maude:open-export', { detail: { scope } }));
|
|
280
|
+
} catch {
|
|
281
|
+
/* non-window environments */
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
|
|
266
286
|
return {
|
|
267
287
|
element: [
|
|
268
288
|
[
|
|
@@ -298,6 +318,7 @@ function buildRegistry(deps: {
|
|
|
298
318
|
},
|
|
299
319
|
},
|
|
300
320
|
],
|
|
321
|
+
[exportItem('export-selection', 'Export selection…', 'selection', '⌘E')],
|
|
301
322
|
[
|
|
302
323
|
{
|
|
303
324
|
id: 'hide',
|
|
@@ -334,8 +355,15 @@ function buildRegistry(deps: {
|
|
|
334
355
|
fitItem,
|
|
335
356
|
resetItem,
|
|
336
357
|
],
|
|
358
|
+
[exportItem('export-artboard', 'Export this artboard…', 'artboard')],
|
|
359
|
+
],
|
|
360
|
+
world: [
|
|
361
|
+
[fitItem, resetItem],
|
|
362
|
+
[
|
|
363
|
+
exportItem('export-canvas', 'Export canvas as separate…', 'canvas-as-separate'),
|
|
364
|
+
exportItem('export-project', 'Export project (ZIP)…', 'project-raw'),
|
|
365
|
+
],
|
|
337
366
|
],
|
|
338
|
-
world: [[fitItem, resetItem]],
|
|
339
367
|
overlay: [],
|
|
340
368
|
};
|
|
341
369
|
}
|
|
@@ -1861,8 +1861,25 @@ function App() {
|
|
|
1861
1861
|
const activeFileComments = (activePath && activePath !== SYSTEM_TAB) ? (commentsByFile[activePath] || []) : [];
|
|
1862
1862
|
const totalOpen = totalCounts(commentsByFile).open;
|
|
1863
1863
|
|
|
1864
|
+
// Suppress the native browser context menu across the shell — the canvas
|
|
1865
|
+
// input-router already handles right-click inside the canvas host, but
|
|
1866
|
+
// sidebar / menubar / statusbar / floating chrome would otherwise leak the
|
|
1867
|
+
// native menu on top of our `.dc-context-menu` (or alone, outside canvas).
|
|
1868
|
+
// Editable fields (search box, future text inputs) keep the native menu so
|
|
1869
|
+
// copy/paste still works.
|
|
1870
|
+
const onShellContextMenu = useCallback((e) => {
|
|
1871
|
+
const t = e.target;
|
|
1872
|
+
if (t && (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' || (t.isContentEditable))) {
|
|
1873
|
+
return;
|
|
1874
|
+
}
|
|
1875
|
+
e.preventDefault();
|
|
1876
|
+
}, []);
|
|
1877
|
+
|
|
1864
1878
|
return (
|
|
1865
|
-
<div
|
|
1879
|
+
<div
|
|
1880
|
+
className={'app' + (commentsPanelOpen ? ' with-rsidebar' : '') + (sidebarOpen ? '' : ' no-sidebar')}
|
|
1881
|
+
onContextMenu={onShellContextMenu}
|
|
1882
|
+
>
|
|
1866
1883
|
<Sidebar
|
|
1867
1884
|
groups={groups}
|
|
1868
1885
|
activePath={activePath}
|
|
@@ -74,6 +74,27 @@ function noop(name: string) {
|
|
|
74
74
|
};
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
// Phase 6.5 T9 — export hooks. The default registry items use noop() so the
|
|
78
|
+
// menu still renders when the dialog provider isn't mounted; consumers wire
|
|
79
|
+
// real `openExport(scope)` callbacks by passing a custom registry to
|
|
80
|
+
// <ContextMenuProvider extra>. Pattern matches the existing Phase 5 noop
|
|
81
|
+
// affordances.
|
|
82
|
+
function defaultExportItem(label: string, scopeHint: string): MenuItem {
|
|
83
|
+
return {
|
|
84
|
+
id: `export-${scopeHint}`,
|
|
85
|
+
label,
|
|
86
|
+
shortcut: scopeHint === 'selection' ? '⌘E' : undefined,
|
|
87
|
+
onSelect: () => {
|
|
88
|
+
const detail = { scope: scopeHint };
|
|
89
|
+
try {
|
|
90
|
+
window.dispatchEvent(new CustomEvent('maude:open-export', { detail }));
|
|
91
|
+
} catch {
|
|
92
|
+
/* SSR / non-window environments */
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
77
98
|
const DEFAULT_REGISTRY: ContextRegistry = {
|
|
78
99
|
element: [
|
|
79
100
|
[
|
|
@@ -82,6 +103,7 @@ const DEFAULT_REGISTRY: ContextRegistry = {
|
|
|
82
103
|
{ id: 'copy-id', label: 'Copy data-cd-id', onSelect: noop('copy-id') },
|
|
83
104
|
{ id: 'inspect', label: 'Inspect', shortcut: '⌥I', onSelect: noop('inspect') },
|
|
84
105
|
],
|
|
106
|
+
[defaultExportItem('Export selection…', 'selection')],
|
|
85
107
|
[
|
|
86
108
|
{ id: 'hide', label: 'Hide', shortcut: '⌘⇧H', onSelect: noop('hide') },
|
|
87
109
|
{ id: 'lock', label: 'Lock', shortcut: '⌘⇧L', onSelect: noop('lock') },
|
|
@@ -92,6 +114,7 @@ const DEFAULT_REGISTRY: ContextRegistry = {
|
|
|
92
114
|
{ id: 'rename', label: 'Rename', shortcut: '↵', onSelect: noop('rename-artboard') },
|
|
93
115
|
{ id: 'duplicate', label: 'Duplicate', shortcut: '⌘D', onSelect: noop('duplicate-artboard') },
|
|
94
116
|
],
|
|
117
|
+
[defaultExportItem('Export this artboard…', 'artboard')],
|
|
95
118
|
[
|
|
96
119
|
{ id: 'fit-one', label: 'Fit just this artboard', onSelect: noop('fit-one') },
|
|
97
120
|
{ id: 'reset-pos', label: 'Reset position', onSelect: noop('reset-artboard-pos') },
|
|
@@ -108,6 +131,10 @@ const DEFAULT_REGISTRY: ContextRegistry = {
|
|
|
108
131
|
{ id: 'fit-view', label: 'Fit to view', shortcut: '1', onSelect: noop('fit-view') },
|
|
109
132
|
{ id: 'reset-view', label: 'Reset view', shortcut: '⌘0', onSelect: noop('reset-view') },
|
|
110
133
|
],
|
|
134
|
+
[
|
|
135
|
+
defaultExportItem('Export project (ZIP)…', 'project-raw'),
|
|
136
|
+
defaultExportItem('Export canvas as separate…', 'canvas-as-separate'),
|
|
137
|
+
],
|
|
111
138
|
],
|
|
112
139
|
overlay: [],
|
|
113
140
|
};
|
|
@@ -131,15 +158,15 @@ const MENU_CSS = `
|
|
|
131
158
|
.dc-context-menu {
|
|
132
159
|
position: fixed;
|
|
133
160
|
z-index: 7;
|
|
134
|
-
background: var(--bg-1, #fff);
|
|
135
|
-
border: 1px solid var(--
|
|
136
|
-
border-radius:
|
|
137
|
-
box-shadow: var(--
|
|
161
|
+
background: var(--u-bg-2, var(--bg-1, #fff));
|
|
162
|
+
border: 1px solid var(--u-fg-0, #1c1917);
|
|
163
|
+
border-radius: 0;
|
|
164
|
+
box-shadow: 4px 4px 0 var(--u-fg-0, #1c1917);
|
|
138
165
|
padding: 4px;
|
|
139
166
|
min-width: 220px;
|
|
140
|
-
font:
|
|
167
|
+
font-family: var(--u-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
|
141
168
|
font-size: 12px;
|
|
142
|
-
color: var(--fg-0, rgba(20,15,10,0.92));
|
|
169
|
+
color: var(--u-fg-0, var(--fg-0, rgba(20,15,10,0.92)));
|
|
143
170
|
user-select: none;
|
|
144
171
|
}
|
|
145
172
|
.dc-context-menu .dc-menu-sep {
|
|
@@ -152,8 +179,8 @@ const MENU_CSS = `
|
|
|
152
179
|
justify-content: space-between;
|
|
153
180
|
align-items: center;
|
|
154
181
|
gap: 16px;
|
|
155
|
-
padding:
|
|
156
|
-
border-radius:
|
|
182
|
+
padding: 5px 12px;
|
|
183
|
+
border-radius: 0;
|
|
157
184
|
cursor: pointer;
|
|
158
185
|
background: transparent;
|
|
159
186
|
border: 0;
|
|
@@ -164,7 +191,7 @@ const MENU_CSS = `
|
|
|
164
191
|
}
|
|
165
192
|
.dc-context-menu .dc-menu-item:hover,
|
|
166
193
|
.dc-context-menu .dc-menu-item:focus-visible {
|
|
167
|
-
background: var(--bg-3, rgba(0,0,0,0.05));
|
|
194
|
+
background: var(--u-bg-3, var(--bg-3, rgba(0,0,0,0.05)));
|
|
168
195
|
outline: none;
|
|
169
196
|
}
|
|
170
197
|
.dc-context-menu .dc-menu-item[disabled] {
|