veryfront 0.1.142 → 0.1.144

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.
Files changed (45) hide show
  1. package/esm/cli/commands/knowledge/command.d.ts.map +1 -1
  2. package/esm/cli/commands/knowledge/command.js +3 -3
  3. package/esm/cli/mcp/server.d.ts +1 -0
  4. package/esm/cli/mcp/server.d.ts.map +1 -1
  5. package/esm/cli/mcp/server.js +11 -6
  6. package/esm/cli/mcp/standalone.d.ts +1 -0
  7. package/esm/cli/mcp/standalone.d.ts.map +1 -1
  8. package/esm/cli/mcp/standalone.js +14 -11
  9. package/esm/cli/shared/config.d.ts.map +1 -1
  10. package/esm/cli/shared/config.js +13 -2
  11. package/esm/cli/templates/manifest.d.ts +484 -484
  12. package/esm/cli/templates/manifest.js +534 -534
  13. package/esm/deno.js +1 -1
  14. package/esm/src/build/embedded/preset.d.ts +10 -0
  15. package/esm/src/build/embedded/preset.d.ts.map +1 -1
  16. package/esm/src/build/embedded/preset.js +23 -11
  17. package/esm/src/mcp/server.d.ts +2 -0
  18. package/esm/src/mcp/server.d.ts.map +1 -1
  19. package/esm/src/mcp/server.js +42 -8
  20. package/esm/src/resource/factory.d.ts.map +1 -1
  21. package/esm/src/resource/factory.js +1 -0
  22. package/esm/src/resource/types.d.ts +2 -0
  23. package/esm/src/resource/types.d.ts.map +1 -1
  24. package/esm/src/server/dev-ui/manifest.d.ts +11 -11
  25. package/esm/src/server/dev-ui/manifest.js +12 -12
  26. package/esm/src/server/services/rsc/endpoints/action-parser.js +1 -1
  27. package/esm/src/studio/bridge/bridge-bundle.generated.d.ts.map +1 -1
  28. package/esm/src/studio/bridge/bridge-bundle.generated.js +1 -1
  29. package/esm/src/utils/version-constant.d.ts +1 -1
  30. package/esm/src/utils/version-constant.js +1 -1
  31. package/package.json +1 -1
  32. package/src/cli/commands/knowledge/command.ts +5 -3
  33. package/src/cli/mcp/server.ts +12 -6
  34. package/src/cli/mcp/standalone.ts +15 -11
  35. package/src/cli/shared/config.ts +14 -2
  36. package/src/cli/templates/manifest.js +534 -534
  37. package/src/deno.js +1 -1
  38. package/src/src/build/embedded/preset.ts +26 -10
  39. package/src/src/mcp/server.ts +52 -8
  40. package/src/src/resource/factory.ts +1 -0
  41. package/src/src/resource/types.ts +2 -0
  42. package/src/src/server/dev-ui/manifest.js +12 -12
  43. package/src/src/server/services/rsc/endpoints/action-parser.ts +1 -1
  44. package/src/src/studio/bridge/bridge-bundle.generated.ts +1 -1
  45. package/src/src/utils/version-constant.ts +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"bridge-bundle.generated.d.ts","sourceRoot":"","sources":["../../../../src/src/studio/bridge/bridge-bundle.generated.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,oBAAoB,EAAE,MAAskoD,CAAC"}
1
+ {"version":3,"file":"bridge-bundle.generated.d.ts","sourceRoot":"","sources":["../../../../src/src/studio/bridge/bridge-bundle.generated.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,oBAAoB,EAAE,MAAo2oD,CAAC"}
@@ -5,4 +5,4 @@
5
5
  * Do not edit manually — run `deno task build` to regenerate.
6
6
  * @module
7
7
  */
8
- export const STUDIO_BRIDGE_BUNDLE = "// src/studio/bridge/bridge-logger.ts\nfunction normalizeContext(context) {\n if (!context) return void 0;\n if (context instanceof Error) {\n return { error: context.message, ...context.stack ? { stack: context.stack } : {} };\n }\n return context;\n}\nfunction formatArgs(message, context) {\n const normalized = normalizeContext(context);\n if (!normalized || Object.keys(normalized).length === 0) {\n return [message];\n }\n return [message, normalized];\n}\nvar logger = {\n debug(message, context) {\n console.debug(...formatArgs(message, context));\n },\n info(message, context) {\n console.log(...formatArgs(message, context));\n },\n warn(message, context) {\n console.warn(...formatArgs(message, context));\n },\n error(message, context) {\n console.error(...formatArgs(message, context));\n }\n};\n\n// src/studio/bridge/bridge-config.ts\nvar config = null;\nvar DEFAULT_CONFIG = {\n projectId: \"\",\n pageId: \"\",\n pagePath: \"\",\n wsUrl: \"\",\n yjsGuid: \"\",\n studioMode: \"advanced\",\n debugSkipInit: false,\n debugExposeInternals: false\n};\nfunction resolveStudioMode(value, queryString) {\n const params = new URLSearchParams(queryString);\n return value === \"simple\" || params.get(\"vf_studio_mode\") === \"simple\" ? \"simple\" : \"advanced\";\n}\nfunction normalizeConfig(raw) {\n const queryString = window.location.search;\n if (!raw || typeof raw !== \"object\") {\n logger.warn(\"No bridge config found on window.__VF_BRIDGE_CONFIG__\");\n return {\n ...DEFAULT_CONFIG,\n studioMode: resolveStudioMode(void 0, queryString)\n };\n }\n return {\n ...DEFAULT_CONFIG,\n projectId: String(raw.projectId ?? \"\"),\n pageId: String(raw.pageId ?? \"\"),\n pagePath: String(raw.pagePath ?? raw.pageId ?? \"\"),\n wsUrl: String(raw.wsUrl ?? \"\"),\n yjsGuid: String(raw.yjsGuid ?? \"\"),\n studioMode: resolveStudioMode(raw.studioMode, queryString),\n debugSkipInit: !!raw.debugSkipInit,\n debugExposeInternals: !!raw.debugExposeInternals\n };\n}\nfunction initConfig() {\n const raw = globalThis.__VF_BRIDGE_CONFIG__;\n config = normalizeConfig(raw);\n}\nfunction getConfig() {\n if (!config) {\n throw new Error(\"[StudioBridge] Config not initialized. Call initConfig() first.\");\n }\n return config;\n}\n\n// src/studio/bridge/bridge-state.ts\nvar state = {\n // Inspector\n inspectMode: false,\n selectedNodeId: null,\n hoveredNodeId: null,\n lastTreeSignature: \"\",\n // Overlays\n hoverOverlay: null,\n selectionOverlay: null,\n // Console\n originalConsole: {},\n logCounter: 0,\n // Screenshot\n html2canvasLoaded: false,\n html2canvasPromise: null\n};\nvar CONSOLE_METHODS = [\n \"log\",\n \"debug\",\n \"info\",\n \"warn\",\n \"error\",\n \"table\",\n \"clear\",\n \"dir\"\n];\nvar DOM_IGNORE_TAGS = [\"SCRIPT\", \"STYLE\", \"LINK\", \"META\", \"NOSCRIPT\"];\n\n// src/studio/bridge/bridge-messaging.ts\nvar studioOrigin = null;\nfunction postToStudio(message) {\n if (!window.parent || window.parent === window) return;\n try {\n window.parent.postMessage(message, studioOrigin || \"*\");\n } catch (e) {\n logger.debug(\"postMessage failed\", e instanceof Error ? e : { error: String(e) });\n }\n}\nfunction isFromStudio(event) {\n try {\n if (!event.source || event.source === window) return false;\n const url = new URL(event.origin || \"\");\n const host = url.hostname;\n const valid = host === \"localhost\" || host.endsWith(\".veryfront.org\") || host === \"veryfront.org\" || host.endsWith(\".veryfront.com\") || host === \"veryfront.com\" || host.endsWith(\".veryfront.dev\") || host === \"veryfront.dev\";\n if (valid && !studioOrigin) {\n studioOrigin = event.origin;\n }\n return valid;\n } catch (_) {\n return false;\n }\n}\n\n// src/studio/bridge/bridge-styles.ts\nvar OVERLAY_CSS = `\n /* ------------------------------------------------------------------ */\n /* Overlays (hover / selection inspector) */\n /* ------------------------------------------------------------------ */\n\n .vf-overlay {\n position: fixed;\n pointer-events: none;\n z-index: 99999;\n box-sizing: border-box;\n transition: all 0.05s ease-out;\n }\n .vf-overlay-hover {\n border: 2px solid oklch(0.6852 0.162 241.8);\n background: oklch(0.6852 0.162 241.8 / 0.06);\n }\n .vf-overlay-selection {\n border: 2px solid oklch(0.6852 0.162 241.8);\n background: oklch(0.6852 0.162 241.8 / 0.1);\n }\n .vf-overlay-label {\n position: absolute;\n top: -22px;\n left: -2px;\n background: oklch(0.6852 0.162 241.8);\n color: white;\n font-size: 11px;\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n padding: 2px 6px;\n border-radius: 3px 3px 0 0;\n white-space: nowrap;\n pointer-events: none;\n }\n .vf-overlay-label-bottom {\n top: auto;\n bottom: -22px;\n border-radius: 0 0 3px 3px;\n }\n\n /* ------------------------------------------------------------------ */\n /* Edit button (floating CTA) */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-edit-button {\n position: fixed;\n right: 16px;\n bottom: 16px;\n z-index: 100001;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 9999px;\n background: oklch(0.2768 0 0);\n color: oklch(0.9512 0.008 98.88);\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 13px;\n font-weight: 500;\n line-height: 1;\n padding: 10px 16px;\n cursor: pointer;\n box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1);\n transition: transform 100ms ease, box-shadow 100ms ease;\n }\n .vf-markdown-edit-button:hover {\n box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1);\n }\n .vf-markdown-edit-button:active {\n transform: scale(0.98);\n }\n\n /* ------------------------------------------------------------------ */\n /* Editor root */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor {\n position: fixed;\n inset: 0;\n z-index: 100000;\n background: oklch(1 0 0);\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n display: none;\n }\n\n .vf-markdown-editor__history {\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 6px;\n background: oklch(1 0 0);\n color: oklch(0.2768 0 0);\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 13px;\n line-height: 1;\n min-width: 28px;\n height: 28px;\n padding: 0 8px;\n cursor: pointer;\n transition: background 75ms ease;\n }\n .vf-markdown-editor__history:hover {\n background: oklch(0.93 0 0);\n }\n\n /* ------------------------------------------------------------------ */\n /* Surface (editor area) */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__surface-wrap {\n position: relative;\n height: 100vh;\n }\n\n .vf-markdown-editor__surface {\n width: 100%;\n max-width: 980px;\n margin: 0 auto;\n height: 100%;\n overflow: auto;\n outline: none;\n position: relative;\n z-index: 1;\n background: transparent;\n padding: 32px 40px;\n box-sizing: border-box;\n }\n\n /* ------------------------------------------------------------------ */\n /* Selection overlay */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__selection-overlay {\n position: absolute;\n inset: 0;\n pointer-events: none;\n z-index: 2;\n display: none;\n }\n\n .vf-markdown-editor__selection-highlight {\n position: absolute;\n border-radius: 3px;\n opacity: 0.26;\n }\n\n .vf-markdown-editor__selection-caret {\n position: absolute;\n width: 2px;\n border-radius: 1px;\n }\n\n .vf-markdown-editor__selection-label {\n position: absolute;\n transform: translateY(-100%);\n margin-top: -4px;\n border-radius: 9999px;\n padding: 1px 7px;\n font-size: 10px;\n line-height: 1.4;\n white-space: nowrap;\n color: oklch(1 0 0);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.16);\n }\n\n /* ------------------------------------------------------------------ */\n /* Slash menu */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__slash-menu {\n position: fixed;\n z-index: 100005;\n min-width: 240px;\n max-width: 300px;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 8px;\n background: oklch(1 0 0);\n box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1);\n padding: 4px;\n display: none;\n }\n\n .vf-markdown-editor__slash-section {\n padding: 8px 10px 4px;\n font-size: 11px;\n font-weight: 600;\n color: oklch(0.55 0.005 95.11);\n text-transform: none;\n letter-spacing: 0;\n }\n\n .vf-markdown-editor__slash-item {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n border: 0;\n border-radius: 6px;\n background: transparent;\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n text-align: left;\n padding: 6px 10px;\n cursor: pointer;\n transition: background 75ms ease;\n }\n\n .vf-markdown-editor__slash-item:hover,\n .vf-markdown-editor__slash-item[data-active='true'] {\n background: oklch(0.93 0 0);\n }\n\n .vf-markdown-editor__slash-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 4px;\n background: oklch(1 0 0);\n font-size: 13px;\n font-weight: 600;\n color: oklch(0.2768 0 0);\n flex-shrink: 0;\n }\n\n .vf-markdown-editor__slash-item-title {\n display: block;\n font-size: 13px;\n font-weight: 500;\n color: oklch(0.2768 0 0);\n line-height: 1.35;\n flex: 1;\n }\n\n .vf-markdown-editor__slash-item-desc {\n display: none;\n }\n\n .vf-markdown-editor__slash-shortcut {\n font-size: 11px;\n color: oklch(0.55 0.005 95.11);\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n flex-shrink: 0;\n }\n\n .vf-markdown-editor__slash-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 10px;\n margin-top: 2px;\n border-top: 1px solid oklch(0.9 0 0);\n font-size: 11px;\n color: oklch(0.55 0.005 95.11);\n }\n\n .vf-markdown-editor__slash-footer-key {\n font-size: 10px;\n color: oklch(0.55 0.005 95.11);\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 3px;\n padding: 1px 4px;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n }\n\n /* ------------------------------------------------------------------ */\n /* Inline toolbar */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__inline-toolbar {\n position: fixed;\n z-index: 100006;\n display: none;\n flex-direction: column;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 8px;\n background: oklch(1 0 0);\n box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1);\n padding: 4px;\n }\n\n .vf-markdown-editor__inline-row {\n display: flex;\n align-items: center;\n gap: 2px;\n padding: 2px 0;\n }\n\n .vf-markdown-editor__inline-separator {\n width: 1px;\n height: 20px;\n background: oklch(0.9 0 0);\n margin: 0 2px;\n flex-shrink: 0;\n }\n\n .vf-markdown-editor__inline-button {\n border: 0;\n border-radius: 6px;\n background: transparent;\n color: oklch(0.2768 0 0);\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n min-width: 26px;\n height: 26px;\n padding: 0 7px;\n cursor: pointer;\n transition: background 75ms ease;\n }\n\n .vf-markdown-editor__inline-button:hover {\n background: oklch(0.93 0 0);\n }\n\n .vf-markdown-editor__inline-button.active {\n background: oklch(0.6852 0.162 241.8 / 0.14);\n color: oklch(0.6852 0.162 241.8);\n }\n\n .vf-markdown-editor__inline-button[data-format='bold'] {\n font-weight: 700;\n }\n\n .vf-markdown-editor__inline-button[data-format='italic'] {\n font-style: italic;\n }\n\n .vf-markdown-editor__inline-button[data-format='strikethrough'] {\n text-decoration: line-through;\n }\n\n /* ------------------------------------------------------------------ */\n /* Block type trigger */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__block-trigger {\n border: 0;\n border-radius: 6px;\n background: transparent;\n color: oklch(0.2768 0 0);\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n height: 26px;\n padding: 0 7px;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 2px;\n white-space: nowrap;\n transition: background 75ms ease;\n }\n\n .vf-markdown-editor__block-trigger:hover {\n background: oklch(0.93 0 0);\n }\n\n .vf-markdown-editor__block-trigger::after {\n content: '\\\\25BE';\n font-size: 10px;\n color: oklch(0.55 0.005 95.11);\n }\n\n /* ------------------------------------------------------------------ */\n /* Block dropdown */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__block-dropdown {\n position: absolute;\n top: 100%;\n left: 4px;\n z-index: 100007;\n min-width: 160px;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 6px;\n background: oklch(1 0 0);\n box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1);\n padding: 4px;\n margin-top: 4px;\n display: none;\n }\n\n .vf-markdown-editor__block-option {\n display: block;\n width: 100%;\n border: 0;\n border-radius: 6px;\n background: transparent;\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n text-align: left;\n padding: 6px 10px;\n font-size: 13px;\n font-weight: 500;\n color: oklch(0.2768 0 0);\n cursor: pointer;\n transition: background 75ms ease;\n }\n\n .vf-markdown-editor__block-option:hover {\n background: oklch(0.93 0 0);\n }\n\n .vf-markdown-editor__block-option.active {\n background: oklch(0.6852 0.162 241.8 / 0.1);\n color: oklch(0.6852 0.162 241.8);\n }\n\n /* ------------------------------------------------------------------ */\n /* Block drag handle */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__block-handle {\n position: fixed;\n z-index: 100007;\n display: none;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 6px;\n background: oklch(1 0 0);\n color: oklch(0.2768 0 0);\n font-size: 12px;\n font-weight: 700;\n line-height: 1;\n width: 28px;\n height: 28px;\n padding: 0;\n cursor: grab;\n box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px -1px rgba(0,0,0,0.1);\n transition: background 75ms ease, box-shadow 75ms ease;\n }\n\n .vf-markdown-editor__block-handle:hover {\n background: oklch(0.6852 0.162 241.8 / 0.1);\n }\n\n .vf-markdown-editor__block-handle[data-dragging='true'] {\n cursor: grabbing;\n }\n\n /* ------------------------------------------------------------------ */\n /* Block drop indicator */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__block-drop-indicator {\n position: fixed;\n z-index: 100006;\n display: none;\n height: 2px;\n border-radius: 9999px;\n background: oklch(0.6852 0.162 241.8);\n box-shadow: 0 1px 6px oklch(0.6852 0.162 241.8 / 0.5);\n }\n\n .vf-markdown-editor__block-drop-label {\n position: fixed;\n z-index: 100007;\n display: none;\n border-radius: 9999px;\n border: 1px solid oklch(0.6852 0.162 241.8 / 0.24);\n background: oklch(1 0 0 / 0.96);\n color: oklch(0.2768 0 0);\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 11px;\n font-weight: 600;\n line-height: 1.2;\n padding: 3px 8px;\n box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px -1px rgba(0,0,0,0.1);\n }\n\n /* ------------------------------------------------------------------ */\n /* Block drag ghost */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__block-drag-ghost {\n position: fixed;\n top: -9999px;\n left: -9999px;\n width: 260px;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 8px;\n background: oklch(1 0 0);\n box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1);\n padding: 8px 10px;\n }\n\n .vf-markdown-editor__block-drag-ghost-title {\n display: block;\n font-size: 11px;\n font-weight: 700;\n color: oklch(0.2768 0 0);\n }\n\n .vf-markdown-editor__block-drag-ghost-text {\n display: block;\n margin-top: 4px;\n font-size: 11px;\n color: oklch(0.55 0.005 95.11);\n line-height: 1.35;\n }\n\n /* ------------------------------------------------------------------ */\n /* MDX blocks bar */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__mdx-blocks {\n display: none;\n gap: 8px;\n padding: 8px 16px;\n border-bottom: 1px solid oklch(0.9 0 0);\n background: oklch(0.97 0 0 / 0.95);\n overflow-x: auto;\n }\n\n .vf-markdown-editor__mdx-block {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 6px;\n background: oklch(1 0 0);\n padding: 6px 8px;\n }\n\n .vf-markdown-editor__mdx-block-label {\n font-size: 11px;\n color: oklch(0.2768 0 0);\n white-space: nowrap;\n }\n\n .vf-markdown-editor__mdx-note {\n font-size: 10px;\n color: oklch(0.55 0.005 95.11);\n white-space: nowrap;\n }\n\n .vf-markdown-editor__mdx-open {\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 6px;\n background: oklch(1 0 0);\n color: oklch(0.2768 0 0);\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 11px;\n line-height: 1;\n padding: 5px 7px;\n cursor: pointer;\n white-space: nowrap;\n transition: background 75ms ease;\n }\n .vf-markdown-editor__mdx-open:hover {\n background: oklch(0.93 0 0);\n }\n\n /* ------------------------------------------------------------------ */\n /* Lexical surface overrides */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__surface [data-lexical-editor] {\n outline: none;\n }\n\n .vf-markdown-editor__surface s,\n .vf-markdown-editor__surface del,\n .vf-markdown-editor__surface [style*='line-through'] {\n text-decoration: line-through;\n }\n\n .vf-markdown-editor__surface p:empty::before {\n content: \"Type '/' for commands\";\n color: oklch(0.55 0.005 95.11 / 0.6);\n pointer-events: none;\n font-style: normal;\n }\n\n .vf-markdown-editor__surface h1:empty::before {\n content: 'Heading 1';\n color: oklch(0.55 0.005 95.11 / 0.6);\n pointer-events: none;\n }\n\n .vf-markdown-editor__surface h2:empty::before {\n content: 'Heading 2';\n color: oklch(0.55 0.005 95.11 / 0.6);\n pointer-events: none;\n }\n\n .vf-markdown-editor__surface h3:empty::before {\n content: 'Heading 3';\n color: oklch(0.55 0.005 95.11 / 0.6);\n pointer-events: none;\n }\n\n .vf-markdown-editor__surface blockquote:empty::before {\n content: 'Quote';\n color: oklch(0.55 0.005 95.11 / 0.6);\n pointer-events: none;\n }\n\n .vf-markdown-editor__surface p {\n min-height: 1.5em;\n }\n\n /* ------------------------------------------------------------------ */\n /* Textarea fallback */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__textarea {\n width: 100%;\n height: 100vh;\n border: 0;\n outline: none;\n resize: none;\n display: none;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;\n font-size: 14px;\n line-height: 1.6;\n color: oklch(0.2768 0 0);\n background: transparent;\n padding: 16px;\n box-sizing: border-box;\n }\n\n /* ================================================================== */\n /* DARK MODE */\n /* ================================================================== */\n\n [data-theme='dark'] .vf-markdown-editor {\n background: oklch(0.2768 0 0);\n }\n\n [data-theme='dark'] .vf-markdown-editor__history {\n background: oklch(0.3211 0 0);\n border-color: oklch(0.42 0.0017 106.48);\n color: oklch(0.9512 0.008 98.88);\n }\n [data-theme='dark'] .vf-markdown-editor__history:hover {\n background: oklch(0.25 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__textarea {\n color: oklch(0.9512 0.008 98.88);\n }\n\n /* Slash menu \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__slash-menu {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.21 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-section {\n color: oklch(0.5338 0.0046 106.55);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-item:hover,\n [data-theme='dark'] .vf-markdown-editor__slash-item[data-active='true'] {\n background: oklch(0.25 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-icon {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.3211 0 0);\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-item-title {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-shortcut {\n color: oklch(0.5338 0.0046 106.55);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-footer {\n border-top-color: oklch(0.3 0.01 220);\n color: oklch(0.5338 0.0046 106.55);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-footer-key {\n border-color: oklch(0.42 0.0017 106.48);\n color: oklch(0.5338 0.0046 106.55);\n }\n\n /* Inline toolbar \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__inline-toolbar {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.21 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__inline-separator {\n background: oklch(0.3 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__inline-button {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__inline-button:hover {\n background: oklch(0.25 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__inline-button.active {\n background: oklch(0.6852 0.162 241.8 / 0.2);\n color: oklch(0.75 0.14 241.8);\n }\n\n /* Block trigger \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__block-trigger {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-trigger:hover {\n background: oklch(0.25 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-trigger::after {\n color: oklch(0.5338 0.0046 106.55);\n }\n\n /* Block dropdown \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__block-dropdown {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.21 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-option {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-option:hover {\n background: oklch(0.25 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-option.active {\n background: oklch(0.6852 0.162 241.8 / 0.2);\n color: oklch(0.75 0.14 241.8);\n }\n\n /* Placeholder text \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__surface p:empty::before,\n [data-theme='dark'] .vf-markdown-editor__surface h1:empty::before,\n [data-theme='dark'] .vf-markdown-editor__surface h2:empty::before,\n [data-theme='dark'] .vf-markdown-editor__surface h3:empty::before,\n [data-theme='dark'] .vf-markdown-editor__surface blockquote:empty::before {\n color: oklch(0.5338 0.0046 106.55 / 0.5);\n }\n\n /* Block drag \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__block-handle {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.3211 0 0);\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-handle:hover {\n background: oklch(0.6852 0.162 241.8 / 0.2);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-drop-label {\n border-color: oklch(0.6852 0.162 241.8 / 0.35);\n background: oklch(0.18 0.01 220 / 0.96);\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-drag-ghost {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.3211 0 0);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-drag-ghost-title {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-drag-ghost-text {\n color: oklch(0.5338 0.0046 106.55);\n }\n\n /* MDX blocks \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__mdx-blocks {\n border-bottom-color: oklch(0.3 0.01 220);\n background: oklch(0.18 0.01 220 / 0.8);\n }\n\n [data-theme='dark'] .vf-markdown-editor__mdx-block {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.3211 0 0);\n }\n\n [data-theme='dark'] .vf-markdown-editor__mdx-block-label {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__mdx-note {\n color: oklch(0.5338 0.0046 106.55);\n }\n\n [data-theme='dark'] .vf-markdown-editor__mdx-open {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.2768 0 0);\n color: oklch(0.9512 0.008 98.88);\n }\n [data-theme='dark'] .vf-markdown-editor__mdx-open:hover {\n background: oklch(0.25 0.01 220);\n }\n\n /* Edit button \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-edit-button {\n background: oklch(0.9512 0.008 98.88);\n color: oklch(0.2768 0 0);\n border-color: oklch(0.42 0.0017 106.48);\n }\n`;\nfunction injectOverlayStyles() {\n if (document.getElementById(\"vf-overlay-styles\")) return;\n const style = document.createElement(\"style\");\n style.id = \"vf-overlay-styles\";\n style.textContent = OVERLAY_CSS;\n try {\n document.head.appendChild(style);\n if (!style.sheet) {\n logger.warn(\"Inline style injection may be blocked by CSP (style-src)\");\n }\n } catch (error) {\n logger.warn(\n \"Failed to inject bridge styles. This may be caused by CSP style-src restrictions.\",\n error instanceof Error ? error : {\n error: String(error)\n }\n );\n }\n}\n\n// src/studio/bridge/bridge-constants.ts\nvar DATA_VF_ID = \"data-vf-id\";\nvar DATA_VF_SELECTOR = \"data-vf-selector\";\nvar DATA_VF_TEXT = \"data-vf-text\";\nvar DATA_VF_IGNORE = \"data-vf-ignore\";\nvar DATA_NODE_ID = \"data-node-id\";\nvar DATA_NODE_FILE = \"data-node-file\";\nvar DATA_NODE_NAME = \"data-node-name\";\nvar DATA_NODE_LINE = \"data-node-line\";\nvar DATA_NODE_COLUMN = \"data-node-column\";\nvar DATA_NODE_SOURCE = \"data-node-source\";\n\n// src/studio/bridge/bridge-utils.ts\nfunction debounce(fn, ms) {\n let timer;\n const debounced = function(...args) {\n clearTimeout(timer);\n timer = setTimeout(() => {\n fn.apply(this, args);\n }, ms);\n };\n debounced.cancel = () => {\n clearTimeout(timer);\n timer = void 0;\n };\n return debounced;\n}\n\n// src/studio/bridge/bridge-inspector.ts\nfunction createOverlay(type) {\n const overlay = document.createElement(\"div\");\n overlay.className = \"vf-overlay vf-overlay-\" + type;\n overlay.setAttribute(DATA_VF_IGNORE, \"true\");\n const label = document.createElement(\"div\");\n label.className = \"vf-overlay-label\";\n overlay.appendChild(label);\n overlay.style.display = \"none\";\n document.body.appendChild(overlay);\n return overlay;\n}\nfunction hideOverlay(overlay) {\n if (overlay) overlay.style.display = \"none\";\n}\nfunction positionOverlay(overlay, element, nodeName) {\n if (!overlay) return;\n if (!element) {\n hideOverlay(overlay);\n return;\n }\n const rect = element.getBoundingClientRect();\n overlay.style.display = \"block\";\n overlay.style.top = rect.top + \"px\";\n overlay.style.left = rect.left + \"px\";\n overlay.style.width = rect.width + \"px\";\n overlay.style.height = rect.height + \"px\";\n const label = overlay.querySelector(\".vf-overlay-label\");\n if (label) {\n label.textContent = nodeName;\n if (rect.top < 24) {\n label.classList.add(\"vf-overlay-label-bottom\");\n } else {\n label.classList.remove(\"vf-overlay-label-bottom\");\n }\n }\n}\nfunction getNodeName(element) {\n const vfId = element.getAttribute(DATA_VF_ID);\n if (vfId) return vfId.split(\"_\")[0] ?? vfId;\n return element.tagName.toLowerCase();\n}\nfunction findElementById(nodeId) {\n if (!nodeId) return null;\n return document.querySelector(\"[\" + DATA_VF_ID + '=\"' + nodeId + '\"]') || document.querySelector(\"[\" + DATA_VF_SELECTOR + '=\"' + nodeId + '\"]') || document.querySelector(\"[\" + DATA_NODE_ID + '=\"' + nodeId + '\"]');\n}\nfunction isValidElement(el) {\n return !!el && el.nodeType === Node.ELEMENT_NODE && !DOM_IGNORE_TAGS.includes(el.tagName) && !el.hasAttribute(DATA_VF_IGNORE) && el.style.display !== \"none\";\n}\nfunction getNodeType(el) {\n const vfId = el.getAttribute(DATA_VF_ID) || \"\";\n if (vfId && /^[A-Z]/.test(vfId)) return \"component\";\n if (el.hasAttribute(DATA_VF_TEXT)) return \"text\";\n if (el.getAttribute(DATA_NODE_SOURCE) === \"md\") return \"markdown\";\n return \"element\";\n}\nfunction buildNavigatorTree(root) {\n const config3 = getConfig();\n let nodeIndex = 0;\n function processElement(el, parentId) {\n if (!isValidElement(el)) {\n const children = [];\n Array.from(el.children || []).forEach((child) => {\n children.push(...processElement(child, parentId));\n });\n return children;\n }\n let id = el.getAttribute(DATA_VF_ID) || el.getAttribute(DATA_NODE_ID) || el.getAttribute(DATA_VF_SELECTOR);\n if (!id) {\n id = \"vf-\" + el.tagName.toLowerCase() + \"-\" + ++nodeIndex;\n el.setAttribute(DATA_VF_SELECTOR, id);\n }\n const vfId = el.getAttribute(DATA_VF_ID);\n const name = vfId ? vfId.split(\"_\")[0] ?? vfId : el.tagName.toLowerCase();\n const node = {\n id,\n name,\n type: getNodeType(el),\n path: config3.pagePath,\n parentId,\n start: {\n line: parseInt(el.getAttribute(DATA_NODE_LINE) || \"0\", 10),\n column: parseInt(el.getAttribute(DATA_NODE_COLUMN) || \"0\", 10)\n },\n end: { line: 0, column: 0 },\n children: [],\n text: el.hasAttribute(DATA_VF_TEXT) ? el.textContent?.trim() : void 0,\n isRemote: false\n };\n Array.from(el.children || []).forEach((child) => {\n node.children.push(...processElement(child, id));\n });\n return [node];\n }\n const rootNode = {\n id: \"root\",\n name: \"root\",\n type: \"root\",\n path: \"\",\n parentId: \"\",\n start: { line: 0, column: 0 },\n end: { line: 0, column: 0 },\n children: [],\n isRemote: false\n };\n Array.from(root.children || []).forEach((child) => {\n rootNode.children.push(...processElement(child, \"root\"));\n });\n return rootNode;\n}\nfunction createTreeSignature(root) {\n const validElements = Array.from(root.querySelectorAll(\"*\")).filter((el) => isValidElement(el));\n return validElements.length + \"-\" + validElements.map((el) => el.tagName).join(\"\");\n}\nvar treeUpdateTimer = null;\nvar mutationObserver = null;\nfunction sendTreeUpdate() {\n const config3 = getConfig();\n const root = document.getElementById(\"root\") || document.body;\n if (!root) return;\n const signature = createTreeSignature(root);\n if (signature === state.lastTreeSignature) return;\n state.lastTreeSignature = signature;\n postToStudio({\n action: \"treeUpdated\",\n id: config3.pageId,\n url: window.location.href,\n tree: buildNavigatorTree(root),\n sourceHash: globalThis.__VERYFRONT_SOURCE_HASH__ || null\n });\n}\nfunction debouncedTreeUpdate() {\n if (treeUpdateTimer) clearTimeout(treeUpdateTimer);\n treeUpdateTimer = setTimeout(sendTreeUpdate, 150);\n}\nfunction setupMutationObserver() {\n const root = document.getElementById(\"root\") || document.body;\n if (!root) return;\n mutationObserver = new MutationObserver(function(mutations) {\n const hasRelevantChanges = mutations.some(\n (m) => m.type === \"childList\" || m.type === \"characterData\"\n );\n if (!hasRelevantChanges) return;\n if (state.selectedNodeId && !findElementById(state.selectedNodeId)) {\n state.selectedNodeId = null;\n hideOverlay(state.selectionOverlay);\n postToStudio({ action: \"setSelectedNode\", id: null });\n }\n debouncedTreeUpdate();\n });\n mutationObserver.observe(root, { childList: true, characterData: true, subtree: true });\n sendTreeUpdate();\n}\nfunction showOverlay(overlay, nodeId) {\n if (!nodeId) {\n hideOverlay(overlay);\n return;\n }\n const el = findElementById(nodeId);\n if (!el) {\n hideOverlay(overlay);\n return;\n }\n positionOverlay(overlay, el, getNodeName(el));\n}\nfunction showHoverOverlay(nodeId) {\n showOverlay(state.hoverOverlay, nodeId);\n}\nfunction showSelectionOverlay(nodeId) {\n showOverlay(state.selectionOverlay, nodeId);\n}\nfunction scrollToElement(nodeId) {\n const el = document.querySelector(\"[\" + DATA_VF_ID + '=\"' + nodeId + '\"]') || document.querySelector(\"[\" + DATA_NODE_ID + '=\"' + nodeId + '\"]') || document.querySelector(\"[\" + DATA_VF_SELECTOR + '*=\"' + nodeId + '\"]');\n if (el) el.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n}\nfunction getDirectText(el) {\n let text = \"\";\n for (let i = 0; i < el.childNodes.length; i++) {\n const node = el.childNodes[i];\n if (node?.nodeType === Node.TEXT_NODE) {\n text += node.textContent || \"\";\n }\n }\n return text.trim();\n}\nfunction setupInspectMode() {\n const INSPECTABLE_SELECTOR = \"[\" + DATA_VF_ID + \"], [\" + DATA_VF_SELECTOR + \"], [\" + DATA_NODE_ID + \"], [\" + DATA_NODE_FILE + \"]\";\n function getElementId(el) {\n return el.getAttribute(DATA_VF_ID) || el.getAttribute(DATA_NODE_ID) || el.getAttribute(DATA_VF_SELECTOR);\n }\n document.addEventListener(\n \"click\",\n function(event) {\n if (!state.inspectMode) return;\n event.preventDefault();\n event.stopPropagation();\n const target = event.target.closest(INSPECTABLE_SELECTOR);\n if (!target) {\n state.selectedNodeId = null;\n hideOverlay(state.selectionOverlay);\n postToStudio({ action: \"setSelectedNode\", id: null });\n return;\n }\n const id = getElementId(target);\n state.selectedNodeId = id;\n showSelectionOverlay(id);\n postToStudio({\n action: \"setSelectedNode\",\n id,\n node: {\n name: target.getAttribute(DATA_NODE_NAME) || target.tagName.toLowerCase(),\n type: getNodeType(target),\n file: target.getAttribute(DATA_NODE_FILE) || getConfig().pagePath,\n line: parseInt(target.getAttribute(DATA_NODE_LINE) || \"0\", 10),\n column: parseInt(target.getAttribute(DATA_NODE_COLUMN) || \"0\", 10),\n text: getDirectText(target).slice(0, 200)\n }\n });\n },\n true\n );\n document.addEventListener(\"pointerover\", function(event) {\n if (!state.inspectMode || event.pointerType === \"touch\") return;\n const target = event.target.closest(INSPECTABLE_SELECTOR);\n if (!target) return;\n const id = getElementId(target);\n if (id === state.hoveredNodeId) return;\n state.hoveredNodeId = id;\n showHoverOverlay(id);\n });\n document.addEventListener(\"pointerout\", function(event) {\n if (!state.inspectMode || event.pointerType === \"touch\") return;\n const target = event.target.closest(INSPECTABLE_SELECTOR);\n if (!target) return;\n const relatedTarget = event.relatedTarget;\n if (relatedTarget && target.contains(relatedTarget)) return;\n state.hoveredNodeId = null;\n hideOverlay(state.hoverOverlay);\n });\n const updateOverlays = debounce(function() {\n if (state.inspectMode && state.hoveredNodeId) showHoverOverlay(state.hoveredNodeId);\n if (state.selectedNodeId) showSelectionOverlay(state.selectedNodeId);\n }, 16);\n window.addEventListener(\"scroll\", updateOverlays, true);\n window.addEventListener(\"resize\", updateOverlays);\n}\nfunction setColorMode(mode) {\n document.documentElement.setAttribute(\"data-theme\", mode);\n document.documentElement.classList.remove(\"light\", \"dark\");\n document.documentElement.classList.add(mode);\n}\n\n// src/studio/bridge/bridge-console.ts\nfunction setupConsoleCapture() {\n const consoleObj = console;\n CONSOLE_METHODS.forEach((method) => {\n state.originalConsole[method] = consoleObj[method];\n consoleObj[method] = function(...args) {\n state.originalConsole[method].apply(console, args);\n const logId = \"vf-\" + Date.now() + \"-\" + ++state.logCounter;\n const formattedData = args.map((arg) => {\n try {\n if (arg instanceof Error) {\n return { __isError: true, message: arg.message, stack: arg.stack, name: arg.name };\n }\n if (arg === void 0) return { __isUndefined: true };\n if (arg === null) return null;\n if (typeof arg === \"function\") {\n return { __isFunction: true, name: arg.name || \"anonymous\" };\n }\n if (typeof arg === \"symbol\") return { __isSymbol: true, description: arg.description };\n if (typeof arg === \"object\") return JSON.parse(JSON.stringify(arg));\n return arg;\n } catch (_) {\n return String(arg);\n }\n });\n postToStudio({\n action: \"logEvent\",\n value: {\n id: logId,\n method,\n data: formattedData,\n timestamp: (/* @__PURE__ */ new Date()).toISOString()\n }\n });\n };\n });\n}\nfunction setupErrorHandling() {\n function hideOverlays() {\n hideOverlay(state.hoverOverlay);\n hideOverlay(state.selectionOverlay);\n }\n window.addEventListener(\"error\", function(event) {\n hideOverlays();\n postToStudio({\n action: \"runtimeError\",\n url: window.location.href,\n errors: [\n {\n type: \"error\",\n message: event.message,\n file: event.filename,\n line: event.lineno,\n column: event.colno\n }\n ]\n });\n });\n window.addEventListener(\"unhandledrejection\", function(event) {\n hideOverlays();\n const reason = event.reason;\n postToStudio({\n action: \"runtimeError\",\n url: window.location.href,\n errors: [\n {\n type: \"error\",\n message: reason instanceof Error ? reason.message : String(reason),\n file: reason instanceof Error ? reason.stack : void 0\n }\n ]\n });\n });\n}\n\n// src/studio/bridge/bridge-screenshot.ts\nfunction loadHtml2Canvas() {\n if (state.html2canvasLoaded) return Promise.resolve();\n if (state.html2canvasPromise) return state.html2canvasPromise;\n state.html2canvasPromise = new Promise((resolve, reject) => {\n const script = document.createElement(\"script\");\n script.src = \"https://cdn.jsdelivr.net/npm/html2canvas-pro@2.0.0/dist/html2canvas-pro.min.js\";\n script.onload = () => {\n state.html2canvasLoaded = true;\n resolve();\n };\n script.onerror = (event) => {\n logger.warn(\n \"Failed to load html2canvas script. This may be caused by CSP script-src restrictions.\",\n { event: String(event) }\n );\n reject(new Error(\"Failed to load html2canvas script\"));\n };\n try {\n document.head.appendChild(script);\n } catch (error) {\n logger.warn(\n \"Failed to append html2canvas script element. This may be caused by CSP script-src restrictions.\",\n error instanceof Error ? error : { error: String(error) }\n );\n reject(\n error instanceof Error ? error : new Error(\"Failed to append html2canvas script element\")\n );\n }\n });\n return state.html2canvasPromise;\n}\nasync function captureScreenshot(options) {\n const { scrollTo, fullPage, quality = 0.8 } = options || {};\n const originalScrollY = window.scrollY;\n try {\n await loadHtml2Canvas();\n if (typeof scrollTo === \"number\") {\n window.scrollTo(0, scrollTo);\n await new Promise((r) => setTimeout(r, 150));\n }\n const canvasOptions = {\n useCORS: true,\n logging: false,\n scale: window.devicePixelRatio || 1\n };\n if (fullPage) {\n canvasOptions.height = document.documentElement.scrollHeight;\n canvasOptions.windowHeight = document.documentElement.scrollHeight;\n canvasOptions.y = 0;\n window.scrollTo(0, 0);\n await new Promise((r) => setTimeout(r, 100));\n }\n const html2canvasFn = window.html2canvas.default || window.html2canvas;\n const canvas = await html2canvasFn(document.body, canvasOptions);\n if (!canvas || canvas.width === 0 || canvas.height === 0) {\n logger.error(\"html2canvas produced empty canvas\", {\n width: canvas?.width,\n height: canvas?.height\n });\n window.scrollTo(0, originalScrollY);\n return {\n success: false,\n error: \"html2canvas produced empty canvas (0x0 dimensions)\"\n };\n }\n const dataUrl = canvas.toDataURL(\"image/png\", quality);\n if (!dataUrl || !dataUrl.startsWith(\"data:image/\") || dataUrl.length < 100) {\n logger.error(\"html2canvas produced invalid data URL\", {\n dataUrlPreview: dataUrl?.substring(0, 50)\n });\n window.scrollTo(0, originalScrollY);\n return {\n success: false,\n error: \"html2canvas produced invalid image data\"\n };\n }\n window.scrollTo(0, originalScrollY);\n return {\n success: true,\n data: dataUrl,\n width: canvas.width,\n height: canvas.height,\n scrollY: window.scrollY,\n totalHeight: document.documentElement.scrollHeight,\n viewportHeight: window.innerHeight,\n url: window.location.href\n };\n } catch (error) {\n logger.error(\"html2canvas error\", error instanceof Error ? error : { error: String(error) });\n window.scrollTo(0, originalScrollY);\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error)\n };\n }\n}\nasync function captureMultipleSections(sectionCount) {\n const originalScrollY = window.scrollY;\n const results = [];\n const totalHeight = document.documentElement.scrollHeight;\n const viewportHeight = window.innerHeight;\n const sections = sectionCount || Math.ceil(totalHeight / viewportHeight);\n try {\n for (let i = 0; i < sections; i++) {\n const scrollY = Math.min(i * viewportHeight, totalHeight - viewportHeight);\n const result = await captureScreenshot({ scrollTo: scrollY });\n if (result.success) {\n results.push({ ...result, section: i + 1, totalSections: sections });\n }\n }\n } finally {\n window.scrollTo(0, originalScrollY);\n }\n return results;\n}\n\n// src/studio/bridge/bridge-message-handler.ts\nvar SAFE_PROTOCOLS = /* @__PURE__ */ new Set([\"http:\", \"https:\"]);\nfunction isSafeNavigationUrl(url) {\n if (url.startsWith(\"/\")) return true;\n try {\n const parsed = new URL(url, window.location.origin);\n return SAFE_PROTOCOLS.has(parsed.protocol);\n } catch {\n return false;\n }\n}\nfunction clearSelection(notifyStudio = false) {\n state.selectedNodeId = null;\n hideOverlay(state.selectionOverlay);\n if (notifyStudio) {\n postToStudio({ action: \"setSelectedNode\", id: null });\n }\n}\nfunction disableInspectMode(deselectElements = false) {\n state.inspectMode = false;\n hideOverlay(state.hoverOverlay);\n state.hoveredNodeId = null;\n if (deselectElements) {\n clearSelection();\n }\n}\nfunction postScreenshotResult(requestId, result) {\n postToStudio({\n action: \"screenshotResult\",\n requestId,\n multiple: false,\n ...result\n });\n}\nasync function handleScreenshotRequest(message) {\n if (message.multipleSections) {\n const results = await captureMultipleSections(message.sectionCount);\n postToStudio({\n action: \"screenshotResult\",\n requestId: message.requestId,\n multiple: true,\n results\n });\n return;\n }\n const result = await captureScreenshot(message.options);\n postScreenshotResult(message.requestId, result);\n}\nfunction handleStudioMessage(event) {\n if (!isFromStudio(event)) return;\n const message = event.data;\n if (!message?.action) return;\n const config3 = getConfig();\n switch (message.action) {\n case \"routeChange\":\n if (message.url) {\n if (!isSafeNavigationUrl(message.url)) {\n logger.warn(\"[StudioBridge] Blocked unsafe URL in routeChange\", { url: message.url });\n return;\n }\n if (state.selectedNodeId) {\n clearSelection(true);\n }\n postToStudio({\n action: \"onPageTransitionStart\",\n url: message.url,\n projectId: config3.projectId\n });\n window.location.href = message.url;\n }\n return;\n case \"reload\":\n window.location.reload();\n return;\n case \"goBack\":\n window.history.back();\n return;\n case \"goForward\":\n window.history.forward();\n return;\n case \"colorMode\":\n setColorMode(message.value);\n return;\n case \"toggleInspectMode\":\n state.inspectMode = message.value;\n if (state.inspectMode) return;\n disableInspectMode(message.deselectElements);\n return;\n case \"setSelectedNode\":\n state.selectedNodeId = message.id;\n showSelectionOverlay(message.id);\n if (message.scroll) scrollToElement(message.id);\n return;\n case \"setHoveredNode\":\n if (!state.inspectMode) showHoverOverlay(message.id);\n return;\n case \"screenshot\":\n void handleScreenshotRequest(message);\n return;\n default:\n logger.debug(\"Unknown action\", { action: message.action });\n return;\n }\n}\n\n// src/studio/bridge/bridge-init.ts\nfunction notifyAppLoaded() {\n const config3 = getConfig();\n postToStudio({ action: \"appLoaded\", url: window.location.href });\n postToStudio({\n action: \"appUpdated\",\n url: window.location.href,\n id: config3.pageId,\n isInitialLoad: true,\n errors: [],\n warnings: []\n });\n postToStudio({\n action: \"onPageTransitionEnd\",\n url: window.location.href,\n projectId: config3.projectId,\n id: config3.pageId,\n params: {}\n });\n}\nfunction notifyAppUnloaded() {\n postToStudio({ action: \"appUnloaded\", url: window.location.href });\n}\nfunction init() {\n const config3 = getConfig();\n const params = new URLSearchParams(window.location.search);\n const studioEmbed = params.get(\"studio_embed\") === \"true\";\n const isStandalone = window.parent === window && !studioEmbed;\n if (isStandalone) {\n logger.debug(\n \"[StudioBridge] Not in iframe and not studio_embed mode, skipping initialization\"\n );\n return;\n }\n logger.debug(\"Initializing...\");\n if (!isStandalone) {\n injectOverlayStyles();\n state.hoverOverlay = createOverlay(\"hover\");\n state.selectionOverlay = createOverlay(\"selection\");\n setupConsoleCapture();\n setupErrorHandling();\n setupInspectMode();\n }\n window.addEventListener(\"message\", handleStudioMessage);\n if (!isStandalone) {\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", function() {\n notifyAppLoaded();\n setupMutationObserver();\n }, { once: true });\n } else {\n notifyAppLoaded();\n setupMutationObserver();\n }\n window.addEventListener(\"beforeunload\", notifyAppUnloaded, { once: true });\n }\n const colorMode = params.get(\"color_mode\");\n if (colorMode) setColorMode(colorMode);\n if (!isStandalone) {\n const inspectModeParam = params.get(\"inspect_mode\");\n if (inspectModeParam === \"true\") {\n state.inspectMode = true;\n logger.debug(\"Inspect mode enabled from query param\");\n }\n }\n logger.debug(\"Initialized successfully\");\n}\n\n// src/studio/bridge/bridge-coordinator.ts\ninitConfig();\nvar config2 = getConfig();\nif (!config2.debugSkipInit) {\n init();\n}\n";
8
+ export const STUDIO_BRIDGE_BUNDLE = "// src/studio/bridge/bridge-logger.ts\nfunction normalizeContext(context) {\n if (!context) return void 0;\n if (context instanceof Error) {\n return { error: context.message, ...context.stack ? { stack: context.stack } : {} };\n }\n return context;\n}\nfunction formatArgs(message, context) {\n const normalized = normalizeContext(context);\n if (!normalized || Object.keys(normalized).length === 0) {\n return [message];\n }\n return [message, normalized];\n}\nvar logger = {\n debug(message, context) {\n console.debug(...formatArgs(message, context));\n },\n info(message, context) {\n console.log(...formatArgs(message, context));\n },\n warn(message, context) {\n console.warn(...formatArgs(message, context));\n },\n error(message, context) {\n console.error(...formatArgs(message, context));\n }\n};\n\n// src/studio/bridge/bridge-config.ts\nvar config = null;\nvar DEFAULT_CONFIG = {\n projectId: \"\",\n pageId: \"\",\n pagePath: \"\",\n wsUrl: \"\",\n yjsGuid: \"\",\n studioMode: \"advanced\",\n debugSkipInit: false,\n debugExposeInternals: false\n};\nfunction resolveStudioMode(value, queryString) {\n const params = new URLSearchParams(queryString);\n return value === \"simple\" || params.get(\"vf_studio_mode\") === \"simple\" ? \"simple\" : \"advanced\";\n}\nfunction normalizeConfig(raw) {\n const queryString = window.location.search;\n if (!raw || typeof raw !== \"object\") {\n logger.warn(\"No bridge config found on window.__VF_BRIDGE_CONFIG__\");\n return {\n ...DEFAULT_CONFIG,\n studioMode: resolveStudioMode(void 0, queryString)\n };\n }\n return {\n ...DEFAULT_CONFIG,\n projectId: String(raw.projectId ?? \"\"),\n pageId: String(raw.pageId ?? \"\"),\n pagePath: String(raw.pagePath ?? raw.pageId ?? \"\"),\n wsUrl: String(raw.wsUrl ?? \"\"),\n yjsGuid: String(raw.yjsGuid ?? \"\"),\n studioMode: resolveStudioMode(raw.studioMode, queryString),\n debugSkipInit: !!raw.debugSkipInit,\n debugExposeInternals: !!raw.debugExposeInternals\n };\n}\nfunction initConfig() {\n const raw = globalThis.__VF_BRIDGE_CONFIG__;\n config = normalizeConfig(raw);\n}\nfunction getConfig() {\n if (!config) {\n throw new Error(\"[StudioBridge] Config not initialized. Call initConfig() first.\");\n }\n return config;\n}\n\n// src/studio/bridge/bridge-state.ts\nvar state = {\n // Inspector\n inspectMode: false,\n selectedNodeId: null,\n hoveredNodeId: null,\n lastTreeSignature: \"\",\n // Overlays\n hoverOverlay: null,\n selectionOverlay: null,\n // Console\n originalConsole: {},\n logCounter: 0,\n // Screenshot\n html2canvasLoaded: false,\n html2canvasPromise: null\n};\nvar CONSOLE_METHODS = [\n \"log\",\n \"debug\",\n \"info\",\n \"warn\",\n \"error\",\n \"table\",\n \"clear\",\n \"dir\"\n];\nvar DOM_IGNORE_TAGS = [\"SCRIPT\", \"STYLE\", \"LINK\", \"META\", \"NOSCRIPT\"];\n\n// src/studio/bridge/bridge-messaging.ts\nvar studioOrigin = null;\nfunction postToStudio(message) {\n if (!window.parent || window.parent === window) return;\n try {\n window.parent.postMessage(message, studioOrigin || \"*\");\n } catch (e) {\n logger.debug(\"postMessage failed\", e instanceof Error ? e : { error: String(e) });\n }\n}\nfunction isFromStudio(event) {\n try {\n if (!event.source || event.source === window) return false;\n const url = new URL(event.origin || \"\");\n const host = url.hostname;\n const valid = host === \"localhost\" || host.endsWith(\".veryfront.org\") || host === \"veryfront.org\" || host.endsWith(\".veryfront.com\") || host === \"veryfront.com\" || host.endsWith(\".veryfront.dev\") || host === \"veryfront.dev\";\n if (valid && !studioOrigin) {\n studioOrigin = event.origin;\n }\n return valid;\n } catch (_) {\n return false;\n }\n}\n\n// src/studio/bridge/bridge-styles.ts\nvar OVERLAY_CSS = `\n /* ------------------------------------------------------------------ */\n /* Overlays (hover / selection inspector) */\n /* ------------------------------------------------------------------ */\n\n .vf-overlay {\n position: fixed;\n pointer-events: none;\n z-index: 99999;\n box-sizing: border-box;\n transition: all 0.05s ease-out;\n }\n .vf-overlay-hover {\n border: 2px solid oklch(0.6852 0.162 241.8);\n background: oklch(0.6852 0.162 241.8 / 0.06);\n }\n .vf-overlay-selection {\n border: 2px solid oklch(0.6852 0.162 241.8);\n background: oklch(0.6852 0.162 241.8 / 0.1);\n }\n .vf-overlay-label {\n position: absolute;\n top: -22px;\n left: -2px;\n background: oklch(0.6852 0.162 241.8);\n color: white;\n font-size: 11px;\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n padding: 2px 6px;\n border-radius: 3px 3px 0 0;\n white-space: nowrap;\n pointer-events: none;\n }\n .vf-overlay-label-bottom {\n top: auto;\n bottom: -22px;\n border-radius: 0 0 3px 3px;\n }\n\n /* ------------------------------------------------------------------ */\n /* Edit button (floating CTA) */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-edit-button {\n position: fixed;\n right: 16px;\n bottom: 16px;\n z-index: 100001;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 9999px;\n background: oklch(0.2768 0 0);\n color: oklch(0.9512 0.008 98.88);\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 13px;\n font-weight: 500;\n line-height: 1;\n padding: 10px 16px;\n cursor: pointer;\n box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1);\n transition: transform 100ms ease, box-shadow 100ms ease;\n }\n .vf-markdown-edit-button:hover {\n box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1);\n }\n .vf-markdown-edit-button:active {\n transform: scale(0.98);\n }\n\n /* ------------------------------------------------------------------ */\n /* Editor root */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor {\n position: fixed;\n inset: 0;\n z-index: 100000;\n background: oklch(1 0 0);\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n display: none;\n }\n\n .vf-markdown-editor__history {\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 6px;\n background: oklch(1 0 0);\n color: oklch(0.2768 0 0);\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 13px;\n line-height: 1;\n min-width: 28px;\n height: 28px;\n padding: 0 8px;\n cursor: pointer;\n transition: background 75ms ease;\n }\n .vf-markdown-editor__history:hover {\n background: oklch(0.93 0 0);\n }\n\n /* ------------------------------------------------------------------ */\n /* Surface (editor area) */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__surface-wrap {\n position: relative;\n height: 100vh;\n }\n\n .vf-markdown-editor__surface {\n width: 100%;\n max-width: 980px;\n margin: 0 auto;\n height: 100%;\n overflow: auto;\n outline: none;\n position: relative;\n z-index: 1;\n background: transparent;\n padding: 32px 40px;\n box-sizing: border-box;\n }\n\n /* ------------------------------------------------------------------ */\n /* Selection overlay */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__selection-overlay {\n position: absolute;\n inset: 0;\n pointer-events: none;\n z-index: 2;\n display: none;\n }\n\n .vf-markdown-editor__selection-highlight {\n position: absolute;\n border-radius: 3px;\n opacity: 0.26;\n }\n\n .vf-markdown-editor__selection-caret {\n position: absolute;\n width: 2px;\n border-radius: 1px;\n }\n\n .vf-markdown-editor__selection-label {\n position: absolute;\n transform: translateY(-100%);\n margin-top: -4px;\n border-radius: 9999px;\n padding: 1px 7px;\n font-size: 10px;\n line-height: 1.4;\n white-space: nowrap;\n color: oklch(1 0 0);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.16);\n }\n\n /* ------------------------------------------------------------------ */\n /* Slash menu */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__slash-menu {\n position: fixed;\n z-index: 100005;\n min-width: 240px;\n max-width: 300px;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 8px;\n background: oklch(1 0 0);\n box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1);\n padding: 4px;\n display: none;\n }\n\n .vf-markdown-editor__slash-section {\n padding: 8px 10px 4px;\n font-size: 11px;\n font-weight: 600;\n color: oklch(0.55 0.005 95.11);\n text-transform: none;\n letter-spacing: 0;\n }\n\n .vf-markdown-editor__slash-item {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n border: 0;\n border-radius: 6px;\n background: transparent;\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n text-align: left;\n padding: 6px 10px;\n cursor: pointer;\n transition: background 75ms ease;\n }\n\n .vf-markdown-editor__slash-item:hover,\n .vf-markdown-editor__slash-item[data-active='true'] {\n background: oklch(0.93 0 0);\n }\n\n .vf-markdown-editor__slash-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 4px;\n background: oklch(1 0 0);\n font-size: 13px;\n font-weight: 600;\n color: oklch(0.2768 0 0);\n flex-shrink: 0;\n }\n\n .vf-markdown-editor__slash-item-title {\n display: block;\n font-size: 13px;\n font-weight: 500;\n color: oklch(0.2768 0 0);\n line-height: 1.35;\n flex: 1;\n }\n\n .vf-markdown-editor__slash-item-desc {\n display: none;\n }\n\n .vf-markdown-editor__slash-shortcut {\n font-size: 11px;\n color: oklch(0.55 0.005 95.11);\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n flex-shrink: 0;\n }\n\n .vf-markdown-editor__slash-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 10px;\n margin-top: 2px;\n border-top: 1px solid oklch(0.9 0 0);\n font-size: 11px;\n color: oklch(0.55 0.005 95.11);\n }\n\n .vf-markdown-editor__slash-footer-key {\n font-size: 10px;\n color: oklch(0.55 0.005 95.11);\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 3px;\n padding: 1px 4px;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n }\n\n /* ------------------------------------------------------------------ */\n /* Inline toolbar */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__inline-toolbar {\n position: fixed;\n z-index: 100006;\n display: none;\n flex-direction: column;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 8px;\n background: oklch(1 0 0);\n box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1);\n padding: 4px;\n }\n\n .vf-markdown-editor__inline-row {\n display: flex;\n align-items: center;\n gap: 2px;\n padding: 2px 0;\n }\n\n .vf-markdown-editor__inline-separator {\n width: 1px;\n height: 20px;\n background: oklch(0.9 0 0);\n margin: 0 2px;\n flex-shrink: 0;\n }\n\n .vf-markdown-editor__inline-button {\n border: 0;\n border-radius: 6px;\n background: transparent;\n color: oklch(0.2768 0 0);\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n min-width: 26px;\n height: 26px;\n padding: 0 7px;\n cursor: pointer;\n transition: background 75ms ease;\n }\n\n .vf-markdown-editor__inline-button:hover {\n background: oklch(0.93 0 0);\n }\n\n .vf-markdown-editor__inline-button.active {\n background: oklch(0.6852 0.162 241.8 / 0.14);\n color: oklch(0.6852 0.162 241.8);\n }\n\n .vf-markdown-editor__inline-button[data-format='bold'] {\n font-weight: 700;\n }\n\n .vf-markdown-editor__inline-button[data-format='italic'] {\n font-style: italic;\n }\n\n .vf-markdown-editor__inline-button[data-format='strikethrough'] {\n text-decoration: line-through;\n }\n\n /* ------------------------------------------------------------------ */\n /* Block type trigger */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__block-trigger {\n border: 0;\n border-radius: 6px;\n background: transparent;\n color: oklch(0.2768 0 0);\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n height: 26px;\n padding: 0 7px;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 2px;\n white-space: nowrap;\n transition: background 75ms ease;\n }\n\n .vf-markdown-editor__block-trigger:hover {\n background: oklch(0.93 0 0);\n }\n\n .vf-markdown-editor__block-trigger::after {\n content: '\\\\25BE';\n font-size: 10px;\n color: oklch(0.55 0.005 95.11);\n }\n\n /* ------------------------------------------------------------------ */\n /* Block dropdown */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__block-dropdown {\n position: absolute;\n top: 100%;\n left: 4px;\n z-index: 100007;\n min-width: 160px;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 6px;\n background: oklch(1 0 0);\n box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1);\n padding: 4px;\n margin-top: 4px;\n display: none;\n }\n\n .vf-markdown-editor__block-option {\n display: block;\n width: 100%;\n border: 0;\n border-radius: 6px;\n background: transparent;\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n text-align: left;\n padding: 6px 10px;\n font-size: 13px;\n font-weight: 500;\n color: oklch(0.2768 0 0);\n cursor: pointer;\n transition: background 75ms ease;\n }\n\n .vf-markdown-editor__block-option:hover {\n background: oklch(0.93 0 0);\n }\n\n .vf-markdown-editor__block-option.active {\n background: oklch(0.6852 0.162 241.8 / 0.1);\n color: oklch(0.6852 0.162 241.8);\n }\n\n /* ------------------------------------------------------------------ */\n /* Block drag handle */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__block-handle {\n position: fixed;\n z-index: 100007;\n display: none;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 6px;\n background: oklch(1 0 0);\n color: oklch(0.2768 0 0);\n font-size: 12px;\n font-weight: 700;\n line-height: 1;\n width: 28px;\n height: 28px;\n padding: 0;\n cursor: grab;\n box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px -1px rgba(0,0,0,0.1);\n transition: background 75ms ease, box-shadow 75ms ease;\n }\n\n .vf-markdown-editor__block-handle:hover {\n background: oklch(0.6852 0.162 241.8 / 0.1);\n }\n\n .vf-markdown-editor__block-handle[data-dragging='true'] {\n cursor: grabbing;\n }\n\n /* ------------------------------------------------------------------ */\n /* Block drop indicator */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__block-drop-indicator {\n position: fixed;\n z-index: 100006;\n display: none;\n height: 2px;\n border-radius: 9999px;\n background: oklch(0.6852 0.162 241.8);\n box-shadow: 0 1px 6px oklch(0.6852 0.162 241.8 / 0.5);\n }\n\n .vf-markdown-editor__block-drop-label {\n position: fixed;\n z-index: 100007;\n display: none;\n border-radius: 9999px;\n border: 1px solid oklch(0.6852 0.162 241.8 / 0.24);\n background: oklch(1 0 0 / 0.96);\n color: oklch(0.2768 0 0);\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 11px;\n font-weight: 600;\n line-height: 1.2;\n padding: 3px 8px;\n box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px -1px rgba(0,0,0,0.1);\n }\n\n /* ------------------------------------------------------------------ */\n /* Block drag ghost */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__block-drag-ghost {\n position: fixed;\n top: -9999px;\n left: -9999px;\n width: 260px;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 8px;\n background: oklch(1 0 0);\n box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1);\n padding: 8px 10px;\n }\n\n .vf-markdown-editor__block-drag-ghost-title {\n display: block;\n font-size: 11px;\n font-weight: 700;\n color: oklch(0.2768 0 0);\n }\n\n .vf-markdown-editor__block-drag-ghost-text {\n display: block;\n margin-top: 4px;\n font-size: 11px;\n color: oklch(0.55 0.005 95.11);\n line-height: 1.35;\n }\n\n /* ------------------------------------------------------------------ */\n /* MDX blocks bar */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__mdx-blocks {\n display: none;\n gap: 8px;\n padding: 8px 16px;\n border-bottom: 1px solid oklch(0.9 0 0);\n background: oklch(0.97 0 0 / 0.95);\n overflow-x: auto;\n }\n\n .vf-markdown-editor__mdx-block {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 6px;\n background: oklch(1 0 0);\n padding: 6px 8px;\n }\n\n .vf-markdown-editor__mdx-block-label {\n font-size: 11px;\n color: oklch(0.2768 0 0);\n white-space: nowrap;\n }\n\n .vf-markdown-editor__mdx-note {\n font-size: 10px;\n color: oklch(0.55 0.005 95.11);\n white-space: nowrap;\n }\n\n .vf-markdown-editor__mdx-open {\n border: 1px solid oklch(0.84 0.0055 95.11);\n border-radius: 6px;\n background: oklch(1 0 0);\n color: oklch(0.2768 0 0);\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;\n font-size: 11px;\n line-height: 1;\n padding: 5px 7px;\n cursor: pointer;\n white-space: nowrap;\n transition: background 75ms ease;\n }\n .vf-markdown-editor__mdx-open:hover {\n background: oklch(0.93 0 0);\n }\n\n /* ------------------------------------------------------------------ */\n /* Lexical surface overrides */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__surface [data-lexical-editor] {\n outline: none;\n }\n\n .vf-markdown-editor__surface s,\n .vf-markdown-editor__surface del,\n .vf-markdown-editor__surface [style*='line-through'] {\n text-decoration: line-through;\n }\n\n .vf-markdown-editor__surface p:empty::before {\n content: \"Type '/' for commands\";\n color: oklch(0.55 0.005 95.11 / 0.6);\n pointer-events: none;\n font-style: normal;\n }\n\n .vf-markdown-editor__surface h1:empty::before {\n content: 'Heading 1';\n color: oklch(0.55 0.005 95.11 / 0.6);\n pointer-events: none;\n }\n\n .vf-markdown-editor__surface h2:empty::before {\n content: 'Heading 2';\n color: oklch(0.55 0.005 95.11 / 0.6);\n pointer-events: none;\n }\n\n .vf-markdown-editor__surface h3:empty::before {\n content: 'Heading 3';\n color: oklch(0.55 0.005 95.11 / 0.6);\n pointer-events: none;\n }\n\n .vf-markdown-editor__surface blockquote:empty::before {\n content: 'Quote';\n color: oklch(0.55 0.005 95.11 / 0.6);\n pointer-events: none;\n }\n\n .vf-markdown-editor__surface p {\n min-height: 1.5em;\n }\n\n /* ------------------------------------------------------------------ */\n /* Textarea fallback */\n /* ------------------------------------------------------------------ */\n\n .vf-markdown-editor__textarea {\n width: 100%;\n height: 100vh;\n border: 0;\n outline: none;\n resize: none;\n display: none;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;\n font-size: 14px;\n line-height: 1.6;\n color: oklch(0.2768 0 0);\n background: transparent;\n padding: 16px;\n box-sizing: border-box;\n }\n\n /* ================================================================== */\n /* DARK MODE */\n /* ================================================================== */\n\n [data-theme='dark'] .vf-markdown-editor {\n background: oklch(0.2768 0 0);\n }\n\n [data-theme='dark'] .vf-markdown-editor__history {\n background: oklch(0.3211 0 0);\n border-color: oklch(0.42 0.0017 106.48);\n color: oklch(0.9512 0.008 98.88);\n }\n [data-theme='dark'] .vf-markdown-editor__history:hover {\n background: oklch(0.25 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__textarea {\n color: oklch(0.9512 0.008 98.88);\n }\n\n /* Slash menu \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__slash-menu {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.21 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-section {\n color: oklch(0.5338 0.0046 106.55);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-item:hover,\n [data-theme='dark'] .vf-markdown-editor__slash-item[data-active='true'] {\n background: oklch(0.25 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-icon {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.3211 0 0);\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-item-title {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-shortcut {\n color: oklch(0.5338 0.0046 106.55);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-footer {\n border-top-color: oklch(0.3 0.01 220);\n color: oklch(0.5338 0.0046 106.55);\n }\n\n [data-theme='dark'] .vf-markdown-editor__slash-footer-key {\n border-color: oklch(0.42 0.0017 106.48);\n color: oklch(0.5338 0.0046 106.55);\n }\n\n /* Inline toolbar \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__inline-toolbar {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.21 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__inline-separator {\n background: oklch(0.3 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__inline-button {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__inline-button:hover {\n background: oklch(0.25 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__inline-button.active {\n background: oklch(0.6852 0.162 241.8 / 0.2);\n color: oklch(0.75 0.14 241.8);\n }\n\n /* Block trigger \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__block-trigger {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-trigger:hover {\n background: oklch(0.25 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-trigger::after {\n color: oklch(0.5338 0.0046 106.55);\n }\n\n /* Block dropdown \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__block-dropdown {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.21 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-option {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-option:hover {\n background: oklch(0.25 0.01 220);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-option.active {\n background: oklch(0.6852 0.162 241.8 / 0.2);\n color: oklch(0.75 0.14 241.8);\n }\n\n /* Placeholder text \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__surface p:empty::before,\n [data-theme='dark'] .vf-markdown-editor__surface h1:empty::before,\n [data-theme='dark'] .vf-markdown-editor__surface h2:empty::before,\n [data-theme='dark'] .vf-markdown-editor__surface h3:empty::before,\n [data-theme='dark'] .vf-markdown-editor__surface blockquote:empty::before {\n color: oklch(0.5338 0.0046 106.55 / 0.5);\n }\n\n /* Block drag \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__block-handle {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.3211 0 0);\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-handle:hover {\n background: oklch(0.6852 0.162 241.8 / 0.2);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-drop-label {\n border-color: oklch(0.6852 0.162 241.8 / 0.35);\n background: oklch(0.18 0.01 220 / 0.96);\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-drag-ghost {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.3211 0 0);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-drag-ghost-title {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__block-drag-ghost-text {\n color: oklch(0.5338 0.0046 106.55);\n }\n\n /* MDX blocks \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-editor__mdx-blocks {\n border-bottom-color: oklch(0.3 0.01 220);\n background: oklch(0.18 0.01 220 / 0.8);\n }\n\n [data-theme='dark'] .vf-markdown-editor__mdx-block {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.3211 0 0);\n }\n\n [data-theme='dark'] .vf-markdown-editor__mdx-block-label {\n color: oklch(0.9512 0.008 98.88);\n }\n\n [data-theme='dark'] .vf-markdown-editor__mdx-note {\n color: oklch(0.5338 0.0046 106.55);\n }\n\n [data-theme='dark'] .vf-markdown-editor__mdx-open {\n border-color: oklch(0.42 0.0017 106.48);\n background: oklch(0.2768 0 0);\n color: oklch(0.9512 0.008 98.88);\n }\n [data-theme='dark'] .vf-markdown-editor__mdx-open:hover {\n background: oklch(0.25 0.01 220);\n }\n\n /* Edit button \\u2013 dark */\n\n [data-theme='dark'] .vf-markdown-edit-button {\n background: oklch(0.9512 0.008 98.88);\n color: oklch(0.2768 0 0);\n border-color: oklch(0.42 0.0017 106.48);\n }\n`;\nfunction injectOverlayStyles() {\n if (document.getElementById(\"vf-overlay-styles\")) return;\n const style = document.createElement(\"style\");\n style.id = \"vf-overlay-styles\";\n style.textContent = OVERLAY_CSS;\n try {\n document.head.appendChild(style);\n if (!style.sheet) {\n logger.warn(\"Inline style injection may be blocked by CSP (style-src)\");\n }\n } catch (error) {\n logger.warn(\n \"Failed to inject bridge styles. This may be caused by CSP style-src restrictions.\",\n error instanceof Error ? error : {\n error: String(error)\n }\n );\n }\n}\n\n// src/studio/bridge/bridge-constants.ts\nvar DATA_VF_ID = \"data-vf-id\";\nvar DATA_VF_SELECTOR = \"data-vf-selector\";\nvar DATA_VF_TEXT = \"data-vf-text\";\nvar DATA_VF_IGNORE = \"data-vf-ignore\";\nvar DATA_NODE_ID = \"data-node-id\";\nvar DATA_NODE_FILE = \"data-node-file\";\nvar DATA_NODE_NAME = \"data-node-name\";\nvar DATA_NODE_LINE = \"data-node-line\";\nvar DATA_NODE_COLUMN = \"data-node-column\";\nvar DATA_NODE_SOURCE = \"data-node-source\";\n\n// src/studio/bridge/bridge-utils.ts\nfunction debounce(fn, ms) {\n let timer;\n const debounced = function(...args) {\n clearTimeout(timer);\n timer = setTimeout(() => {\n fn.apply(this, args);\n }, ms);\n };\n debounced.cancel = () => {\n clearTimeout(timer);\n timer = void 0;\n };\n return debounced;\n}\n\n// src/studio/bridge/bridge-inspector.ts\nfunction createOverlay(type) {\n const overlay = document.createElement(\"div\");\n overlay.className = \"vf-overlay vf-overlay-\" + type;\n overlay.setAttribute(DATA_VF_IGNORE, \"true\");\n const label = document.createElement(\"div\");\n label.className = \"vf-overlay-label\";\n overlay.appendChild(label);\n overlay.style.display = \"none\";\n document.body.appendChild(overlay);\n return overlay;\n}\nfunction hideOverlay(overlay) {\n if (overlay) overlay.style.display = \"none\";\n}\nfunction positionOverlay(overlay, element, nodeName) {\n if (!overlay) return;\n if (!element) {\n hideOverlay(overlay);\n return;\n }\n const rect = element.getBoundingClientRect();\n overlay.style.display = \"block\";\n overlay.style.top = rect.top + \"px\";\n overlay.style.left = rect.left + \"px\";\n overlay.style.width = rect.width + \"px\";\n overlay.style.height = rect.height + \"px\";\n const label = overlay.querySelector(\".vf-overlay-label\");\n if (label) {\n label.textContent = nodeName;\n if (rect.top < 24) {\n label.classList.add(\"vf-overlay-label-bottom\");\n } else {\n label.classList.remove(\"vf-overlay-label-bottom\");\n }\n }\n}\nfunction getNodeName(element) {\n const vfId = element.getAttribute(DATA_VF_ID);\n if (vfId) return vfId.split(\"_\")[0] ?? vfId;\n return element.tagName.toLowerCase();\n}\nfunction findElementById(nodeId) {\n if (!nodeId) return null;\n return document.querySelector(\"[\" + DATA_VF_ID + '=\"' + nodeId + '\"]') || document.querySelector(\"[\" + DATA_VF_SELECTOR + '=\"' + nodeId + '\"]') || document.querySelector(\"[\" + DATA_NODE_ID + '=\"' + nodeId + '\"]');\n}\nfunction isValidElement(el) {\n return !!el && el.nodeType === Node.ELEMENT_NODE && !DOM_IGNORE_TAGS.includes(el.tagName) && !el.hasAttribute(DATA_VF_IGNORE) && el.style.display !== \"none\";\n}\nfunction getNodeType(el) {\n const vfId = el.getAttribute(DATA_VF_ID) || \"\";\n if (vfId && /^[A-Z]/.test(vfId)) return \"component\";\n if (el.hasAttribute(DATA_VF_TEXT)) return \"text\";\n if (el.getAttribute(DATA_NODE_SOURCE) === \"md\") return \"markdown\";\n return \"element\";\n}\nfunction buildNavigatorTree(root) {\n const config3 = getConfig();\n let nodeIndex = 0;\n function processElement(el, parentId) {\n if (!isValidElement(el)) {\n const children = [];\n Array.from(el.children || []).forEach((child) => {\n children.push(...processElement(child, parentId));\n });\n return children;\n }\n let id = el.getAttribute(DATA_VF_ID) || el.getAttribute(DATA_NODE_ID) || el.getAttribute(DATA_VF_SELECTOR);\n if (!id) {\n id = \"vf-\" + el.tagName.toLowerCase() + \"-\" + ++nodeIndex;\n el.setAttribute(DATA_VF_SELECTOR, id);\n }\n const vfId = el.getAttribute(DATA_VF_ID);\n const name = vfId ? vfId.split(\"_\")[0] ?? vfId : el.tagName.toLowerCase();\n const node = {\n id,\n name,\n type: getNodeType(el),\n path: config3.pagePath,\n parentId,\n start: {\n line: parseInt(el.getAttribute(DATA_NODE_LINE) || \"0\", 10),\n column: parseInt(el.getAttribute(DATA_NODE_COLUMN) || \"0\", 10)\n },\n end: { line: 0, column: 0 },\n children: [],\n text: el.hasAttribute(DATA_VF_TEXT) ? el.textContent?.trim() : void 0,\n isRemote: false\n };\n Array.from(el.children || []).forEach((child) => {\n node.children.push(...processElement(child, id));\n });\n return [node];\n }\n const rootNode = {\n id: \"root\",\n name: \"root\",\n type: \"root\",\n path: \"\",\n parentId: \"\",\n start: { line: 0, column: 0 },\n end: { line: 0, column: 0 },\n children: [],\n isRemote: false\n };\n Array.from(root.children || []).forEach((child) => {\n rootNode.children.push(...processElement(child, \"root\"));\n });\n return rootNode;\n}\nfunction createTreeSignature(root) {\n const validElements = Array.from(root.querySelectorAll(\"*\")).filter((el) => isValidElement(el));\n return validElements.length + \"-\" + validElements.map((el) => el.tagName).join(\"\");\n}\nvar treeUpdateTimer = null;\nvar mutationObserver = null;\nfunction sendTreeUpdate() {\n const config3 = getConfig();\n const root = document.getElementById(\"root\") || document.body;\n if (!root) return;\n const signature = createTreeSignature(root);\n if (signature === state.lastTreeSignature) return;\n state.lastTreeSignature = signature;\n postToStudio({\n action: \"treeUpdated\",\n id: config3.pageId,\n url: window.location.href,\n tree: buildNavigatorTree(root),\n sourceHash: globalThis.__VERYFRONT_SOURCE_HASH__ || null\n });\n}\nfunction debouncedTreeUpdate() {\n if (treeUpdateTimer) clearTimeout(treeUpdateTimer);\n treeUpdateTimer = setTimeout(sendTreeUpdate, 150);\n}\nfunction setupMutationObserver() {\n const root = document.getElementById(\"root\") || document.body;\n if (!root) return;\n mutationObserver = new MutationObserver(function(mutations) {\n const hasRelevantChanges = mutations.some(\n (m) => m.type === \"childList\" || m.type === \"characterData\"\n );\n if (!hasRelevantChanges) return;\n if (state.selectedNodeId && !findElementById(state.selectedNodeId)) {\n state.selectedNodeId = null;\n hideOverlay(state.selectionOverlay);\n postToStudio({ action: \"setSelectedNode\", id: null });\n }\n debouncedTreeUpdate();\n });\n mutationObserver.observe(root, { childList: true, characterData: true, subtree: true });\n sendTreeUpdate();\n}\nfunction showOverlay(overlay, nodeId) {\n if (!nodeId) {\n hideOverlay(overlay);\n return;\n }\n const el = findElementById(nodeId);\n if (!el) {\n hideOverlay(overlay);\n return;\n }\n positionOverlay(overlay, el, getNodeName(el));\n}\nfunction showHoverOverlay(nodeId) {\n showOverlay(state.hoverOverlay, nodeId);\n}\nfunction showSelectionOverlay(nodeId) {\n showOverlay(state.selectionOverlay, nodeId);\n}\nfunction scrollToElement(nodeId) {\n const el = document.querySelector(\"[\" + DATA_VF_ID + '=\"' + nodeId + '\"]') || document.querySelector(\"[\" + DATA_NODE_ID + '=\"' + nodeId + '\"]') || document.querySelector(\"[\" + DATA_VF_SELECTOR + '*=\"' + nodeId + '\"]');\n if (el) el.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n}\nfunction getDirectText(el) {\n let text = \"\";\n for (let i = 0; i < el.childNodes.length; i++) {\n const node = el.childNodes[i];\n if (node?.nodeType === Node.TEXT_NODE) {\n text += node.textContent || \"\";\n }\n }\n return text.trim();\n}\nfunction setupInspectMode() {\n const INSPECTABLE_SELECTOR = \"[\" + DATA_VF_ID + \"], [\" + DATA_VF_SELECTOR + \"], [\" + DATA_NODE_ID + \"], [\" + DATA_NODE_FILE + \"]\";\n function getElementId(el) {\n return el.getAttribute(DATA_VF_ID) || el.getAttribute(DATA_NODE_ID) || el.getAttribute(DATA_VF_SELECTOR);\n }\n document.addEventListener(\n \"click\",\n function(event) {\n if (!state.inspectMode) return;\n event.preventDefault();\n event.stopPropagation();\n const target = event.target.closest(INSPECTABLE_SELECTOR);\n if (!target) {\n state.selectedNodeId = null;\n hideOverlay(state.selectionOverlay);\n postToStudio({ action: \"setSelectedNode\", id: null });\n return;\n }\n const id = getElementId(target);\n state.selectedNodeId = id;\n showSelectionOverlay(id);\n postToStudio({\n action: \"setSelectedNode\",\n id,\n node: {\n name: target.getAttribute(DATA_NODE_NAME) || target.tagName.toLowerCase(),\n type: getNodeType(target),\n file: target.getAttribute(DATA_NODE_FILE) || getConfig().pagePath,\n line: parseInt(target.getAttribute(DATA_NODE_LINE) || \"0\", 10),\n column: parseInt(target.getAttribute(DATA_NODE_COLUMN) || \"0\", 10),\n text: getDirectText(target).slice(0, 200)\n }\n });\n },\n true\n );\n document.addEventListener(\"pointerover\", function(event) {\n if (!state.inspectMode || event.pointerType === \"touch\") return;\n const target = event.target.closest(INSPECTABLE_SELECTOR);\n if (!target) return;\n const id = getElementId(target);\n if (id === state.hoveredNodeId) return;\n state.hoveredNodeId = id;\n showHoverOverlay(id);\n });\n document.addEventListener(\"pointerout\", function(event) {\n if (!state.inspectMode || event.pointerType === \"touch\") return;\n const target = event.target.closest(INSPECTABLE_SELECTOR);\n if (!target) return;\n const relatedTarget = event.relatedTarget;\n if (relatedTarget && target.contains(relatedTarget)) return;\n state.hoveredNodeId = null;\n hideOverlay(state.hoverOverlay);\n });\n const updateOverlays = debounce(function() {\n if (state.inspectMode && state.hoveredNodeId) showHoverOverlay(state.hoveredNodeId);\n if (state.selectedNodeId) showSelectionOverlay(state.selectedNodeId);\n }, 16);\n window.addEventListener(\"scroll\", updateOverlays, true);\n window.addEventListener(\"resize\", updateOverlays);\n}\nfunction setColorMode(mode) {\n document.documentElement.setAttribute(\"data-theme\", mode);\n document.documentElement.classList.remove(\"light\", \"dark\");\n document.documentElement.classList.add(mode);\n}\n\n// src/studio/bridge/bridge-console.ts\nfunction setupConsoleCapture() {\n const consoleObj = console;\n CONSOLE_METHODS.forEach((method) => {\n state.originalConsole[method] = consoleObj[method];\n consoleObj[method] = function(...args) {\n state.originalConsole[method].apply(console, args);\n const logId = \"vf-\" + Date.now() + \"-\" + ++state.logCounter;\n const formattedData = args.map((arg) => {\n try {\n if (arg instanceof Error) {\n return { __isError: true, message: arg.message, stack: arg.stack, name: arg.name };\n }\n if (arg === void 0) return { __isUndefined: true };\n if (arg === null) return null;\n if (typeof arg === \"function\") {\n return { __isFunction: true, name: arg.name || \"anonymous\" };\n }\n if (typeof arg === \"symbol\") return { __isSymbol: true, description: arg.description };\n if (typeof arg === \"object\") return JSON.parse(JSON.stringify(arg));\n return arg;\n } catch (_) {\n return String(arg);\n }\n });\n postToStudio({\n action: \"logEvent\",\n value: {\n id: logId,\n method,\n data: formattedData,\n timestamp: (/* @__PURE__ */ new Date()).toISOString()\n }\n });\n };\n });\n}\nfunction setupErrorHandling() {\n function hideOverlays() {\n hideOverlay(state.hoverOverlay);\n hideOverlay(state.selectionOverlay);\n }\n window.addEventListener(\"error\", function(event) {\n hideOverlays();\n postToStudio({\n action: \"runtimeError\",\n url: window.location.href,\n errors: [\n {\n type: \"error\",\n message: event.message,\n file: event.filename,\n line: event.lineno,\n column: event.colno\n }\n ]\n });\n });\n window.addEventListener(\"unhandledrejection\", function(event) {\n hideOverlays();\n const reason = event.reason;\n postToStudio({\n action: \"runtimeError\",\n url: window.location.href,\n errors: [\n {\n type: \"error\",\n message: reason instanceof Error ? reason.message : String(reason),\n file: reason instanceof Error ? reason.stack : void 0\n }\n ]\n });\n });\n}\n\n// src/studio/bridge/bridge-screenshot.ts\nfunction loadHtml2Canvas() {\n if (state.html2canvasLoaded) return Promise.resolve();\n if (state.html2canvasPromise) return state.html2canvasPromise;\n state.html2canvasPromise = new Promise((resolve, reject) => {\n const script = document.createElement(\"script\");\n script.src = \"https://cdn.jsdelivr.net/npm/html2canvas-pro@2.0.0/dist/html2canvas-pro.min.js\";\n script.onload = () => {\n state.html2canvasLoaded = true;\n resolve();\n };\n script.onerror = (event) => {\n logger.warn(\n \"Failed to load html2canvas script. This may be caused by CSP script-src restrictions.\",\n { event: String(event) }\n );\n reject(new Error(\"Failed to load html2canvas script\"));\n };\n try {\n document.head.appendChild(script);\n } catch (error) {\n logger.warn(\n \"Failed to append html2canvas script element. This may be caused by CSP script-src restrictions.\",\n error instanceof Error ? error : { error: String(error) }\n );\n reject(\n error instanceof Error ? error : new Error(\"Failed to append html2canvas script element\")\n );\n }\n });\n return state.html2canvasPromise;\n}\nasync function captureScreenshot(options) {\n const { scrollTo, fullPage, quality = 0.8 } = options || {};\n const originalScrollY = window.scrollY;\n try {\n await loadHtml2Canvas();\n if (typeof scrollTo === \"number\") {\n window.scrollTo(0, scrollTo);\n await new Promise((r) => setTimeout(r, 150));\n }\n const canvasOptions = {\n useCORS: true,\n logging: false,\n scale: window.devicePixelRatio || 1\n };\n if (fullPage) {\n canvasOptions.height = document.documentElement.scrollHeight;\n canvasOptions.windowHeight = document.documentElement.scrollHeight;\n canvasOptions.y = 0;\n window.scrollTo(0, 0);\n await new Promise((r) => setTimeout(r, 100));\n }\n const html2canvasFn = window.html2canvas.default || window.html2canvas;\n const canvas = await html2canvasFn(document.body, canvasOptions);\n if (!canvas || canvas.width === 0 || canvas.height === 0) {\n logger.error(\"html2canvas produced empty canvas\", {\n width: canvas?.width,\n height: canvas?.height\n });\n window.scrollTo(0, originalScrollY);\n return {\n success: false,\n error: \"html2canvas produced empty canvas (0x0 dimensions)\"\n };\n }\n const dataUrl = canvas.toDataURL(\"image/png\", quality);\n if (!dataUrl || !dataUrl.startsWith(\"data:image/\") || dataUrl.length < 100) {\n logger.error(\"html2canvas produced invalid data URL\", {\n dataUrlPreview: dataUrl?.substring(0, 50)\n });\n window.scrollTo(0, originalScrollY);\n return {\n success: false,\n error: \"html2canvas produced invalid image data\"\n };\n }\n window.scrollTo(0, originalScrollY);\n return {\n success: true,\n data: dataUrl,\n width: canvas.width,\n height: canvas.height,\n scrollY: window.scrollY,\n totalHeight: document.documentElement.scrollHeight,\n viewportHeight: window.innerHeight,\n url: window.location.href\n };\n } catch (error) {\n logger.error(\"html2canvas error\", error instanceof Error ? error : { error: String(error) });\n window.scrollTo(0, originalScrollY);\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error)\n };\n }\n}\nasync function captureMultipleSections(sectionCount) {\n const originalScrollY = window.scrollY;\n const results = [];\n const totalHeight = document.documentElement.scrollHeight;\n const viewportHeight = window.innerHeight;\n const sections = sectionCount || Math.ceil(totalHeight / viewportHeight);\n try {\n for (let i = 0; i < sections; i++) {\n const scrollY = Math.min(i * viewportHeight, totalHeight - viewportHeight);\n const result = await captureScreenshot({ scrollTo: scrollY });\n if (result.success) {\n results.push({ ...result, section: i + 1, totalSections: sections });\n }\n }\n } finally {\n window.scrollTo(0, originalScrollY);\n }\n return results;\n}\n\n// src/studio/bridge/bridge-message-handler.ts\nvar SAFE_PROTOCOLS = /* @__PURE__ */ new Set([\"http:\", \"https:\"]);\nfunction sanitizeNavigationUrl(url) {\n if (typeof url !== \"string\" || url.length === 0) return null;\n if (url[0] === \"/\" && url[1] !== \"/\") return url;\n try {\n const parsed = new URL(url, window.location.origin);\n if (!SAFE_PROTOCOLS.has(parsed.protocol)) return null;\n if (parsed.origin === window.location.origin) return parsed.href;\n const host = parsed.hostname;\n if (host !== \"veryfront.com\" && !host.endsWith(\".veryfront.com\")) return null;\n return parsed.href;\n } catch {\n return null;\n }\n}\nfunction clearSelection(notifyStudio = false) {\n state.selectedNodeId = null;\n hideOverlay(state.selectionOverlay);\n if (notifyStudio) {\n postToStudio({ action: \"setSelectedNode\", id: null });\n }\n}\nfunction disableInspectMode(deselectElements = false) {\n state.inspectMode = false;\n hideOverlay(state.hoverOverlay);\n state.hoveredNodeId = null;\n if (deselectElements) {\n clearSelection();\n }\n}\nfunction postScreenshotResult(requestId, result) {\n postToStudio({\n action: \"screenshotResult\",\n requestId,\n multiple: false,\n ...result\n });\n}\nasync function handleScreenshotRequest(message) {\n if (message.multipleSections) {\n const results = await captureMultipleSections(message.sectionCount);\n postToStudio({\n action: \"screenshotResult\",\n requestId: message.requestId,\n multiple: true,\n results\n });\n return;\n }\n const result = await captureScreenshot(message.options);\n postScreenshotResult(message.requestId, result);\n}\nfunction handleStudioMessage(event) {\n if (!isFromStudio(event)) return;\n const message = event.data;\n if (!message?.action) return;\n const config3 = getConfig();\n switch (message.action) {\n case \"routeChange\": {\n const safeUrl = sanitizeNavigationUrl(message.url);\n if (!safeUrl) {\n logger.warn(\"[StudioBridge] Blocked unsafe URL in routeChange\", { url: message.url });\n return;\n }\n if (state.selectedNodeId) {\n clearSelection(true);\n }\n postToStudio({\n action: \"onPageTransitionStart\",\n url: safeUrl,\n projectId: config3.projectId\n });\n window.location.href = safeUrl;\n return;\n }\n case \"reload\":\n window.location.reload();\n return;\n case \"goBack\":\n window.history.back();\n return;\n case \"goForward\":\n window.history.forward();\n return;\n case \"colorMode\":\n setColorMode(message.value);\n return;\n case \"toggleInspectMode\":\n state.inspectMode = message.value;\n if (state.inspectMode) return;\n disableInspectMode(message.deselectElements);\n return;\n case \"setSelectedNode\":\n state.selectedNodeId = message.id;\n showSelectionOverlay(message.id);\n if (message.scroll) scrollToElement(message.id);\n return;\n case \"setHoveredNode\":\n if (!state.inspectMode) showHoverOverlay(message.id);\n return;\n case \"screenshot\":\n void handleScreenshotRequest(message);\n return;\n default:\n logger.debug(\"Unknown action\", { action: message.action });\n return;\n }\n}\n\n// src/studio/bridge/bridge-init.ts\nfunction notifyAppLoaded() {\n const config3 = getConfig();\n postToStudio({ action: \"appLoaded\", url: window.location.href });\n postToStudio({\n action: \"appUpdated\",\n url: window.location.href,\n id: config3.pageId,\n isInitialLoad: true,\n errors: [],\n warnings: []\n });\n postToStudio({\n action: \"onPageTransitionEnd\",\n url: window.location.href,\n projectId: config3.projectId,\n id: config3.pageId,\n params: {}\n });\n}\nfunction notifyAppUnloaded() {\n postToStudio({ action: \"appUnloaded\", url: window.location.href });\n}\nfunction init() {\n const config3 = getConfig();\n const params = new URLSearchParams(window.location.search);\n const studioEmbed = params.get(\"studio_embed\") === \"true\";\n const isStandalone = window.parent === window && !studioEmbed;\n if (isStandalone) {\n logger.debug(\n \"[StudioBridge] Not in iframe and not studio_embed mode, skipping initialization\"\n );\n return;\n }\n logger.debug(\"Initializing...\");\n if (!isStandalone) {\n injectOverlayStyles();\n state.hoverOverlay = createOverlay(\"hover\");\n state.selectionOverlay = createOverlay(\"selection\");\n setupConsoleCapture();\n setupErrorHandling();\n setupInspectMode();\n }\n window.addEventListener(\"message\", handleStudioMessage);\n if (!isStandalone) {\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", function() {\n notifyAppLoaded();\n setupMutationObserver();\n }, { once: true });\n } else {\n notifyAppLoaded();\n setupMutationObserver();\n }\n window.addEventListener(\"beforeunload\", notifyAppUnloaded, { once: true });\n }\n const colorMode = params.get(\"color_mode\");\n if (colorMode) setColorMode(colorMode);\n if (!isStandalone) {\n const inspectModeParam = params.get(\"inspect_mode\");\n if (inspectModeParam === \"true\") {\n state.inspectMode = true;\n logger.debug(\"Inspect mode enabled from query param\");\n }\n }\n logger.debug(\"Initialized successfully\");\n}\n\n// src/studio/bridge/bridge-coordinator.ts\ninitConfig();\nvar config2 = getConfig();\nif (!config2.debugSkipInit) {\n init();\n}\n";
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.1.142";
1
+ export declare const VERSION = "0.1.144";
2
2
  //# sourceMappingURL=version-constant.d.ts.map
@@ -1,3 +1,3 @@
1
1
  // Keep in sync with deno.json version.
2
2
  // scripts/release.ts updates this constant during releases.
3
- export const VERSION = "0.1.142";
3
+ export const VERSION = "0.1.144";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.142",
3
+ "version": "0.1.144",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",
@@ -188,8 +188,7 @@ export function normalizeKnowledgeInputPath(inputPath: string): string {
188
188
  }
189
189
 
190
190
  export function normalizeProjectUploadPath(inputPath: string): string {
191
- const normalizedPath = normalizeKnowledgeInputPath(inputPath);
192
- return normalizedPath === "uploads" ? "" : normalizedPath.replace(/^uploads\/+/, "");
191
+ return normalizeKnowledgeInputPath(inputPath);
193
192
  }
194
193
 
195
194
  export function formatKnowledgeUploadSource(uploadPath: string): string {
@@ -203,7 +202,10 @@ function resolveExplicitUploadPath(inputPath: string): string {
203
202
  const normalizedInput = normalizeKnowledgeInputPath(inputPath);
204
203
  const displayInput = inputPath.replace(/\\/g, "/");
205
204
  const uploadPath = normalizeProjectUploadPath(inputPath);
206
- if (!uploadPath || normalizedInput.endsWith("/")) {
205
+ if (
206
+ !uploadPath || uploadPath === "uploads" || normalizedInput === "uploads" ||
207
+ normalizedInput.endsWith("/")
208
+ ) {
207
209
  throw new Error(
208
210
  `Directory upload references require --path <prefix> --all: ${displayInput}`,
209
211
  );
@@ -185,15 +185,17 @@ export class MCPDevServer {
185
185
  case "notifications/initialized":
186
186
  return Promise.resolve({});
187
187
  case "tools/list":
188
- return Promise.resolve(this.handleToolsList());
188
+ return Promise.resolve(this.handleToolsList(params));
189
189
  case "tools/call":
190
190
  return this.handleToolsCall(params);
191
191
  case "resources/list":
192
- return Promise.resolve(this.handleResourcesList());
192
+ return Promise.resolve(this.handleResourcesList(params));
193
+ case "resources/templates/list":
194
+ return Promise.resolve(this.handleResourceTemplatesList());
193
195
  case "resources/read":
194
196
  return this.handleResourcesRead(params);
195
197
  case "prompts/list":
196
- return Promise.resolve(this.handlePromptsList());
198
+ return Promise.resolve(this.handlePromptsList(params));
197
199
  case "prompts/get":
198
200
  return this.handlePromptsGet(params);
199
201
  default:
@@ -215,7 +217,7 @@ export class MCPDevServer {
215
217
  );
216
218
  }
217
219
 
218
- private handleToolsList(): { tools: ToolListEntry[] } {
220
+ private handleToolsList(_params?: unknown): { tools: ToolListEntry[] } {
219
221
  return {
220
222
  tools: allTools.map((tool) => {
221
223
  const entry: ToolListEntry = {
@@ -268,7 +270,11 @@ export class MCPDevServer {
268
270
  );
269
271
  }
270
272
 
271
- private handleResourcesList(): unknown {
273
+ private handleResourceTemplatesList(): { resourceTemplates: Array<Record<string, unknown>> } {
274
+ return { resourceTemplates: [] };
275
+ }
276
+
277
+ private handleResourcesList(_params?: unknown): unknown {
272
278
  return {
273
279
  resources: [
274
280
  {
@@ -456,7 +462,7 @@ export class MCPDevServer {
456
462
  );
457
463
  }
458
464
 
459
- private handlePromptsList(): unknown {
465
+ private handlePromptsList(_params?: unknown): unknown {
460
466
  return {
461
467
  prompts: [
462
468
  {
@@ -94,21 +94,15 @@ export class StandaloneMCPServer {
94
94
  case "notifications/initialized":
95
95
  return Promise.resolve({});
96
96
  case "tools/list":
97
- return Promise.resolve({
98
- tools: this.tools.map(({ name, description, inputSchema }) => ({
99
- name,
100
- description,
101
- inputSchema,
102
- })),
103
- });
97
+ return Promise.resolve(this.handleToolsList(params));
104
98
  case "tools/call":
105
99
  return this.handleToolsCall(params);
106
100
  case "resources/list":
107
- return Promise.resolve(this.handleResourcesList());
101
+ return Promise.resolve(this.handleResourcesList(params));
108
102
  case "resources/read":
109
103
  return this.handleResourcesRead(params);
110
104
  case "prompts/list":
111
- return Promise.resolve(this.handlePromptsList());
105
+ return Promise.resolve(this.handlePromptsList(params));
112
106
  case "prompts/get":
113
107
  return this.handlePromptsGet(params);
114
108
  default:
@@ -137,7 +131,17 @@ export class StandaloneMCPServer {
137
131
  }
138
132
  }
139
133
 
140
- private handleResourcesList(): unknown {
134
+ private handleToolsList(_params?: unknown): unknown {
135
+ return {
136
+ tools: this.tools.map(({ name, description, inputSchema }) => ({
137
+ name,
138
+ description,
139
+ inputSchema,
140
+ })),
141
+ };
142
+ }
143
+
144
+ private handleResourcesList(_params?: unknown): unknown {
141
145
  return {
142
146
  resources: [
143
147
  {
@@ -207,7 +211,7 @@ export class StandaloneMCPServer {
207
211
  throw new Error(`Unknown resource: ${uri}`);
208
212
  }
209
213
 
210
- private handlePromptsList(): unknown {
214
+ private handlePromptsList(_params?: unknown): unknown {
211
215
  return {
212
216
  prompts: [
213
217
  {
@@ -11,6 +11,7 @@ import { z } from "zod";
11
11
  import { join } from "../../src/platform/compat/path/index.js";
12
12
  import { cwd } from "../../src/platform/index.js";
13
13
  import { createFileSystem } from "../../src/platform/index.js";
14
+ import { getEnv } from "../../src/platform/compat/process.js";
14
15
  import { type EnvironmentConfig, getEnvironmentConfig } from "../../src/config/index.js";
15
16
  import { cliLogger } from "../utils/index.js";
16
17
  import { readToken } from "../auth/token-store.js";
@@ -102,6 +103,14 @@ async function inferProjectSlug(projectDir: string): Promise<string | null> {
102
103
  return dirName ? slugify(dirName) : null;
103
104
  }
104
105
 
106
+ function resolveTenantProjectReference(): string | undefined {
107
+ return getEnv("VERYFRONT_PROJECT_SLUG") ||
108
+ getEnv("TENANT_PROJECT_SLUG") ||
109
+ getEnv("VERYFRONT_PROJECT_ID") ||
110
+ getEnv("TENANT_PROJECT_ID") ||
111
+ undefined;
112
+ }
113
+
105
114
  async function resolveConfigBase(
106
115
  projectDir: string | undefined,
107
116
  env: EnvironmentConfig,
@@ -127,10 +136,13 @@ async function resolveConfigBase(
127
136
  );
128
137
  }
129
138
 
130
- const projectSlug = env.projectSlug ?? configFile?.projectSlug ?? (await inferProjectSlug(dir));
139
+ const projectSlug = env.projectSlug ??
140
+ configFile?.projectSlug ??
141
+ resolveTenantProjectReference() ??
142
+ (await inferProjectSlug(dir));
131
143
  if (!projectSlug) {
132
144
  throw new Error(
133
- "Could not determine project slug. Set VERYFRONT_PROJECT_SLUG environment variable or add projectSlug to veryfront.config.ts",
145
+ "Could not determine project reference. Set VERYFRONT_PROJECT_SLUG, TENANT_PROJECT_SLUG, VERYFRONT_PROJECT_ID, or add projectSlug to veryfront.config.ts",
134
146
  );
135
147
  }
136
148