veryfront 0.1.48 → 0.1.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/cli/app/components/inline-input.d.ts +1 -4
- package/esm/cli/app/components/inline-input.d.ts.map +1 -1
- package/esm/cli/app/components/inline-input.js +1 -1
- package/esm/cli/app/components/list-select.d.ts +0 -8
- package/esm/cli/app/components/list-select.d.ts.map +1 -1
- package/esm/cli/app/components/list-select.js +0 -13
- package/esm/cli/app/operations/project-creation.d.ts +2 -14
- package/esm/cli/app/operations/project-creation.d.ts.map +1 -1
- package/esm/cli/app/operations/project-creation.js +3 -68
- package/esm/cli/app/shell.js +1 -1
- package/esm/cli/app/utils.d.ts +1 -2
- package/esm/cli/app/utils.d.ts.map +1 -1
- package/esm/cli/app/utils.js +1 -17
- package/esm/cli/app/views/dashboard.d.ts +0 -4
- package/esm/cli/app/views/dashboard.d.ts.map +1 -1
- package/esm/cli/app/views/dashboard.js +0 -15
- package/esm/cli/app/views/startup.d.ts +0 -4
- package/esm/cli/app/views/startup.d.ts.map +1 -1
- package/esm/cli/app/views/startup.js +0 -7
- package/esm/cli/auth/login.d.ts.map +1 -1
- package/esm/cli/auth/login.js +1 -2
- package/esm/cli/auth/token-store.js +1 -1
- package/esm/cli/auth/utils.d.ts +2 -1
- package/esm/cli/auth/utils.d.ts.map +1 -1
- package/esm/cli/commands/generate/integration-generator.js +1 -1
- package/esm/cli/commands/install/types.d.ts +2 -2
- package/esm/cli/help/formatters.d.ts +1 -1
- package/esm/cli/help/formatters.d.ts.map +1 -1
- package/esm/cli/help/formatters.js +8 -4
- package/esm/cli/help/main-help.d.ts.map +1 -1
- package/esm/cli/help/main-help.js +2 -2
- package/esm/cli/mcp/server.d.ts +0 -1
- package/esm/cli/mcp/server.d.ts.map +1 -1
- package/esm/cli/mcp/server.js +2 -5
- package/esm/cli/mcp/tools/dev-tools.d.ts +1 -1
- package/esm/cli/mcp/tools/dev-tools.d.ts.map +1 -1
- package/esm/cli/mcp/tools/dev-tools.js +1 -2
- package/esm/cli/mcp/tools/scaffold-tools.d.ts +2 -2
- package/esm/cli/mcp/tools.d.ts +1 -2
- package/esm/cli/mcp/tools.d.ts.map +1 -1
- package/esm/cli/mcp/tools.js +1 -2
- package/esm/cli/shared/reserve-slug.d.ts.map +1 -1
- package/esm/cli/shared/reserve-slug.js +1 -3
- package/esm/cli/sync/ignore.d.ts +1 -1
- package/esm/cli/sync/ignore.d.ts.map +1 -1
- package/esm/cli/sync/ignore.js +22 -18
- package/esm/cli/ui/colors.d.ts +0 -1
- package/esm/cli/ui/colors.d.ts.map +1 -1
- package/esm/cli/ui/colors.js +0 -10
- package/esm/cli/ui/dot-matrix.d.ts.map +1 -1
- package/esm/cli/ui/dot-matrix.js +2 -5
- package/esm/cli/ui/tui.js +2 -3
- package/esm/cli/utils/index.js +1 -2
- package/esm/cli/utils/package-manager.d.ts.map +1 -1
- package/esm/cli/utils/package-manager.js +3 -4
- package/esm/deno.d.ts +3 -0
- package/esm/deno.js +22 -19
- package/esm/src/agent/chat-handler.d.ts.map +1 -1
- package/esm/src/agent/chat-handler.js +8 -23
- package/esm/src/agent/factory.d.ts +0 -8
- package/esm/src/agent/factory.d.ts.map +1 -1
- package/esm/src/agent/factory.js +50 -1
- package/esm/src/agent/index.d.ts +16 -1
- package/esm/src/agent/index.d.ts.map +1 -1
- package/esm/src/agent/index.js +16 -1
- package/esm/src/agent/memory/memory.d.ts +2 -2
- package/esm/src/agent/memory/memory.d.ts.map +1 -1
- package/esm/src/agent/memory/memory.js +0 -1
- package/esm/src/agent/react/use-chat/types.d.ts +2 -0
- package/esm/src/agent/react/use-chat/types.d.ts.map +1 -1
- package/esm/src/agent/react/use-chat/use-chat.d.ts.map +1 -1
- package/esm/src/agent/react/use-chat/use-chat.js +15 -9
- package/esm/src/agent/runtime/index.d.ts +18 -0
- package/esm/src/agent/runtime/index.d.ts.map +1 -1
- package/esm/src/agent/runtime/index.js +167 -36
- package/esm/src/agent/runtime/tool-helpers.d.ts +6 -1
- package/esm/src/agent/runtime/tool-helpers.d.ts.map +1 -1
- package/esm/src/agent/runtime/tool-helpers.js +10 -1
- package/esm/src/agent/schemas/agent.schema.d.ts +4 -4
- package/esm/src/agent/types.d.ts +10 -0
- package/esm/src/agent/types.d.ts.map +1 -1
- package/esm/src/ai/registry-manager.d.ts.map +1 -1
- package/esm/src/ai/registry-manager.js +2 -1
- package/esm/src/build/bundler/code-splitter/manifest-builder.d.ts +0 -2
- package/esm/src/build/bundler/code-splitter/manifest-builder.d.ts.map +1 -1
- package/esm/src/build/bundler/code-splitter/manifest-builder.js +1 -1
- package/esm/src/build/compiler/mdx-compiler/validator.d.ts.map +1 -1
- package/esm/src/build/compiler/mdx-compiler/validator.js +2 -3
- package/esm/src/build/compiler/mdx-to-js.d.ts +0 -2
- package/esm/src/build/compiler/mdx-to-js.d.ts.map +1 -1
- package/esm/src/build/compiler/mdx-to-js.js +0 -81
- package/esm/src/build/index.d.ts +0 -6
- package/esm/src/build/index.d.ts.map +1 -1
- package/esm/src/build/index.js +0 -1
- package/esm/src/build/production-build/build/route-collector.d.ts.map +1 -1
- package/esm/src/build/production-build/build/route-collector.js +2 -3
- package/esm/src/build/production-build/templates.d.ts +5 -9
- package/esm/src/build/production-build/templates.d.ts.map +1 -1
- package/esm/src/build/production-build/templates.js +6 -18
- package/esm/src/build/vendor-cache.d.ts.map +1 -1
- package/esm/src/build/vendor-cache.js +0 -5
- package/esm/src/cache/tokenizing-gateway.d.ts +0 -2
- package/esm/src/cache/tokenizing-gateway.d.ts.map +1 -1
- package/esm/src/cache/tokenizing-gateway.js +1 -3
- package/esm/src/chat/index.d.ts +2 -2
- package/esm/src/chat/index.d.ts.map +1 -1
- package/esm/src/chat/index.js +1 -1
- package/esm/src/config/define-config.d.ts.map +1 -1
- package/esm/src/config/define-config.js +28 -25
- package/esm/src/config/loader.d.ts.map +1 -1
- package/esm/src/config/loader.js +10 -7
- package/esm/src/config/schemas/config.schema.d.ts +58 -12
- package/esm/src/config/schemas/config.schema.d.ts.map +1 -1
- package/esm/src/config/schemas/config.schema.js +12 -0
- package/esm/src/data/data-fetcher.d.ts +1 -2
- package/esm/src/data/data-fetcher.d.ts.map +1 -1
- package/esm/src/data/data-fetcher.js +14 -15
- package/esm/src/data/server-data-fetcher.d.ts +0 -3
- package/esm/src/data/server-data-fetcher.d.ts.map +1 -1
- package/esm/src/data/server-data-fetcher.js +1 -8
- package/esm/src/data/static-data-fetcher.d.ts +1 -3
- package/esm/src/data/static-data-fetcher.d.ts.map +1 -1
- package/esm/src/data/static-data-fetcher.js +1 -3
- package/esm/src/discovery/discovery-engine.d.ts.map +1 -1
- package/esm/src/discovery/discovery-engine.js +19 -1
- package/esm/src/discovery/discovery-utils.d.ts +0 -4
- package/esm/src/discovery/discovery-utils.d.ts.map +1 -1
- package/esm/src/discovery/discovery-utils.js +0 -6
- package/esm/src/discovery/file-discovery.d.ts +3 -1
- package/esm/src/discovery/file-discovery.d.ts.map +1 -1
- package/esm/src/discovery/file-discovery.js +3 -8
- package/esm/src/discovery/handlers/index.d.ts +1 -0
- package/esm/src/discovery/handlers/index.d.ts.map +1 -1
- package/esm/src/discovery/handlers/index.js +1 -0
- package/esm/src/discovery/handlers/skill-handler.d.ts +26 -0
- package/esm/src/discovery/handlers/skill-handler.d.ts.map +1 -0
- package/esm/src/discovery/handlers/skill-handler.js +87 -0
- package/esm/src/discovery/handlers/task-handler.d.ts.map +1 -1
- package/esm/src/discovery/handlers/task-handler.js +1 -5
- package/esm/src/discovery/transpiler.d.ts.map +1 -1
- package/esm/src/discovery/transpiler.js +7 -4
- package/esm/src/discovery/types.d.ts +3 -0
- package/esm/src/discovery/types.d.ts.map +1 -1
- package/esm/src/embedding/resolve.d.ts.map +1 -1
- package/esm/src/embedding/resolve.js +1 -3
- package/esm/src/embedding/upload-handler.d.ts.map +1 -1
- package/esm/src/embedding/upload-handler.js +4 -3
- package/esm/src/embedding/upload-store.js +4 -4
- package/esm/src/errors/error-registry.d.ts +2 -1
- package/esm/src/errors/error-registry.d.ts.map +1 -1
- package/esm/src/errors/http-error.d.ts +1 -2
- package/esm/src/errors/http-error.d.ts.map +1 -1
- package/esm/src/errors/http-error.js +2 -2
- package/esm/src/errors/middleware/cli-error-boundary.d.ts.map +1 -1
- package/esm/src/errors/middleware/cli-error-boundary.js +13 -2
- package/esm/src/errors/middleware/wrap-unknown.d.ts +0 -7
- package/esm/src/errors/middleware/wrap-unknown.d.ts.map +1 -1
- package/esm/src/errors/middleware/wrap-unknown.js +0 -9
- package/esm/src/errors/veryfront-error.d.ts +0 -3
- package/esm/src/errors/veryfront-error.d.ts.map +1 -1
- package/esm/src/errors/veryfront-error.js +0 -5
- package/esm/src/html/schemas/html.schema.d.ts +2 -2
- package/esm/src/html/styles-builder/css-hash-cache.d.ts.map +1 -1
- package/esm/src/html/styles-builder/css-hash-cache.js +1 -2
- package/esm/src/html/styles-builder/plugin-loader.d.ts.map +1 -1
- package/esm/src/html/styles-builder/plugin-loader.js +50 -21
- package/esm/src/html/styles-builder/tailwind-compiler-cache.d.ts.map +1 -1
- package/esm/src/html/styles-builder/tailwind-compiler-cache.js +12 -4
- package/esm/src/html/utils.d.ts +0 -7
- package/esm/src/html/utils.d.ts.map +1 -1
- package/esm/src/html/utils.js +0 -25
- package/esm/src/modules/component-registry/registry.d.ts +0 -3
- package/esm/src/modules/component-registry/registry.d.ts.map +1 -1
- package/esm/src/modules/component-registry/registry.js +0 -3
- package/esm/src/oauth/handlers/callback-handler.d.ts +2 -0
- package/esm/src/oauth/handlers/callback-handler.d.ts.map +1 -1
- package/esm/src/oauth/handlers/callback-handler.js +10 -4
- package/esm/src/oauth/providers/common.d.ts.map +1 -1
- package/esm/src/oauth/providers/common.js +0 -23
- package/esm/src/observability/auto-instrument/react-instrumentation.js +2 -1
- package/esm/src/observability/instruments/instruments-factory.d.ts +1 -1
- package/esm/src/observability/instruments/instruments-factory.d.ts.map +1 -1
- package/esm/src/observability/instruments/instruments-factory.js +5 -4
- package/esm/src/observability/metrics/config.js +5 -4
- package/esm/src/observability/metrics/manager.js +1 -1
- package/esm/src/observability/tracing/span-operations.d.ts.map +1 -1
- package/esm/src/observability/tracing/span-operations.js +14 -8
- package/esm/src/platform/compat/console/node.d.ts +0 -1
- package/esm/src/platform/compat/console/node.d.ts.map +1 -1
- package/esm/src/platform/compat/console/node.js +4 -11
- package/esm/src/platform/compat/fs.d.ts.map +1 -1
- package/esm/src/platform/compat/fs.js +4 -2
- package/esm/src/platform/compat/opaque-deps.d.ts +4 -2
- package/esm/src/platform/compat/opaque-deps.d.ts.map +1 -1
- package/esm/src/platform/compat/opaque-deps.js +1 -1
- package/esm/src/platform/compat/path/basic-operations.d.ts.map +1 -1
- package/esm/src/platform/compat/path/basic-operations.js +8 -7
- package/esm/src/platform/compat/path/resolution.d.ts.map +1 -1
- package/esm/src/platform/compat/path/resolution.js +15 -10
- package/esm/src/platform/compat/path/url-conversion.d.ts.map +1 -1
- package/esm/src/platform/compat/path/url-conversion.js +3 -2
- package/esm/src/platform/compat/process.d.ts +2 -14
- package/esm/src/platform/compat/process.d.ts.map +1 -1
- package/esm/src/platform/compat/process.js +92 -70
- package/esm/src/provider/index.d.ts +1 -1
- package/esm/src/provider/index.d.ts.map +1 -1
- package/esm/src/provider/index.js +1 -1
- package/esm/src/provider/local/ai-sdk-adapter.d.ts.map +1 -1
- package/esm/src/provider/local/ai-sdk-adapter.js +18 -18
- package/esm/src/provider/local/env.d.ts.map +1 -1
- package/esm/src/provider/local/env.js +2 -1
- package/esm/src/provider/model-registry.d.ts +10 -0
- package/esm/src/provider/model-registry.d.ts.map +1 -1
- package/esm/src/provider/model-registry.js +43 -0
- package/esm/src/proxy/retry.d.ts +3 -3
- package/esm/src/proxy/retry.d.ts.map +1 -1
- package/esm/src/proxy/retry.js +0 -7
- package/esm/src/proxy/tracing.d.ts +1 -5
- package/esm/src/proxy/tracing.d.ts.map +1 -1
- package/esm/src/proxy/tracing.js +1 -7
- package/esm/src/react/components/ai/chat/components/code-block.js +1 -1
- package/esm/src/react/components/ai/chat/components/skill-badge.d.ts +12 -0
- package/esm/src/react/components/ai/chat/components/skill-badge.d.ts.map +1 -0
- package/esm/src/react/components/ai/chat/components/skill-badge.js +34 -0
- package/esm/src/react/components/ai/chat/components/step-indicator.js +4 -4
- package/esm/src/react/components/ai/chat/composition/chat-message-list.d.ts.map +1 -1
- package/esm/src/react/components/ai/chat/composition/chat-message-list.js +8 -2
- package/esm/src/react/components/ai/chat/composition/message.d.ts.map +1 -1
- package/esm/src/react/components/ai/chat/composition/message.js +8 -2
- package/esm/src/react/components/ai/chat/index.d.ts +4 -1
- package/esm/src/react/components/ai/chat/index.d.ts.map +1 -1
- package/esm/src/react/components/ai/chat/index.js +4 -3
- package/esm/src/react/components/ai/chat/utils/message-parts.d.ts +2 -0
- package/esm/src/react/components/ai/chat/utils/message-parts.d.ts.map +1 -1
- package/esm/src/react/components/ai/chat/utils/message-parts.js +9 -0
- package/esm/src/react/components/ai/chat-with-sidebar.d.ts +1 -0
- package/esm/src/react/components/ai/chat-with-sidebar.d.ts.map +1 -1
- package/esm/src/react/components/ai/chat-with-sidebar.js +1 -0
- package/esm/src/react/components/ai/chat.d.ts +1 -1
- package/esm/src/react/components/ai/chat.d.ts.map +1 -1
- package/esm/src/react/components/ai/chat.js +1 -1
- package/esm/src/rendering/cache/index.d.ts +1 -5
- package/esm/src/rendering/cache/index.d.ts.map +1 -1
- package/esm/src/rendering/cache/index.js +1 -5
- package/esm/src/rendering/orchestrator/pipeline.d.ts +11 -2
- package/esm/src/rendering/orchestrator/pipeline.d.ts.map +1 -1
- package/esm/src/rendering/shared/context-aware-cache.d.ts.map +1 -1
- package/esm/src/rendering/shared/context-aware-cache.js +0 -4
- package/esm/src/repositories/schemas/index.d.ts +1 -1
- package/esm/src/repositories/schemas/index.d.ts.map +1 -1
- package/esm/src/repositories/schemas/index.js +1 -1
- package/esm/src/repositories/schemas/repository.schema.d.ts +0 -11
- package/esm/src/repositories/schemas/repository.schema.d.ts.map +1 -1
- package/esm/src/repositories/schemas/repository.schema.js +0 -13
- package/esm/src/repositories/types.d.ts +1 -1
- package/esm/src/repositories/types.d.ts.map +1 -1
- package/esm/src/routing/api/module-loader/loader.d.ts.map +1 -1
- package/esm/src/routing/api/module-loader/loader.js +63 -7
- package/esm/src/routing/api/openapi/path-utils.d.ts +0 -19
- package/esm/src/routing/api/openapi/path-utils.d.ts.map +1 -1
- package/esm/src/routing/api/openapi/path-utils.js +0 -34
- package/esm/src/routing/api/openapi/spec-generator.d.ts.map +1 -1
- package/esm/src/routing/api/openapi/spec-generator.js +1 -19
- package/esm/src/routing/api/openapi/types.d.ts +1 -0
- package/esm/src/routing/api/openapi/types.d.ts.map +1 -1
- package/esm/src/routing/api/openapi/types.js +18 -0
- package/esm/src/sandbox/sandbox.d.ts +1 -0
- package/esm/src/sandbox/sandbox.d.ts.map +1 -1
- package/esm/src/sandbox/sandbox.js +7 -8
- package/esm/src/security/http/cors/constants.d.ts +0 -2
- package/esm/src/security/http/cors/constants.d.ts.map +1 -1
- package/esm/src/security/http/cors/constants.js +0 -2
- package/esm/src/security/http/response/index.d.ts +3 -4
- package/esm/src/security/http/response/index.d.ts.map +1 -1
- package/esm/src/security/http/response/index.js +2 -3
- package/esm/src/server/context/enriched-context.d.ts +0 -8
- package/esm/src/server/context/enriched-context.d.ts.map +1 -1
- package/esm/src/server/context/enriched-context.js +1 -12
- package/esm/src/server/dev-server/server.d.ts.map +1 -1
- package/esm/src/server/dev-server/server.js +11 -4
- package/esm/src/server/dev-ui/manifest.d.ts +20 -20
- package/esm/src/server/dev-ui/manifest.js +20 -20
- package/esm/src/server/handlers/dev/framework-candidates.generated.d.ts.map +1 -1
- package/esm/src/server/handlers/dev/framework-candidates.generated.js +426 -179
- package/esm/src/server/runtime-handler/index.d.ts.map +1 -1
- package/esm/src/server/runtime-handler/index.js +14 -24
- package/esm/src/server/shared/renderer/memory/pressure.d.ts +0 -7
- package/esm/src/server/shared/renderer/memory/pressure.d.ts.map +1 -1
- package/esm/src/server/shared/renderer/memory/pressure.js +1 -13
- package/esm/src/server/utils/domain-lookup.d.ts +0 -4
- package/esm/src/server/utils/domain-lookup.d.ts.map +1 -1
- package/esm/src/server/utils/domain-lookup.js +0 -3
- package/esm/src/skill/allowed-tools.d.ts +54 -0
- package/esm/src/skill/allowed-tools.d.ts.map +1 -0
- package/esm/src/skill/allowed-tools.js +87 -0
- package/esm/src/skill/executor.d.ts +28 -0
- package/esm/src/skill/executor.d.ts.map +1 -0
- package/esm/src/skill/executor.js +187 -0
- package/esm/src/skill/index.d.ts +19 -0
- package/esm/src/skill/index.d.ts.map +1 -0
- package/esm/src/skill/index.js +24 -0
- package/esm/src/skill/parser.d.ts +30 -0
- package/esm/src/skill/parser.d.ts.map +1 -0
- package/esm/src/skill/parser.js +162 -0
- package/esm/src/skill/path-safety.d.ts +22 -0
- package/esm/src/skill/path-safety.d.ts.map +1 -0
- package/esm/src/skill/path-safety.js +156 -0
- package/esm/src/skill/prompt-augmentation.d.ts +19 -0
- package/esm/src/skill/prompt-augmentation.d.ts.map +1 -0
- package/esm/src/skill/prompt-augmentation.js +36 -0
- package/esm/src/skill/registry.d.ts +25 -0
- package/esm/src/skill/registry.d.ts.map +1 -0
- package/esm/src/skill/registry.js +42 -0
- package/esm/src/skill/tools.d.ts +27 -0
- package/esm/src/skill/tools.d.ts.map +1 -0
- package/esm/src/skill/tools.js +149 -0
- package/esm/src/skill/types.d.ts +85 -0
- package/esm/src/skill/types.d.ts.map +1 -0
- package/esm/src/skill/types.js +27 -0
- package/esm/src/studio/bridge/bridge-bundle.generated.d.ts +1 -1
- package/esm/src/studio/bridge/bridge-bundle.generated.d.ts.map +1 -1
- package/esm/src/studio/bridge/bridge-bundle.generated.js +1 -1
- package/esm/src/studio/element-selector-injector.d.ts +0 -2
- package/esm/src/studio/element-selector-injector.d.ts.map +1 -1
- package/esm/src/task/runner.d.ts +6 -0
- package/esm/src/task/runner.d.ts.map +1 -1
- package/esm/src/task/runner.js +8 -8
- package/esm/src/tool/factory.js +31 -39
- package/esm/src/transforms/esm/http-cache-helpers.d.ts +0 -8
- package/esm/src/transforms/esm/http-cache-helpers.d.ts.map +1 -1
- package/esm/src/transforms/esm/http-cache-helpers.js +0 -20
- package/esm/src/transforms/esm/path-resolver.d.ts +0 -14
- package/esm/src/transforms/esm/path-resolver.d.ts.map +1 -1
- package/esm/src/transforms/esm/path-resolver.js +1 -92
- package/esm/src/transforms/esm/source-url-embed.d.ts +0 -14
- package/esm/src/transforms/esm/source-url-embed.d.ts.map +1 -1
- package/esm/src/transforms/esm/source-url-embed.js +0 -47
- package/esm/src/transforms/esm/transform-cache.d.ts.map +1 -1
- package/esm/src/transforms/esm/transform-cache.js +2 -6
- package/esm/src/transforms/mdx/index.d.ts +0 -1
- package/esm/src/transforms/mdx/index.d.ts.map +1 -1
- package/esm/src/transforms/mdx/index.js +0 -4
- package/esm/src/transforms/pipeline/context.d.ts +0 -1
- package/esm/src/transforms/pipeline/context.d.ts.map +1 -1
- package/esm/src/transforms/pipeline/context.js +0 -1
- package/esm/src/transforms/pipeline/index.d.ts +1 -2
- package/esm/src/transforms/pipeline/index.d.ts.map +1 -1
- package/esm/src/transforms/pipeline/index.js +0 -3
- package/esm/src/transforms/pipeline/stages/ssr-http-cache.d.ts.map +1 -1
- package/esm/src/transforms/pipeline/stages/ssr-http-cache.js +0 -1
- package/esm/src/types/entities/getEntityInfo.js +1 -1
- package/esm/src/types/index.d.ts +2 -13
- package/esm/src/types/index.d.ts.map +1 -1
- package/esm/src/types/server.d.ts +2 -1
- package/esm/src/types/server.d.ts.map +1 -1
- package/esm/src/utils/cache-file-ops.d.ts +1 -1
- package/esm/src/utils/cache-file-ops.d.ts.map +1 -1
- package/esm/src/utils/cache-file-ops.js +5 -6
- package/esm/src/utils/lru-wrapper.d.ts.map +1 -1
- package/esm/src/utils/lru-wrapper.js +2 -4
- package/esm/src/workflow/claude-code/event-publisher.d.ts +0 -4
- package/esm/src/workflow/claude-code/event-publisher.d.ts.map +1 -1
- package/esm/src/workflow/claude-code/event-publisher.js +2 -6
- package/esm/src/workflow/executor/workflow-executor.d.ts.map +1 -1
- package/esm/src/workflow/executor/workflow-executor.js +6 -1
- package/esm/src/workflow/react/use-workflow-list.d.ts.map +1 -1
- package/esm/src/workflow/react/use-workflow-list.js +2 -1
- package/esm/src/workflow/schemas/workflow.schema.d.ts +8 -8
- package/package.json +1 -2
- package/src/cli/app/components/inline-input.ts +0 -5
- package/src/cli/app/components/list-select.ts +0 -21
- package/src/cli/app/operations/project-creation.ts +4 -109
- package/src/cli/app/shell.ts +1 -1
- package/src/cli/app/utils.ts +0 -22
- package/src/cli/app/views/dashboard.ts +0 -17
- package/src/cli/app/views/startup.ts +0 -13
- package/src/cli/auth/login.ts +1 -2
- package/src/cli/auth/token-store.ts +1 -1
- package/src/cli/auth/utils.ts +2 -1
- package/src/cli/commands/generate/integration-generator.ts +1 -1
- package/src/cli/help/formatters.ts +11 -4
- package/src/cli/help/main-help.ts +2 -3
- package/src/cli/mcp/server.ts +2 -5
- package/src/cli/mcp/tools/dev-tools.ts +1 -2
- package/src/cli/mcp/tools.ts +8 -2
- package/src/cli/shared/reserve-slug.ts +1 -4
- package/src/cli/sync/ignore.ts +26 -21
- package/src/cli/ui/colors.ts +0 -12
- package/src/cli/ui/dot-matrix.ts +3 -6
- package/src/cli/ui/tui.ts +3 -3
- package/src/cli/utils/index.ts +1 -1
- package/src/cli/utils/package-manager.ts +3 -4
- package/src/deno.js +22 -19
- package/src/src/agent/chat-handler.ts +8 -23
- package/src/src/agent/factory.ts +58 -9
- package/src/src/agent/index.ts +16 -1
- package/src/src/agent/memory/memory.ts +0 -9
- package/src/src/agent/react/use-chat/types.ts +2 -0
- package/src/src/agent/react/use-chat/use-chat.ts +15 -9
- package/src/src/agent/runtime/index.ts +213 -35
- package/src/src/agent/runtime/tool-helpers.ts +9 -0
- package/src/src/agent/types.ts +10 -0
- package/src/src/ai/registry-manager.ts +2 -1
- package/src/src/build/bundler/code-splitter/manifest-builder.ts +1 -1
- package/src/src/build/compiler/mdx-compiler/validator.ts +3 -7
- package/src/src/build/compiler/mdx-to-js.ts +0 -101
- package/src/src/build/index.ts +0 -8
- package/src/src/build/production-build/build/route-collector.ts +2 -4
- package/src/src/build/production-build/templates.ts +9 -18
- package/src/src/build/vendor-cache.ts +0 -6
- package/src/src/cache/tokenizing-gateway.ts +1 -9
- package/src/src/chat/index.ts +3 -0
- package/src/src/config/define-config.ts +30 -29
- package/src/src/config/loader.ts +10 -9
- package/src/src/config/schemas/config.schema.ts +12 -0
- package/src/src/data/data-fetcher.ts +15 -21
- package/src/src/data/server-data-fetcher.ts +1 -8
- package/src/src/data/static-data-fetcher.ts +1 -6
- package/src/src/discovery/discovery-engine.ts +27 -0
- package/src/src/discovery/discovery-utils.ts +0 -7
- package/src/src/discovery/file-discovery.ts +3 -9
- package/src/src/discovery/handlers/index.ts +1 -0
- package/src/src/discovery/handlers/skill-handler.ts +123 -0
- package/src/src/discovery/handlers/task-handler.ts +1 -5
- package/src/src/discovery/transpiler.ts +7 -4
- package/src/src/discovery/types.ts +3 -0
- package/src/src/embedding/resolve.ts +1 -3
- package/src/src/embedding/upload-handler.ts +7 -3
- package/src/src/embedding/upload-store.ts +4 -4
- package/src/src/errors/error-registry.ts +2 -2
- package/src/src/errors/http-error.ts +7 -3
- package/src/src/errors/middleware/cli-error-boundary.ts +28 -2
- package/src/src/errors/middleware/wrap-unknown.ts +0 -10
- package/src/src/errors/veryfront-error.ts +0 -9
- package/src/src/html/styles-builder/css-hash-cache.ts +5 -2
- package/src/src/html/styles-builder/plugin-loader.ts +58 -21
- package/src/src/html/styles-builder/tailwind-compiler-cache.ts +11 -4
- package/src/src/html/utils.ts +0 -33
- package/src/src/modules/component-registry/registry.ts +0 -3
- package/src/src/modules/server/websocket-handler.ts +1 -1
- package/src/src/oauth/handlers/callback-handler.ts +17 -5
- package/src/src/oauth/providers/base.ts +3 -3
- package/src/src/oauth/providers/common.ts +0 -23
- package/src/src/observability/auto-instrument/react-instrumentation.ts +2 -2
- package/src/src/observability/instruments/instruments-factory.ts +6 -6
- package/src/src/observability/metrics/config.ts +5 -5
- package/src/src/observability/metrics/manager.ts +1 -1
- package/src/src/observability/tracing/span-operations.ts +14 -9
- package/src/src/platform/compat/console/node.ts +4 -14
- package/src/src/platform/compat/fs.ts +14 -3
- package/src/src/platform/compat/opaque-deps.ts +10 -5
- package/src/src/platform/compat/path/basic-operations.ts +9 -7
- package/src/src/platform/compat/path/resolution.ts +15 -8
- package/src/src/platform/compat/path/url-conversion.ts +10 -6
- package/src/src/platform/compat/process.ts +133 -76
- package/src/src/provider/index.ts +1 -0
- package/src/src/provider/local/ai-sdk-adapter.ts +40 -37
- package/src/src/provider/local/env.ts +4 -1
- package/src/src/provider/model-registry.ts +47 -0
- package/src/src/proxy/retry.ts +0 -9
- package/src/src/proxy/tracing.ts +1 -9
- package/src/src/react/components/ai/chat/components/code-block.tsx +1 -1
- package/src/src/react/components/ai/chat/components/skill-badge.tsx +51 -0
- package/src/src/react/components/ai/chat/components/step-indicator.tsx +4 -4
- package/src/src/react/components/ai/chat/composition/chat-message-list.tsx +9 -2
- package/src/src/react/components/ai/chat/composition/message.tsx +9 -2
- package/src/src/react/components/ai/chat/index.tsx +6 -1
- package/src/src/react/components/ai/chat/utils/message-parts.ts +11 -0
- package/src/src/react/components/ai/chat-with-sidebar.tsx +2 -0
- package/src/src/react/components/ai/chat.tsx +3 -0
- package/src/src/rendering/cache/index.ts +12 -5
- package/src/src/rendering/orchestrator/pipeline.ts +12 -2
- package/src/src/rendering/renderer.ts +1 -1
- package/src/src/rendering/shared/context-aware-cache.ts +0 -5
- package/src/src/repositories/schemas/index.ts +0 -2
- package/src/src/repositories/schemas/repository.schema.ts +0 -15
- package/src/src/repositories/types.ts +1 -6
- package/src/src/routing/api/module-loader/loader.ts +88 -3
- package/src/src/routing/api/openapi/path-utils.ts +0 -39
- package/src/src/routing/api/openapi/spec-generator.ts +1 -20
- package/src/src/routing/api/openapi/types.ts +20 -0
- package/src/src/sandbox/sandbox.ts +8 -8
- package/src/src/security/http/cors/constants.ts +0 -4
- package/src/src/security/http/response/index.ts +3 -9
- package/src/src/server/context/enriched-context.ts +1 -19
- package/src/src/server/dev-server/server.ts +11 -4
- package/src/src/server/dev-ui/manifest.js +20 -20
- package/src/src/server/handlers/dev/framework-candidates.generated.ts +426 -179
- package/src/src/server/runtime-handler/index.ts +17 -28
- package/src/src/server/shared/renderer/memory/pressure.ts +2 -15
- package/src/src/server/utils/domain-lookup.ts +0 -4
- package/src/src/skill/allowed-tools.ts +107 -0
- package/src/src/skill/executor.ts +215 -0
- package/src/src/skill/index.ts +60 -0
- package/src/src/skill/parser.ts +214 -0
- package/src/src/skill/path-safety.ts +203 -0
- package/src/src/skill/prompt-augmentation.ts +48 -0
- package/src/src/skill/registry.ts +51 -0
- package/src/src/skill/tools.ts +197 -0
- package/src/src/skill/types.ts +107 -0
- package/src/src/studio/bridge/bridge-bundle.generated.ts +1 -1
- package/src/src/studio/element-selector-injector.ts +0 -2
- package/src/src/task/runner.ts +10 -8
- package/src/src/tool/factory.ts +54 -54
- package/src/src/transforms/esm/http-cache-helpers.ts +0 -20
- package/src/src/transforms/esm/path-resolver.ts +1 -140
- package/src/src/transforms/esm/source-url-embed.ts +0 -53
- package/src/src/transforms/esm/transform-cache.ts +3 -7
- package/src/src/transforms/mdx/index.ts +0 -5
- package/src/src/transforms/pipeline/context.ts +0 -2
- package/src/src/transforms/pipeline/index.ts +0 -4
- package/src/src/transforms/pipeline/stages/ssr-http-cache.ts +0 -1
- package/src/src/types/entities/getEntityInfo.ts +1 -1
- package/src/src/types/index.ts +1 -20
- package/src/src/types/server.ts +1 -1
- package/src/src/utils/cache-file-ops.ts +5 -5
- package/src/src/utils/lru-wrapper.ts +2 -8
- package/src/src/workflow/claude-code/event-publisher.ts +13 -4
- package/src/src/workflow/executor/workflow-executor.ts +7 -2
- package/src/src/workflow/react/use-workflow-list.ts +3 -2
- package/esm/src/transforms/mdx/parser.d.ts +0 -4
- package/esm/src/transforms/mdx/parser.d.ts.map +0 -1
- package/esm/src/transforms/mdx/parser.js +0 -49
- package/src/src/transforms/mdx/parser.ts +0 -65
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
* @module
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
export const STUDIO_BRIDGE_BUNDLE: string | undefined = undefined;
|
|
9
|
+
export const STUDIO_BRIDGE_BUNDLE: string = "// src/studio/bridge/bridge-config.ts\nvar config = null;\nfunction initConfig() {\n const raw = window.__VF_BRIDGE_CONFIG__;\n const params = new URLSearchParams(window.location.search);\n const qsMode = params.get(\"vf_studio_mode\");\n const resolveMode = (value) => value === \"simple\" || qsMode === \"simple\" ? \"simple\" : \"advanced\";\n if (!raw || typeof raw !== \"object\") {\n console.warn(\"[StudioBridge] No bridge config found on window.__VF_BRIDGE_CONFIG__\");\n config = {\n projectId: \"\",\n pageId: \"\",\n pagePath: \"\",\n wsUrl: \"\",\n yjsGuid: \"\",\n studioMode: resolveMode(void 0),\n debugSkipInit: false,\n debugExposeInternals: false\n };\n return;\n }\n config = {\n projectId: raw.projectId ?? \"\",\n pageId: raw.pageId ?? \"\",\n pagePath: raw.pagePath ?? raw.pageId ?? \"\",\n wsUrl: raw.wsUrl ?? \"\",\n yjsGuid: raw.yjsGuid ?? \"\",\n studioMode: resolveMode(raw.studioMode),\n debugSkipInit: !!raw.debugSkipInit,\n debugExposeInternals: !!raw.debugExposeInternals\n };\n}\nfunction getConfig() {\n if (!config) {\n throw new Error(\"[StudioBridge] Config not initialized. Call initConfig() first.\");\n }\n return config;\n}\nfunction isMarkdownPage() {\n const cfg = getConfig();\n if (typeof cfg.pagePath !== \"string\") {\n return false;\n }\n const lowerPath = cfg.pagePath.toLowerCase();\n return lowerPath.endsWith(\".md\") || lowerPath.endsWith(\".mdx\");\n}\nfunction isMdxPage() {\n const cfg = getConfig();\n return typeof cfg.pagePath === \"string\" && cfg.pagePath.toLowerCase().endsWith(\".mdx\");\n}\n\n// src/studio/bridge/bridge-editor-state.ts\nvar editorState = {\n // Editor DOM\n markdownEditorRoot: null,\n markdownEditorSurface: null,\n markdownEditorTextarea: null,\n markdownEditButton: null,\n markdownFileId: null,\n // Timers\n markdownSyncTimer: null,\n markdownSelectionSyncTimer: null,\n // Selection overlay\n markdownSelectionOverlayRoot: null,\n markdownOverlaySelections: [],\n markdownSelectionOverlayRenderFrame: null,\n // Slash menu\n markdownSlashMenuRoot: null,\n markdownSlashMenuTimer: null,\n markdownSlashMenuContext: null,\n markdownSlashMenuCommands: [],\n markdownSlashMenuActiveIndex: 0,\n // Inline toolbar\n markdownInlineToolbarRoot: null,\n markdownInlineToolbarFrame: null,\n // Block drag\n markdownBlockDragHandle: null,\n markdownBlockDropIndicator: null,\n markdownBlockDropLabel: null,\n markdownBlockDragGhost: null,\n markdownBlockDragSourceIndex: -1,\n markdownBlockDropSlotIndex: -1,\n markdownBlockHandleHoverIndex: -1,\n markdownBlockDragActive: false,\n // MDX blocks\n markdownMdxBlocksRoot: null,\n // Global listener cleanups\n markdownGlobalListenerCleanups: [],\n // Lexical\n markdownLexicalApi: null,\n markdownLexicalSetupPromise: null,\n // Content\n markdownCurrentContent: \"\",\n markdownCurrentEditorContent: \"\",\n markdownLexicalRenderedContent: null,\n markdownApplyingRemoteUpdate: false,\n markdownRemoteUpdateToken: 0,\n markdownPendingLocalReconcile: false,\n markdownLastRemoteContent: null,\n markdownFrontmatter: \"\",\n markdownRawBlocks: [],\n markdownRawBlockTokenPrefix: \"VF_RAW_BLOCK\",\n markdownEditorToRenderedMap: [],\n markdownRenderedToEditorMap: [],\n markdownLatestMdxBlocks: [],\n markdownLatestMdxImportMap: {},\n markdownLatestPresenceUsers: [],\n markdownLatestSelections: [],\n markdownHasUnsavedChanges: false,\n // Yjs (dynamically imported — typed structurally)\n markdownYDoc: null,\n markdownYProvider: null,\n markdownYText: null,\n markdownYjsConnected: false,\n markdownYjsSetupId: 0,\n markdownYjsY: null,\n markdownPendingSelection: null\n};\n\n// src/studio/bridge/bridge-editor-callbacks.ts\nvar callbacks = null;\nfunction registerEditorCallbacks(cb) {\n if (callbacks) {\n console.warn(\"[StudioBridge] EditorCallbacks already registered, overwriting\");\n }\n callbacks = cb;\n}\nfunction getEditorCallbacks() {\n return callbacks;\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 LEXICAL_YJS_ORIGIN = \"lexical-yjs-binding\";\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\"];\nvar MARKDOWN_SLASH_COMMANDS = [\n {\n id: \"text\",\n label: \"Text\",\n description: \"Plain text block\",\n aliases: [\"text\", \"paragraph\", \"p\"],\n icon: \"T\",\n shortcut: \"\"\n },\n {\n id: \"heading-1\",\n label: \"Heading 1\",\n description: \"Create a top-level heading\",\n aliases: [\"h1\", \"heading\", \"title\"],\n icon: \"H\\u2081\",\n shortcut: \"#\"\n },\n {\n id: \"heading-2\",\n label: \"Heading 2\",\n description: \"Create a second-level heading\",\n aliases: [\"h2\", \"heading2\", \"subheading\"],\n icon: \"H\\u2082\",\n shortcut: \"##\"\n },\n {\n id: \"heading-3\",\n label: \"Heading 3\",\n description: \"Create a third-level heading\",\n aliases: [\"h3\", \"heading3\"],\n icon: \"H\\u2083\",\n shortcut: \"###\"\n },\n {\n id: \"bulleted-list\",\n label: \"Bulleted list\",\n description: \"Start a bullet list item\",\n aliases: [\"list\", \"bullet\", \"ul\"],\n icon: \"\\u2022\",\n shortcut: \"-\"\n },\n {\n id: \"numbered-list\",\n label: \"Numbered list\",\n description: \"Start a numbered list item\",\n aliases: [\"olist\", \"numbered\", \"ol\"],\n icon: \"1.\",\n shortcut: \"1.\"\n },\n {\n id: \"quote-block\",\n label: \"Quote\",\n description: \"Insert a block quote line\",\n aliases: [\"quote\", \"blockquote\"],\n icon: \"\\u201C\",\n shortcut: \">\"\n },\n {\n id: \"code-block\",\n label: \"Code block\",\n description: \"Insert a fenced code block\",\n aliases: [\"code\", \"fence\", \"snippet\"],\n icon: \"<>\",\n shortcut: \"```\"\n },\n {\n id: \"image\",\n label: \"Image\",\n description: \"Insert markdown image syntax\",\n aliases: [\"image\", \"img\", \"photo\"],\n icon: \"\\u{1F5BC}\",\n shortcut: \"\"\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-dom-helpers.ts\nfunction el(tag, className, textContent) {\n const element = document.createElement(tag);\n element.className = className;\n element.setAttribute(DATA_VF_IGNORE, \"true\");\n if (textContent !== void 0)\n element.textContent = textContent;\n return element;\n}\nfunction btn(className, textContent, onClick) {\n const button = el(\"button\", className, textContent);\n button.type = \"button\";\n button.addEventListener(\"click\", onClick);\n return button;\n}\n\n// src/studio/bridge/bridge-selection.ts\nfunction getDomRenderedText(root) {\n if (!root)\n return \"\";\n try {\n const range = document.createRange();\n range.selectNodeContents(root);\n return range.toString();\n } catch {\n return \"\";\n }\n}\nfunction getTextOffsetWithinRoot(root, targetNode, targetOffset) {\n if (!root || !targetNode) {\n return 0;\n }\n if (!root.contains(targetNode)) {\n return 0;\n }\n try {\n const range = document.createRange();\n range.selectNodeContents(root);\n range.setEnd(targetNode, targetOffset);\n return range.toString().length;\n } catch {\n return 0;\n }\n}\nfunction getMarkdownEditorSelection() {\n if (editorState.markdownLexicalApi && editorState.markdownEditorSurface) {\n const selection = window.getSelection();\n if (!selection || selection.rangeCount === 0) {\n return null;\n }\n const range = selection.getRangeAt(0);\n if (!editorState.markdownEditorSurface.contains(range.startContainer) || !editorState.markdownEditorSurface.contains(range.endContainer)) {\n return null;\n }\n const start = getTextOffsetWithinRoot(\n editorState.markdownEditorSurface,\n range.startContainer,\n range.startOffset\n );\n const end = getTextOffsetWithinRoot(\n editorState.markdownEditorSurface,\n range.endContainer,\n range.endOffset\n );\n return {\n start: Math.max(0, Math.min(start, end)),\n end: Math.max(0, Math.max(start, end))\n };\n }\n if (editorState.markdownEditorTextarea) {\n const start = typeof editorState.markdownEditorTextarea.selectionStart === \"number\" ? editorState.markdownEditorTextarea.selectionStart : 0;\n const end = typeof editorState.markdownEditorTextarea.selectionEnd === \"number\" ? editorState.markdownEditorTextarea.selectionEnd : start;\n return {\n start: Math.max(0, Math.min(start, end)),\n end: Math.max(0, Math.max(start, end))\n };\n }\n return null;\n}\nfunction getMarkdownRawBlockLength(index) {\n const rawBlock = editorState.markdownRawBlocks[index];\n if (typeof rawBlock !== \"string\") {\n return 0;\n }\n return rawBlock.length;\n}\nfunction escapeRegexText(value) {\n const text = String(value || \"\");\n let escaped = \"\";\n for (let i = 0; i < text.length; i += 1) {\n const char = text[i];\n if (\"\\\\^$.*+?()[]{}|\".indexOf(char) >= 0) {\n escaped += \"\\\\\" + char;\n } else {\n escaped += char;\n }\n }\n return escaped;\n}\nfunction getMarkdownRawBlockTokenPattern() {\n const prefix = typeof editorState.markdownRawBlockTokenPrefix === \"string\" && editorState.markdownRawBlockTokenPrefix ? editorState.markdownRawBlockTokenPrefix : \"VF_RAW_BLOCK\";\n const escapedPrefix = escapeRegexText(prefix);\n return new RegExp(\"\\\\[\\\\[\" + escapedPrefix + \"_(\\\\d+)\\\\]\\\\]\", \"g\");\n}\nfunction editorOffsetToBodyOffset(editorOffset, bias) {\n const editorContent = typeof editorState.markdownCurrentEditorContent === \"string\" ? editorState.markdownCurrentEditorContent : \"\";\n const maxOffset = editorContent.length;\n const safeOffset = Math.max(\n 0,\n Math.min(maxOffset, Math.trunc(editorOffset || 0))\n );\n const tokenPattern = getMarkdownRawBlockTokenPattern();\n let diffBefore = 0;\n let match = tokenPattern.exec(editorContent);\n while (match) {\n const token = match[0];\n const tokenStartEditor = match.index;\n const tokenEndEditor = tokenStartEditor + token.length;\n const rawLength = getMarkdownRawBlockLength(Number(match[1]));\n const tokenDelta = rawLength - token.length;\n const tokenStartBody = tokenStartEditor + diffBefore;\n if (safeOffset >= tokenEndEditor) {\n diffBefore += tokenDelta;\n match = tokenPattern.exec(editorContent);\n continue;\n }\n if (safeOffset > tokenStartEditor) {\n if (bias === \"end\") {\n return tokenStartBody + rawLength;\n }\n return tokenStartBody;\n }\n break;\n }\n return safeOffset + diffBefore;\n}\nfunction bodyOffsetToEditorOffset(bodyOffset, bias) {\n const editorContent = typeof editorState.markdownCurrentEditorContent === \"string\" ? editorState.markdownCurrentEditorContent : \"\";\n const safeBodyOffset = Math.max(0, Math.trunc(bodyOffset || 0));\n const tokenPattern = getMarkdownRawBlockTokenPattern();\n let diffBefore = 0;\n let match = tokenPattern.exec(editorContent);\n while (match) {\n const token = match[0];\n const tokenStartEditor = match.index;\n const tokenEndEditor = tokenStartEditor + token.length;\n const rawLength = getMarkdownRawBlockLength(Number(match[1]));\n const tokenStartBody = tokenStartEditor + diffBefore;\n const tokenEndBody = tokenStartBody + rawLength;\n if (safeBodyOffset > tokenEndBody) {\n diffBefore += rawLength - token.length;\n match = tokenPattern.exec(editorContent);\n continue;\n }\n if (safeBodyOffset >= tokenStartBody && safeBodyOffset <= tokenEndBody) {\n if (bias === \"end\") {\n return tokenEndEditor;\n }\n return tokenStartEditor;\n }\n const mappedOffset2 = safeBodyOffset - diffBefore;\n return Math.max(0, Math.min(editorContent.length, mappedOffset2));\n }\n const mappedOffset = safeBodyOffset - diffBefore;\n return Math.max(0, Math.min(editorContent.length, mappedOffset));\n}\nfunction editorOffsetToSourceOffset(renderedOffset, bias) {\n const frontmatterLength = typeof editorState.markdownFrontmatter === \"string\" ? editorState.markdownFrontmatter.length : 0;\n const editorOffset = renderedOffsetToEditorOffset(renderedOffset);\n const bodyOffset = editorOffsetToBodyOffset(editorOffset, bias);\n return Math.max(0, frontmatterLength + bodyOffset);\n}\nfunction buildEditorRenderedMaps(editorContent, renderedText) {\n let editorTrailing = 0;\n for (let i = editorContent.length - 1; i >= 0 && editorContent[i] === \"\\n\"; i--) {\n editorTrailing++;\n }\n let renderedTrailing = 0;\n for (let i = renderedText.length - 1; i >= 0 && renderedText[i] === \"\\n\"; i--) {\n renderedTrailing++;\n }\n const excessNewlines = Math.max(0, renderedTrailing - editorTrailing);\n const trimmed = excessNewlines > 0 ? renderedText.slice(0, renderedText.length - excessNewlines) : renderedText;\n const e2r = new Array(editorContent.length + 1);\n const r2e = new Array(trimmed.length + 1);\n let ri = 0;\n for (let si = 0; si < editorContent.length; si++) {\n if (ri < trimmed.length && editorContent[si] === trimmed[ri]) {\n e2r[si] = ri;\n r2e[ri] = si;\n ri++;\n } else {\n if (ri < trimmed.length && trimmed[ri] === \"\\n\") {\n let tempRi = ri;\n while (tempRi < trimmed.length && trimmed[tempRi] === \"\\n\") {\n tempRi++;\n }\n if (tempRi < trimmed.length && editorContent[si] === trimmed[tempRi]) {\n for (let k = ri; k < tempRi; k++) {\n if (r2e[k] === void 0)\n r2e[k] = si;\n }\n ri = tempRi;\n e2r[si] = ri;\n r2e[ri] = si;\n ri++;\n continue;\n }\n }\n e2r[si] = ri;\n }\n }\n if (ri < trimmed.length) {\n console.warn(\n \"[StudioBridge] Offset mapping divergence: rendered text has\",\n trimmed.length - ri,\n \"unconsumed characters starting at index\",\n ri\n );\n }\n e2r[editorContent.length] = Math.min(ri, trimmed.length);\n r2e[trimmed.length] = editorContent.length;\n for (let r = ri; r < trimmed.length; r++) {\n if (r2e[r] === void 0) {\n r2e[r] = editorContent.length;\n }\n }\n let lastSrc = 0;\n for (let r = 0; r <= trimmed.length; r++) {\n if (r2e[r] !== void 0) {\n lastSrc = r2e[r];\n } else {\n r2e[r] = lastSrc;\n }\n }\n return { editorToRendered: e2r, renderedToEditor: r2e };\n}\nfunction editorOffsetToRenderedOffset(editorOffset) {\n const map = editorState.markdownEditorToRenderedMap;\n if (!map || map.length === 0) {\n return editorOffset;\n }\n const idx = Math.max(0, Math.min(map.length - 1, Math.trunc(editorOffset || 0)));\n return map[idx] ?? editorOffset;\n}\nfunction renderedOffsetToEditorOffset(renderedOffset) {\n const map = editorState.markdownRenderedToEditorMap;\n if (!map || map.length === 0) {\n return renderedOffset;\n }\n const idx = Math.max(0, Math.min(map.length - 1, Math.trunc(renderedOffset || 0)));\n return map[idx] ?? renderedOffset;\n}\nfunction sourceSelectionToEditorRange(start, end) {\n const frontmatterLength = typeof editorState.markdownFrontmatter === \"string\" ? editorState.markdownFrontmatter.length : 0;\n const safeStart = Math.max(0, Math.trunc(start || 0));\n const safeEnd = Math.max(0, Math.trunc(end || 0));\n const sourceStart = Math.min(safeStart, safeEnd);\n const sourceEnd = Math.max(safeStart, safeEnd);\n if (sourceEnd <= frontmatterLength) {\n return null;\n }\n const bodyStart = Math.max(0, sourceStart - frontmatterLength);\n const bodyEnd = Math.max(0, sourceEnd - frontmatterLength);\n const editorStart = bodyOffsetToEditorOffset(bodyStart, \"start\");\n const editorEnd = bodyOffsetToEditorOffset(bodyEnd, \"end\");\n const renderedStart = editorOffsetToRenderedOffset(editorStart);\n const renderedEnd = editorOffsetToRenderedOffset(editorEnd);\n return {\n start: Math.max(0, Math.min(renderedStart, renderedEnd)),\n end: Math.max(0, Math.max(renderedStart, renderedEnd))\n };\n}\nfunction setMarkdownEditorSelection(start, end) {\n const safeStart = Math.max(0, Math.trunc(start || 0));\n const endValue = typeof end === \"number\" ? end : safeStart;\n const safeEnd = Math.max(0, Math.trunc(endValue));\n if (editorState.markdownLexicalApi && editorState.markdownEditorSurface) {\n const selection = window.getSelection();\n if (!selection) {\n return;\n }\n const anchor = resolveMarkdownTextPoint(\n editorState.markdownEditorSurface,\n safeStart\n );\n const focus = resolveMarkdownTextPoint(\n editorState.markdownEditorSurface,\n safeEnd\n );\n try {\n const range = document.createRange();\n range.setStart(anchor.node, anchor.offset);\n range.setEnd(focus.node, focus.offset);\n selection.removeAllRanges();\n selection.addRange(range);\n } catch {\n }\n return;\n }\n if (editorState.markdownEditorTextarea) {\n const max = editorState.markdownEditorTextarea.value.length;\n editorState.markdownEditorTextarea.setSelectionRange(\n Math.min(safeStart, max),\n Math.min(safeEnd, max)\n );\n }\n}\nfunction scheduleMarkdownSelectionSync() {\n if (!editorState.markdownFileId) {\n return;\n }\n if (editorState.markdownSelectionSyncTimer) {\n clearTimeout(editorState.markdownSelectionSyncTimer);\n }\n editorState.markdownSelectionSyncTimer = setTimeout(function() {\n const selection = getMarkdownEditorSelection();\n if (!selection) {\n editorState.markdownPendingSelection = null;\n if (editorState.markdownYProvider) {\n editorState.markdownYProvider.awareness.setLocalStateField(\"selection\", null);\n }\n return;\n }\n const start = editorOffsetToSourceOffset(selection.start, \"start\");\n const end = editorOffsetToSourceOffset(selection.end, \"end\");\n if (editorState.markdownYjsConnected && editorState.markdownYText && editorState.markdownYjsY && editorState.markdownYProvider) {\n const clampedStart = Math.max(\n 0,\n Math.min(editorState.markdownYText.length, start)\n );\n const clampedEnd = Math.max(\n 0,\n Math.min(editorState.markdownYText.length, end)\n );\n editorState.markdownYProvider.awareness.setLocalStateField(\"selection\", [\n {\n anchor: editorState.markdownYjsY.createRelativePositionFromTypeIndex(\n editorState.markdownYText,\n clampedStart\n ),\n marker: editorState.markdownYjsY.createRelativePositionFromTypeIndex(\n editorState.markdownYText,\n clampedEnd\n )\n }\n ]);\n editorState.markdownPendingSelection = null;\n } else {\n editorState.markdownPendingSelection = { start, end };\n }\n }, 80);\n}\nfunction clearMarkdownSelectionSync() {\n if (editorState.markdownSelectionSyncTimer) {\n clearTimeout(editorState.markdownSelectionSyncTimer);\n editorState.markdownSelectionSyncTimer = null;\n }\n editorState.markdownPendingSelection = null;\n if (editorState.markdownYProvider) {\n editorState.markdownYProvider.awareness.setLocalStateField(\"selection\", null);\n }\n}\nfunction clearMarkdownSelectionOverlay() {\n if (!editorState.markdownSelectionOverlayRoot) {\n return;\n }\n editorState.markdownSelectionOverlayRoot.textContent = \"\";\n editorState.markdownSelectionOverlayRoot.style.display = \"none\";\n}\nfunction resolveMarkdownTextPoint(root, rawOffset) {\n const offset = Math.max(0, Math.trunc(rawOffset || 0));\n if (offset === 0) {\n const walker2 = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);\n const first = walker2.nextNode();\n return first ? { node: first, offset: 0 } : { node: root, offset: 0 };\n }\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);\n const measureRange = document.createRange();\n measureRange.setStart(root, 0);\n let lastTextNode = null;\n let node = walker.nextNode();\n while (node) {\n lastTextNode = node;\n const textLength = node.textContent ? node.textContent.length : 0;\n measureRange.setEnd(node, textLength);\n const cumulative = measureRange.toString().length;\n if (cumulative >= offset) {\n const nodeStart = cumulative - textLength;\n const localOffset = offset - nodeStart;\n return { node, offset: Math.max(0, Math.min(localOffset, textLength)) };\n }\n node = walker.nextNode();\n }\n if (lastTextNode) {\n const textLength = lastTextNode.textContent ? lastTextNode.textContent.length : 0;\n return { node: lastTextNode, offset: textLength };\n }\n return {\n node: root,\n offset: offset > 0 ? root.childNodes.length : 0\n };\n}\nfunction createMarkdownEditorRange(start, end) {\n if (!editorState.markdownEditorSurface) {\n return null;\n }\n const safeStart = Math.max(0, Math.min(start, end));\n const safeEnd = Math.max(0, Math.max(start, end));\n const startPoint = resolveMarkdownTextPoint(\n editorState.markdownEditorSurface,\n safeStart\n );\n const endPoint = resolveMarkdownTextPoint(\n editorState.markdownEditorSurface,\n safeEnd\n );\n try {\n const range = document.createRange();\n range.setStart(startPoint.node, startPoint.offset);\n range.setEnd(endPoint.node, endPoint.offset);\n return range;\n } catch {\n return null;\n }\n}\nfunction toMarkdownOverlayRect(rect, surfaceRect) {\n const rawLeft = rect.left - surfaceRect.left;\n const rawTop = rect.top - surfaceRect.top;\n const rawRight = rawLeft + rect.width;\n const rawBottom = rawTop + rect.height;\n const left = Math.max(0, rawLeft);\n const top = Math.max(0, rawTop);\n const right = Math.min(surfaceRect.width, rawRight);\n const bottom = Math.min(surfaceRect.height, rawBottom);\n const width = right - left;\n const height = bottom - top;\n if (width <= 0 || height <= 0) {\n return null;\n }\n return {\n left,\n top,\n width,\n height\n };\n}\nfunction renderMarkdownSelectionOverlay() {\n if (!editorState.markdownSelectionOverlayRoot) {\n return;\n }\n if (!editorState.markdownEditorRoot || editorState.markdownEditorRoot.style.display !== \"block\" || !editorState.markdownEditorSurface || !editorState.markdownLexicalApi || !Array.isArray(editorState.markdownOverlaySelections) || editorState.markdownOverlaySelections.length === 0) {\n clearMarkdownSelectionOverlay();\n return;\n }\n const surfaceRect = editorState.markdownEditorSurface.getBoundingClientRect();\n if (surfaceRect.width <= 0 || surfaceRect.height <= 0) {\n clearMarkdownSelectionOverlay();\n return;\n }\n const computedStyle = window.getComputedStyle(editorState.markdownEditorSurface);\n const lineHeight = Math.max(\n 14,\n Number.parseFloat(computedStyle.lineHeight || \"0\") || 22\n );\n editorState.markdownSelectionOverlayRoot.textContent = \"\";\n editorState.markdownSelectionOverlayRoot.style.display = \"block\";\n for (const selection of editorState.markdownOverlaySelections) {\n if (!selection || typeof selection.start !== \"number\" || typeof selection.end !== \"number\") {\n continue;\n }\n const range = createMarkdownEditorRange(selection.start, selection.end);\n if (!range) {\n continue;\n }\n const color = typeof selection.color === \"string\" && selection.color ? selection.color : \"#6b7280\";\n const name = typeof selection.name === \"string\" && selection.name ? selection.name : \"Anonymous\";\n let labelAnchor = null;\n if (selection.start === selection.end) {\n const caretRect = range.getBoundingClientRect();\n const clippedCaret = toMarkdownOverlayRect(\n {\n left: caretRect.left,\n top: caretRect.top,\n width: 2,\n height: Math.max(caretRect.height, lineHeight)\n },\n surfaceRect\n );\n if (!clippedCaret) {\n continue;\n }\n const caret = document.createElement(\"div\");\n caret.className = \"vf-markdown-editor__selection-caret\";\n caret.setAttribute(DATA_VF_IGNORE, \"true\");\n caret.style.left = clippedCaret.left + \"px\";\n caret.style.top = clippedCaret.top + \"px\";\n caret.style.height = clippedCaret.height + \"px\";\n caret.style.background = color;\n editorState.markdownSelectionOverlayRoot.appendChild(caret);\n labelAnchor = { left: clippedCaret.left, top: clippedCaret.top };\n } else {\n const rectList = Array.from(range.getClientRects());\n for (const rect of rectList) {\n const clippedRect = toMarkdownOverlayRect(rect, surfaceRect);\n if (!clippedRect) {\n continue;\n }\n const highlight = document.createElement(\"div\");\n highlight.className = \"vf-markdown-editor__selection-highlight\";\n highlight.setAttribute(DATA_VF_IGNORE, \"true\");\n highlight.style.left = clippedRect.left + \"px\";\n highlight.style.top = clippedRect.top + \"px\";\n highlight.style.width = clippedRect.width + \"px\";\n highlight.style.height = clippedRect.height + \"px\";\n highlight.style.background = color;\n editorState.markdownSelectionOverlayRoot.appendChild(highlight);\n if (!labelAnchor) {\n labelAnchor = { left: clippedRect.left, top: clippedRect.top };\n }\n }\n }\n if (!labelAnchor) {\n continue;\n }\n const label = document.createElement(\"div\");\n label.className = \"vf-markdown-editor__selection-label\";\n label.setAttribute(DATA_VF_IGNORE, \"true\");\n label.textContent = name;\n label.style.left = labelAnchor.left + \"px\";\n label.style.top = labelAnchor.top + \"px\";\n label.style.background = color;\n editorState.markdownSelectionOverlayRoot.appendChild(label);\n }\n if (editorState.markdownSelectionOverlayRoot.childNodes.length === 0) {\n clearMarkdownSelectionOverlay();\n }\n}\nfunction scheduleMarkdownSelectionOverlayRender() {\n if (editorState.markdownSelectionOverlayRenderFrame) {\n cancelAnimationFrame(editorState.markdownSelectionOverlayRenderFrame);\n }\n editorState.markdownSelectionOverlayRenderFrame = requestAnimationFrame(\n function() {\n editorState.markdownSelectionOverlayRenderFrame = null;\n renderMarkdownSelectionOverlay();\n }\n );\n}\n\n// src/studio/bridge/bridge-slash-menu.ts\nfunction hideMarkdownSlashMenu() {\n editorState.markdownSlashMenuContext = null;\n editorState.markdownSlashMenuCommands = [];\n editorState.markdownSlashMenuActiveIndex = 0;\n if (!editorState.markdownSlashMenuRoot) {\n return;\n }\n editorState.markdownSlashMenuRoot.style.display = \"none\";\n editorState.markdownSlashMenuRoot.textContent = \"\";\n}\nfunction getMarkdownSlashCommandInsert(id, indent) {\n const prefix = typeof indent === \"string\" ? indent : \"\";\n if (id === \"text\") {\n const text = prefix;\n return { text, caretOffset: text.length };\n }\n if (id === \"heading-1\") {\n const text = prefix + \"# \";\n return { text, caretOffset: text.length };\n }\n if (id === \"heading-2\") {\n const text = prefix + \"## \";\n return { text, caretOffset: text.length };\n }\n if (id === \"heading-3\") {\n const text = prefix + \"### \";\n return { text, caretOffset: text.length };\n }\n if (id === \"bulleted-list\") {\n const text = prefix + \"- \";\n return { text, caretOffset: text.length };\n }\n if (id === \"numbered-list\") {\n const text = prefix + \"1. \";\n return { text, caretOffset: text.length };\n }\n if (id === \"quote-block\") {\n const text = prefix + \"> \";\n return { text, caretOffset: text.length };\n }\n if (id === \"code-block\") {\n const fence = String.fromCharCode(96, 96, 96);\n const text = prefix + fence + \"\\n\" + prefix + \"\\n\" + prefix + fence;\n return {\n text,\n caretOffset: (prefix + fence + \"\\n\" + prefix).length\n };\n }\n if (id === \"image\") {\n const text = prefix + \"\";\n return {\n text,\n caretOffset: (prefix + \".length\n };\n }\n return null;\n}\nfunction applyMarkdownSlashCommand(index) {\n if (!editorState.markdownSlashMenuContext || editorState.markdownSlashMenuCommands.length === 0) {\n return false;\n }\n const command = editorState.markdownSlashMenuCommands[index];\n if (!command) {\n return false;\n }\n const insert = getMarkdownSlashCommandInsert(command.id, editorState.markdownSlashMenuContext.indent);\n if (!insert) {\n return false;\n }\n const editorContent = typeof editorState.markdownCurrentEditorContent === \"string\" ? editorState.markdownCurrentEditorContent : \"\";\n const before = editorContent.slice(0, editorState.markdownSlashMenuContext.lineStart);\n const after = editorContent.slice(editorState.markdownSlashMenuContext.caret);\n const nextEditorContent = before + insert.text + after;\n const nextCaret = before.length + insert.caretOffset;\n const nextFullContent = composeMarkdownContent(restoreRawBlocksFromEditor(nextEditorContent));\n const hasChanged = nextFullContent !== editorState.markdownCurrentContent;\n applyMarkdownContent(nextFullContent);\n if (hasChanged) {\n editorState.markdownHasUnsavedChanges = true;\n scheduleMarkdownSync(nextFullContent);\n }\n setTimeout(function() {\n focusMarkdownEditor();\n setMarkdownEditorSelection(nextCaret, nextCaret);\n scheduleMarkdownSelectionSync();\n scheduleMarkdownSelectionOverlayRender();\n scheduleMarkdownSlashMenuUpdate();\n }, 0);\n hideMarkdownSlashMenu();\n return true;\n}\nfunction renderMarkdownSlashMenu() {\n if (!editorState.markdownSlashMenuRoot || !editorState.markdownSlashMenuContext || editorState.markdownSlashMenuCommands.length === 0) {\n hideMarkdownSlashMenu();\n return;\n }\n editorState.markdownSlashMenuRoot.textContent = \"\";\n const maxLeft = Math.max(8, window.innerWidth - 312);\n const maxTop = Math.max(8, window.innerHeight - 320);\n const left = Math.max(8, Math.min(maxLeft, editorState.markdownSlashMenuContext.anchorLeft));\n const top = Math.max(8, Math.min(maxTop, editorState.markdownSlashMenuContext.anchorTop));\n editorState.markdownSlashMenuRoot.style.left = left + \"px\";\n editorState.markdownSlashMenuRoot.style.top = top + \"px\";\n editorState.markdownSlashMenuRoot.appendChild(\n el(\"div\", \"vf-markdown-editor__slash-section\", \"Basic blocks\")\n );\n editorState.markdownSlashMenuCommands.forEach(function(command, index) {\n const item = el(\"button\", \"vf-markdown-editor__slash-item\");\n item.type = \"button\";\n item.setAttribute(\n \"data-active\",\n index === editorState.markdownSlashMenuActiveIndex ? \"true\" : \"false\"\n );\n item.addEventListener(\"mousedown\", function(event) {\n event.preventDefault();\n });\n item.addEventListener(\"click\", function(event) {\n event.preventDefault();\n editorState.markdownSlashMenuActiveIndex = index;\n applyMarkdownSlashCommand(editorState.markdownSlashMenuActiveIndex);\n });\n item.appendChild(el(\"span\", \"vf-markdown-editor__slash-icon\", command.icon || \"\"));\n item.appendChild(el(\"span\", \"vf-markdown-editor__slash-item-title\", command.label));\n if (command.shortcut) {\n item.appendChild(el(\"span\", \"vf-markdown-editor__slash-shortcut\", command.shortcut));\n }\n editorState.markdownSlashMenuRoot.appendChild(item);\n });\n const footer = document.createElement(\"div\");\n footer.className = \"vf-markdown-editor__slash-footer\";\n const footerLabel = document.createElement(\"span\");\n footerLabel.textContent = \"Close menu\";\n footer.appendChild(footerLabel);\n footer.appendChild(el(\"span\", \"vf-markdown-editor__slash-footer-key\", \"esc\"));\n editorState.markdownSlashMenuRoot.appendChild(footer);\n editorState.markdownSlashMenuRoot.style.display = \"block\";\n const activeItem = editorState.markdownSlashMenuRoot.querySelector(\n '.vf-markdown-editor__slash-item[data-active=\"true\"]'\n );\n if (activeItem) {\n activeItem.scrollIntoView({ block: \"nearest\" });\n }\n}\nfunction updateMarkdownSlashMenu() {\n if (!editorState.markdownEditorRoot || editorState.markdownEditorRoot.style.display !== \"block\" || !editorState.markdownLexicalApi || !editorState.markdownEditorSurface || editorState.markdownEditorSurface.style.display === \"none\") {\n hideMarkdownSlashMenu();\n return;\n }\n const selection = getMarkdownEditorSelection();\n if (!selection || selection.start !== selection.end) {\n hideMarkdownSlashMenu();\n return;\n }\n const caret = selection.start;\n const editorContent = typeof editorState.markdownCurrentEditorContent === \"string\" ? editorState.markdownCurrentEditorContent : \"\";\n const lineStart = editorContent.lastIndexOf(\"\\n\", Math.max(0, caret - 1)) + 1;\n const line = editorContent.slice(lineStart, caret);\n const match = line.match(/^(\\s*)\\/([a-z0-9-]*)$/i);\n if (!match) {\n hideMarkdownSlashMenu();\n return;\n }\n const query = (match[2] || \"\").toLowerCase();\n const commands = MARKDOWN_SLASH_COMMANDS.filter(function(command) {\n if (!query) {\n return true;\n }\n if (command.label.toLowerCase().includes(query)) {\n return true;\n }\n return command.aliases.some(function(alias) {\n return alias.startsWith(query);\n });\n });\n if (commands.length === 0) {\n hideMarkdownSlashMenu();\n return;\n }\n const domSelection = window.getSelection();\n if (!domSelection || domSelection.rangeCount === 0) {\n hideMarkdownSlashMenu();\n return;\n }\n const caretRect = domSelection.getRangeAt(0).getBoundingClientRect();\n const anchorLeft = caretRect.left;\n const anchorTop = caretRect.bottom + 8;\n editorState.markdownSlashMenuCommands = commands.slice(0, 8);\n editorState.markdownSlashMenuActiveIndex = Math.max(\n 0,\n Math.min(editorState.markdownSlashMenuActiveIndex, editorState.markdownSlashMenuCommands.length - 1)\n );\n editorState.markdownSlashMenuContext = {\n lineStart,\n caret,\n indent: match[1] || \"\",\n query,\n anchorLeft,\n anchorTop\n };\n renderMarkdownSlashMenu();\n}\nfunction scheduleMarkdownSlashMenuUpdate() {\n if (editorState.markdownSlashMenuTimer) {\n clearTimeout(editorState.markdownSlashMenuTimer);\n }\n editorState.markdownSlashMenuTimer = setTimeout(function() {\n editorState.markdownSlashMenuTimer = null;\n updateMarkdownSlashMenu();\n }, 0);\n}\nfunction handleMarkdownSlashMenuKeydown(event) {\n if (!editorState.markdownSlashMenuRoot || editorState.markdownSlashMenuRoot.style.display !== \"block\" || editorState.markdownSlashMenuCommands.length === 0) {\n return false;\n }\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n editorState.markdownSlashMenuActiveIndex = (editorState.markdownSlashMenuActiveIndex + 1) % editorState.markdownSlashMenuCommands.length;\n renderMarkdownSlashMenu();\n return true;\n }\n if (event.key === \"ArrowUp\") {\n event.preventDefault();\n editorState.markdownSlashMenuActiveIndex = (editorState.markdownSlashMenuActiveIndex - 1 + editorState.markdownSlashMenuCommands.length) % editorState.markdownSlashMenuCommands.length;\n renderMarkdownSlashMenu();\n return true;\n }\n if (event.key === \"Enter\" || event.key === \"Tab\") {\n event.preventDefault();\n return applyMarkdownSlashCommand(editorState.markdownSlashMenuActiveIndex);\n }\n if (event.key === \"Escape\") {\n event.preventDefault();\n hideMarkdownSlashMenu();\n return true;\n }\n return false;\n}\n\n// src/studio/bridge/bridge-inline-toolbar.ts\nfunction hideMarkdownInlineToolbar() {\n if (!editorState.markdownInlineToolbarRoot) {\n return;\n }\n editorState.markdownInlineToolbarRoot.style.display = \"none\";\n const blockDropdown = editorState.markdownInlineToolbarRoot.querySelector(\n \".vf-markdown-editor__block-dropdown\"\n );\n if (blockDropdown) {\n blockDropdown.style.display = \"none\";\n }\n}\nfunction toggleMarkdownInlineFormat(format) {\n if (!editorState.markdownLexicalApi || !editorState.markdownLexicalApi.editor || !editorState.markdownLexicalApi.lexicalModule) {\n return;\n }\n if (typeof format !== \"string\" || !format) {\n return;\n }\n editorState.markdownLexicalApi.editor.focus();\n editorState.markdownLexicalApi.editor.dispatchCommand(\n editorState.markdownLexicalApi.lexicalModule.FORMAT_TEXT_COMMAND,\n format\n );\n scheduleMarkdownInlineToolbarUpdate();\n}\nfunction insertMarkdownLink() {\n if (!editorState.markdownLexicalApi || !editorState.markdownLexicalApi.editor || !editorState.markdownLexicalApi.lexicalModule) {\n return;\n }\n const api = editorState.markdownLexicalApi;\n api.editor.update(function() {\n const selection = api.lexicalModule.$getSelection();\n if (!selection || !api.lexicalModule.$isRangeSelection(selection)) {\n return;\n }\n const text = selection.getTextContent() || \"link text\";\n selection.insertRawText(\"[\" + text + \"](url)\");\n });\n scheduleMarkdownInlineToolbarUpdate();\n}\nfunction setMarkdownBlockType(type) {\n if (!editorState.markdownLexicalApi || !editorState.markdownLexicalApi.editor || !editorState.markdownLexicalApi.lexicalModule) {\n return;\n }\n const api = editorState.markdownLexicalApi;\n api.editor.update(function() {\n const selection = api.lexicalModule.$getSelection();\n if (!selection || !api.lexicalModule.$isRangeSelection(selection)) {\n return;\n }\n if (type === \"paragraph\") {\n api.selectionModule.$setBlocksType(selection, function() {\n return api.lexicalModule.$createParagraphNode();\n });\n } else if (type === \"h1\") {\n api.selectionModule.$setBlocksType(selection, function() {\n return api.richTextModule.$createHeadingNode(\"h1\");\n });\n } else if (type === \"h2\") {\n api.selectionModule.$setBlocksType(selection, function() {\n return api.richTextModule.$createHeadingNode(\"h2\");\n });\n } else if (type === \"h3\") {\n api.selectionModule.$setBlocksType(selection, function() {\n return api.richTextModule.$createHeadingNode(\"h3\");\n });\n } else if (type === \"quote\") {\n api.selectionModule.$setBlocksType(selection, function() {\n return api.richTextModule.$createQuoteNode();\n });\n } else if (type === \"bullet\") {\n api.editor.dispatchCommand(\n api.listModule.INSERT_UNORDERED_LIST_COMMAND,\n void 0\n );\n } else if (type === \"number\") {\n api.editor.dispatchCommand(\n api.listModule.INSERT_ORDERED_LIST_COMMAND,\n void 0\n );\n }\n });\n scheduleMarkdownInlineToolbarUpdate();\n}\nfunction getMarkdownToolbarState() {\n const toolbarState = {\n bold: false,\n italic: false,\n underline: false,\n strikethrough: false,\n code: false,\n blockType: \"paragraph\"\n };\n if (!editorState.markdownLexicalApi || !editorState.markdownLexicalApi.editor || !editorState.markdownLexicalApi.lexicalModule) {\n return toolbarState;\n }\n const api = editorState.markdownLexicalApi;\n api.editor.getEditorState().read(function() {\n const selection = api.lexicalModule.$getSelection();\n if (!selection || !api.lexicalModule.$isRangeSelection(selection)) {\n return;\n }\n toolbarState.bold = selection.hasFormat(\"bold\");\n toolbarState.italic = selection.hasFormat(\"italic\");\n toolbarState.underline = selection.hasFormat(\"underline\");\n toolbarState.strikethrough = selection.hasFormat(\"strikethrough\");\n toolbarState.code = selection.hasFormat(\"code\");\n let anchorNode = selection.anchor.getNode();\n let element = anchorNode;\n if (element.getType() === \"text\") {\n element = element.getParent();\n }\n if (!element) {\n toolbarState.blockType = \"paragraph\";\n return;\n }\n let node = element;\n while (node) {\n const nodeType = node.getType();\n if (nodeType === \"heading\") {\n toolbarState.blockType = node.getTag();\n return;\n }\n if (nodeType === \"quote\") {\n toolbarState.blockType = \"quote\";\n return;\n }\n if (nodeType === \"list\") {\n toolbarState.blockType = node.getListType() === \"number\" ? \"number\" : \"bullet\";\n return;\n }\n if (nodeType === \"root\") {\n break;\n }\n node = node.getParent();\n }\n toolbarState.blockType = \"paragraph\";\n });\n return toolbarState;\n}\nfunction updateMarkdownInlineToolbar() {\n if (!editorState.markdownInlineToolbarRoot || !editorState.markdownEditorRoot || editorState.markdownEditorRoot.style.display !== \"block\" || !editorState.markdownLexicalApi || !editorState.markdownEditorSurface || editorState.markdownEditorSurface.style.display === \"none\") {\n hideMarkdownInlineToolbar();\n return;\n }\n const selection = window.getSelection();\n if (!selection || selection.rangeCount === 0 || selection.isCollapsed) {\n hideMarkdownInlineToolbar();\n return;\n }\n const range = selection.getRangeAt(0);\n if (!editorState.markdownEditorSurface.contains(range.startContainer) || !editorState.markdownEditorSurface.contains(range.endContainer)) {\n hideMarkdownInlineToolbar();\n return;\n }\n const rect = range.getBoundingClientRect();\n if (!rect || rect.width <= 0 || rect.height <= 0) {\n hideMarkdownInlineToolbar();\n return;\n }\n const toolbarState = getMarkdownToolbarState();\n const buttons = editorState.markdownInlineToolbarRoot.querySelectorAll(\n \".vf-markdown-editor__inline-button[data-format]\"\n );\n for (const btn2 of buttons) {\n const fmt = btn2.getAttribute(\"data-format\");\n if (fmt && toolbarState[fmt]) {\n btn2.classList.add(\"active\");\n } else {\n btn2.classList.remove(\"active\");\n }\n }\n const blockTrigger = editorState.markdownInlineToolbarRoot.querySelector(\n \".vf-markdown-editor__block-trigger\"\n );\n if (blockTrigger) {\n const blockLabels = {\n paragraph: \"\\xB6\",\n h1: \"H\\u2081\",\n h2: \"H\\u2082\",\n h3: \"H\\u2083\",\n quote: \"\\u201C\",\n bullet: \"\\u2022\",\n number: \"1.\"\n };\n blockTrigger.textContent = blockLabels[toolbarState.blockType] || \"\\xB6\";\n }\n const blockDropdown = editorState.markdownInlineToolbarRoot.querySelector(\n \".vf-markdown-editor__block-dropdown\"\n );\n if (blockDropdown) {\n const options = blockDropdown.querySelectorAll(\n \".vf-markdown-editor__block-option\"\n );\n for (const opt of options) {\n if (opt.getAttribute(\"data-block-type\") === toolbarState.blockType) {\n opt.classList.add(\"active\");\n } else {\n opt.classList.remove(\"active\");\n }\n }\n }\n const left = Math.max(\n 8,\n Math.min(window.innerWidth - 220, rect.left + rect.width / 2 - 100)\n );\n const top = Math.max(\n 8,\n Math.min(window.innerHeight - 80, rect.top - 72)\n );\n editorState.markdownInlineToolbarRoot.style.left = left + \"px\";\n editorState.markdownInlineToolbarRoot.style.top = top + \"px\";\n editorState.markdownInlineToolbarRoot.style.display = \"flex\";\n}\nfunction scheduleMarkdownInlineToolbarUpdate() {\n if (editorState.markdownInlineToolbarFrame) {\n cancelAnimationFrame(editorState.markdownInlineToolbarFrame);\n }\n editorState.markdownInlineToolbarFrame = requestAnimationFrame(function() {\n editorState.markdownInlineToolbarFrame = null;\n updateMarkdownInlineToolbar();\n });\n}\n\n// src/studio/bridge/bridge-block-drag.ts\nfunction getMarkdownTopLevelBlocks() {\n if (!editorState.markdownEditorSurface) {\n return [];\n }\n return Array.from(editorState.markdownEditorSurface.children).filter(function(node) {\n return node && node.nodeType === Node.ELEMENT_NODE;\n });\n}\nfunction hideMarkdownBlockDragHandle() {\n editorState.markdownBlockHandleHoverIndex = -1;\n if (!editorState.markdownBlockDragHandle) {\n return;\n }\n editorState.markdownBlockDragHandle.style.display = \"none\";\n editorState.markdownBlockDragHandle.removeAttribute(\"data-block-index\");\n}\nfunction hideMarkdownBlockDropIndicator() {\n editorState.markdownBlockDropSlotIndex = -1;\n if (editorState.markdownBlockDropIndicator) {\n editorState.markdownBlockDropIndicator.style.display = \"none\";\n }\n if (editorState.markdownBlockDropLabel) {\n editorState.markdownBlockDropLabel.style.display = \"none\";\n editorState.markdownBlockDropLabel.textContent = \"\";\n }\n}\nfunction hideMarkdownBlockDragUi() {\n editorState.markdownBlockDragActive = false;\n editorState.markdownBlockDragSourceIndex = -1;\n if (editorState.markdownBlockDragHandle) {\n editorState.markdownBlockDragHandle.setAttribute(\"data-dragging\", \"false\");\n }\n removeMarkdownDragGhost();\n hideMarkdownBlockDropIndicator();\n hideMarkdownBlockDragHandle();\n}\nfunction getMarkdownBlockElementFromNode(node) {\n if (!editorState.markdownEditorSurface || !node) {\n return null;\n }\n let current = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;\n while (current && current.parentElement !== editorState.markdownEditorSurface) {\n current = current.parentElement;\n }\n if (!current || current.parentElement !== editorState.markdownEditorSurface) {\n return null;\n }\n return current;\n}\nfunction getMarkdownBlockTypeInfo(block) {\n if (!block || !block.tagName) {\n return { label: \"block\", color: \"#0081f8\" };\n }\n const tag = block.tagName.toLowerCase();\n if (tag === \"h1\") {\n return { label: \"heading 1\", color: \"#7c3aed\" };\n }\n if (tag === \"h2\") {\n return { label: \"heading 2\", color: \"#7c3aed\" };\n }\n if (tag === \"h3\") {\n return { label: \"heading 3\", color: \"#7c3aed\" };\n }\n if (tag === \"ul\" || tag === \"ol\") {\n return { label: \"list\", color: \"#0d9488\" };\n }\n if (tag === \"blockquote\") {\n return { label: \"quote\", color: \"#2563eb\" };\n }\n if (tag === \"pre\") {\n return { label: \"code block\", color: \"#ea580c\" };\n }\n if (tag === \"img\" || tag === \"figure\") {\n return { label: \"image\", color: \"#db2777\" };\n }\n if (tag === \"p\") {\n return { label: \"paragraph\", color: \"#16a34a\" };\n }\n return { label: tag, color: \"#0081f8\" };\n}\nfunction getMarkdownBlockPreviewText(block) {\n if (!block) {\n return \"\";\n }\n const text = String(block.textContent || \"\").replace(new RegExp(\"\\\\s+\", \"g\"), \" \").trim();\n if (!text) {\n return \"Empty block\";\n }\n if (text.length > 84) {\n return text.slice(0, 84) + \"...\";\n }\n return text;\n}\nfunction removeMarkdownDragGhost() {\n if (!editorState.markdownBlockDragGhost) {\n return;\n }\n editorState.markdownBlockDragGhost.remove();\n editorState.markdownBlockDragGhost = null;\n}\nfunction createMarkdownDragGhost(block) {\n const typeInfo = getMarkdownBlockTypeInfo(block);\n const ghost = el(\"div\", \"vf-markdown-editor__block-drag-ghost\");\n const title = el(\n \"span\",\n \"vf-markdown-editor__block-drag-ghost-title\",\n \"Moving \" + typeInfo.label\n );\n const text = el(\n \"span\",\n \"vf-markdown-editor__block-drag-ghost-text\",\n getMarkdownBlockPreviewText(block)\n );\n ghost.appendChild(title);\n ghost.appendChild(text);\n return ghost;\n}\nfunction getMdxBlockOpenUiState(block) {\n const hasResolvedTarget = !!(block && typeof block.filePath === \"string\" && block.filePath.trim());\n return {\n hasResolvedTarget,\n buttonLabel: hasResolvedTarget ? \"Edit in Studio\" : \"Open MDX source\",\n showUnresolvedNote: !hasResolvedTarget\n };\n}\nfunction setMarkdownMdxBlocks(blocks) {\n const PAGE_PATH = getConfig().pagePath;\n editorState.markdownLatestMdxBlocks = Array.isArray(blocks) ? blocks : [];\n if (!editorState.markdownMdxBlocksRoot) {\n return;\n }\n editorState.markdownMdxBlocksRoot.textContent = \"\";\n if (!isMdxPage() || editorState.markdownLatestMdxBlocks.length === 0) {\n editorState.markdownMdxBlocksRoot.style.display = \"none\";\n return;\n }\n editorState.markdownMdxBlocksRoot.style.display = \"flex\";\n for (const block of editorState.markdownLatestMdxBlocks.slice(0, 8)) {\n if (!block || typeof block.label !== \"string\") {\n continue;\n }\n const item = el(\"div\", \"vf-markdown-editor__mdx-block\");\n const safeLine = Number.isFinite(block.lineNumber) ? Math.max(1, Math.trunc(block.lineNumber)) : 1;\n const label = el(\n \"div\",\n \"vf-markdown-editor__mdx-block-label\",\n block.label + \" (line \" + String(safeLine) + \")\"\n );\n const openUiState = getMdxBlockOpenUiState(block);\n const openButton = btn(\"vf-markdown-editor__mdx-open\", openUiState.buttonLabel, function() {\n const targetFile = typeof block.filePath === \"string\" && block.filePath ? block.filePath : PAGE_PATH;\n const targetLine = targetFile === PAGE_PATH ? safeLine : 1;\n const targetSymbol = targetFile === PAGE_PATH ? \"\" : typeof block.symbolName === \"string\" ? block.symbolName : \"\";\n openFilePathInStudio(targetFile, targetLine, targetSymbol);\n });\n if (openUiState.showUnresolvedNote) {\n openButton.title = \"Component import could not be resolved. Opening current MDX source.\";\n }\n item.appendChild(label);\n if (openUiState.showUnresolvedNote) {\n item.appendChild(el(\"span\", \"vf-markdown-editor__mdx-note\", \"Unresolved import\"));\n }\n item.appendChild(openButton);\n editorState.markdownMdxBlocksRoot.appendChild(item);\n }\n if (editorState.markdownMdxBlocksRoot.childNodes.length === 0) {\n editorState.markdownMdxBlocksRoot.style.display = \"none\";\n }\n}\nfunction getMarkdownBlockHoverIndexFromPointer(targetNode, clientX, clientY) {\n const blocks = getMarkdownTopLevelBlocks();\n if (blocks.length === 0) {\n return -1;\n }\n const directBlock = getMarkdownBlockElementFromNode(targetNode);\n const directIndex = blocks.indexOf(directBlock);\n if (directIndex >= 0) {\n return directIndex;\n }\n if (!editorState.markdownEditorSurface) {\n return -1;\n }\n const surfaceRect = editorState.markdownEditorSurface.getBoundingClientRect();\n const leftBoundary = surfaceRect.left - 44;\n const rightBoundary = surfaceRect.left + Math.min(96, surfaceRect.width * 0.35);\n if (clientX < leftBoundary || clientX > rightBoundary) {\n return -1;\n }\n for (let i = 0; i < blocks.length; i += 1) {\n const rect = blocks[i].getBoundingClientRect();\n if (clientY >= rect.top - 4 && clientY <= rect.bottom + 4) {\n return i;\n }\n }\n const firstRect = blocks[0].getBoundingClientRect();\n const lastRect = blocks[blocks.length - 1].getBoundingClientRect();\n if (clientY < firstRect.top) {\n return 0;\n }\n if (clientY > lastRect.bottom) {\n return blocks.length - 1;\n }\n return -1;\n}\nfunction positionMarkdownBlockDragHandle(block, index) {\n if (!editorState.markdownBlockDragHandle || !block || !editorState.markdownEditorRoot || editorState.markdownEditorRoot.style.display !== \"block\") {\n hideMarkdownBlockDragHandle();\n return;\n }\n const rect = block.getBoundingClientRect();\n const surfaceRect = editorState.markdownEditorSurface ? editorState.markdownEditorSurface.getBoundingClientRect() : null;\n if (!surfaceRect || rect.width <= 0 || rect.height <= 0) {\n hideMarkdownBlockDragHandle();\n return;\n }\n if (rect.bottom < surfaceRect.top || rect.top > surfaceRect.bottom) {\n hideMarkdownBlockDragHandle();\n return;\n }\n const left = Math.max(6, rect.left - 36);\n const top = Math.max(6, rect.top + 1);\n editorState.markdownBlockDragHandle.style.left = left + \"px\";\n editorState.markdownBlockDragHandle.style.top = top + \"px\";\n editorState.markdownBlockDragHandle.style.display = \"block\";\n editorState.markdownBlockDragHandle.setAttribute(\"data-block-index\", String(index));\n editorState.markdownBlockHandleHoverIndex = index;\n}\nfunction refreshMarkdownBlockDragHandlePosition() {\n if (editorState.markdownBlockDragActive || editorState.markdownBlockHandleHoverIndex < 0) {\n return;\n }\n const blocks = getMarkdownTopLevelBlocks();\n const block = blocks[editorState.markdownBlockHandleHoverIndex];\n if (!block) {\n hideMarkdownBlockDragHandle();\n return;\n }\n positionMarkdownBlockDragHandle(block, editorState.markdownBlockHandleHoverIndex);\n}\nfunction getMarkdownDropSlotIndexFromPointer(clientY) {\n const blocks = getMarkdownTopLevelBlocks();\n if (blocks.length === 0) {\n return -1;\n }\n for (let i = 0; i < blocks.length; i += 1) {\n const rect = blocks[i].getBoundingClientRect();\n const midpoint = rect.top + rect.height / 2;\n if (clientY < midpoint) {\n return i;\n }\n }\n return blocks.length;\n}\nfunction autoScrollMarkdownSurfaceDuringDrag(clientY) {\n if (!editorState.markdownEditorSurface) {\n return;\n }\n const rect = editorState.markdownEditorSurface.getBoundingClientRect();\n const threshold = 42;\n const maxStep = 20;\n let delta = 0;\n if (clientY < rect.top + threshold) {\n const distance = rect.top + threshold - clientY;\n delta = -Math.min(maxStep, Math.max(2, Math.floor(distance / 3)));\n } else if (clientY > rect.bottom - threshold) {\n const distance = clientY - (rect.bottom - threshold);\n delta = Math.min(maxStep, Math.max(2, Math.floor(distance / 3)));\n }\n if (delta !== 0) {\n editorState.markdownEditorSurface.scrollTop += delta;\n refreshMarkdownBlockDragHandlePosition();\n }\n}\nfunction showMarkdownBlockDropIndicator(slotIndex) {\n if (!editorState.markdownBlockDropIndicator || !editorState.markdownEditorSurface) {\n return;\n }\n const blocks = getMarkdownTopLevelBlocks();\n if (blocks.length === 0) {\n hideMarkdownBlockDropIndicator();\n return;\n }\n const safeSlot = Math.max(0, Math.min(blocks.length, Math.trunc(slotIndex || 0)));\n const surfaceRect = editorState.markdownEditorSurface.getBoundingClientRect();\n let top;\n if (safeSlot >= blocks.length) {\n const lastRect = blocks[blocks.length - 1].getBoundingClientRect();\n top = lastRect.bottom + 1;\n } else {\n const rect = blocks[safeSlot].getBoundingClientRect();\n top = rect.top - 1;\n }\n editorState.markdownBlockDropIndicator.style.left = Math.max(8, surfaceRect.left + 8) + \"px\";\n editorState.markdownBlockDropIndicator.style.top = Math.max(8, top) + \"px\";\n editorState.markdownBlockDropIndicator.style.width = Math.max(40, surfaceRect.width - 16) + \"px\";\n editorState.markdownBlockDropIndicator.style.display = \"block\";\n editorState.markdownBlockDropSlotIndex = safeSlot;\n const dropType = safeSlot >= blocks.length ? { label: \"end of document\", color: \"#0284c7\" } : getMarkdownBlockTypeInfo(blocks[safeSlot]);\n editorState.markdownBlockDropIndicator.style.background = dropType.color;\n editorState.markdownBlockDropIndicator.style.boxShadow = \"0 1px 6px \" + dropType.color;\n if (editorState.markdownBlockDropLabel) {\n editorState.markdownBlockDropLabel.textContent = safeSlot >= blocks.length ? \"Drop at end\" : \"Drop before \" + dropType.label;\n editorState.markdownBlockDropLabel.style.left = Math.max(8, surfaceRect.left + 8) + \"px\";\n editorState.markdownBlockDropLabel.style.top = Math.max(8, top - 26) + \"px\";\n editorState.markdownBlockDropLabel.style.borderColor = dropType.color;\n editorState.markdownBlockDropLabel.style.display = \"block\";\n }\n}\nfunction moveMarkdownLexicalBlock(sourceIndex, targetSlotIndex) {\n if (!editorState.markdownLexicalApi || !editorState.markdownLexicalApi.editor || !editorState.markdownLexicalApi.lexicalModule) {\n return false;\n }\n const source = Math.trunc(sourceIndex);\n const targetSlot = Math.trunc(targetSlotIndex);\n if (!Number.isInteger(source) || !Number.isInteger(targetSlot)) {\n return false;\n }\n let didMove = false;\n const lexApi = editorState.markdownLexicalApi;\n lexApi.editor.update(function() {\n const root = lexApi.lexicalModule.$getRoot();\n const children = root.getChildren();\n const maxSlot = children.length;\n if (source < 0 || source >= maxSlot || targetSlot < 0 || targetSlot > maxSlot) {\n return;\n }\n let adjustedSlot = targetSlot;\n if (source < adjustedSlot) {\n adjustedSlot -= 1;\n }\n if (adjustedSlot === source) {\n return;\n }\n const node = children[source];\n node.remove();\n const afterRemoval = root.getChildren();\n if (adjustedSlot >= afterRemoval.length) {\n root.append(node);\n } else {\n afterRemoval[adjustedSlot].insertBefore(node);\n }\n didMove = true;\n });\n if (didMove) {\n scheduleMarkdownSelectionSync();\n scheduleMarkdownSelectionOverlayRender();\n scheduleMarkdownSlashMenuUpdate();\n scheduleMarkdownInlineToolbarUpdate();\n }\n return didMove;\n}\nfunction getMarkdownCurrentBlockIndexFromSelection() {\n if (!editorState.markdownEditorSurface) {\n return -1;\n }\n const selection = window.getSelection();\n if (!selection || selection.rangeCount === 0) {\n return -1;\n }\n const range = selection.getRangeAt(0);\n if (!editorState.markdownEditorSurface.contains(range.startContainer)) {\n return -1;\n }\n const block = getMarkdownBlockElementFromNode(range.startContainer);\n if (!block) {\n return -1;\n }\n const blocks = getMarkdownTopLevelBlocks();\n return blocks.indexOf(block);\n}\nfunction moveMarkdownCurrentBlockByDelta(delta) {\n const blocks = getMarkdownTopLevelBlocks();\n if (blocks.length <= 1) {\n return false;\n }\n const index = getMarkdownCurrentBlockIndexFromSelection();\n if (index < 0) {\n return false;\n }\n const step = Math.sign(delta);\n if (step === 0) {\n return false;\n }\n let targetSlot;\n if (step < 0) {\n targetSlot = Math.max(0, index - 1);\n } else {\n targetSlot = Math.min(blocks.length, index + 2);\n }\n const moved = moveMarkdownLexicalBlock(index, targetSlot);\n if (moved) {\n setTimeout(function() {\n const nextBlocks = getMarkdownTopLevelBlocks();\n const nextIndex = Math.max(0, Math.min(nextBlocks.length - 1, index + step));\n const nextBlock = nextBlocks[nextIndex];\n if (nextBlock) {\n positionMarkdownBlockDragHandle(nextBlock, nextIndex);\n }\n }, 0);\n }\n return moved;\n}\n\n// src/studio/bridge/bridge-markdown-editor.ts\nfunction setupMarkdownLexicalEditor() {\n if (!editorState.markdownEditorSurface || editorState.markdownLexicalApi || editorState.markdownLexicalSetupPromise) {\n return;\n }\n editorState.markdownLexicalSetupPromise = Promise.all([\n import(\"https://esm.sh/lexical@0.21.0?target=es2022\"),\n import(\"https://esm.sh/@lexical/rich-text@0.21.0?target=es2022\"),\n import(\"https://esm.sh/@lexical/list@0.21.0?target=es2022\"),\n import(\"https://esm.sh/@lexical/markdown@0.21.0?target=es2022\"),\n import(\"https://esm.sh/@lexical/history@0.21.0?target=es2022\"),\n import(\"https://esm.sh/@lexical/selection@0.21.0?target=es2022\")\n ]).then(function(modules) {\n if (!editorState.markdownEditorSurface) {\n return;\n }\n const lexicalModule = modules[0];\n const richTextModule = modules[1];\n const listModule = modules[2];\n const markdownModule = modules[3];\n const historyModule = modules[4];\n const selectionModule = modules[5];\n const editor = lexicalModule.createEditor({\n namespace: \"veryfront-markdown-preview\",\n nodes: [\n richTextModule.HeadingNode,\n richTextModule.QuoteNode,\n listModule.ListNode,\n listModule.ListItemNode\n ],\n onError: function(error) {\n console.error(\"[StudioBridge] Markdown Lexical error\", error);\n }\n });\n const unregisterRichText = richTextModule.registerRichText(editor);\n const unregisterList = listModule.registerList(editor);\n const unregisterHistory = historyModule.registerHistory(\n editor,\n historyModule.createEmptyHistoryState(),\n 1e3\n );\n const unregisterUpdate = editor.registerUpdateListener(function(update) {\n if (editorState.markdownApplyingRemoteUpdate) {\n editorState.markdownPendingLocalReconcile = true;\n return;\n }\n let nextContent = \"\";\n update.editorState.read(function() {\n nextContent = markdownModule.$convertToMarkdownString(\n markdownModule.TRANSFORMERS,\n void 0,\n true\n );\n });\n const renderedText = getDomRenderedText(editorState.markdownEditorSurface);\n const maps = buildEditorRenderedMaps(nextContent, renderedText);\n editorState.markdownEditorToRenderedMap = maps.editorToRendered;\n editorState.markdownRenderedToEditorMap = maps.renderedToEditor;\n const restoredBody = restoreRawBlocksFromEditor(nextContent);\n const fullContent = composeMarkdownContent(restoredBody);\n if (fullContent === editorState.markdownLexicalRenderedContent) {\n return;\n }\n editorState.markdownLexicalRenderedContent = fullContent;\n handleMarkdownLocalChange(nextContent, fullContent);\n scheduleMarkdownSlashMenuUpdate();\n scheduleMarkdownInlineToolbarUpdate();\n });\n editor.setRootElement(editorState.markdownEditorSurface);\n editor.update(function() {\n const root = lexicalModule.$getRoot();\n if (root.getChildrenSize() === 0) {\n root.append(lexicalModule.$createParagraphNode());\n }\n });\n editorState.markdownLexicalApi = {\n editor,\n lexicalModule,\n richTextModule,\n listModule,\n markdownModule,\n selectionModule,\n unregisterRichText,\n unregisterList,\n unregisterHistory,\n unregisterUpdate\n };\n editorState.markdownEditorSurface.style.display = \"block\";\n if (editorState.markdownEditorTextarea) {\n editorState.markdownEditorTextarea.style.display = \"none\";\n }\n applyMarkdownContent(editorState.markdownCurrentContent);\n hideMarkdownBlockDropIndicator();\n }).catch(function(error) {\n console.warn(\n \"[StudioBridge] Failed to load Lexical markdown editor; falling back to textarea\",\n error\n );\n if (editorState.markdownEditorSurface) {\n editorState.markdownEditorSurface.style.display = \"none\";\n }\n if (editorState.markdownEditorTextarea) {\n editorState.markdownEditorTextarea.style.display = \"block\";\n }\n hideMarkdownSlashMenu();\n hideMarkdownInlineToolbar();\n hideMarkdownBlockDragUi();\n clearMarkdownSelectionOverlay();\n });\n}\nfunction focusMarkdownEditor() {\n if (editorState.markdownLexicalApi && editorState.markdownEditorSurface) {\n editorState.markdownEditorSurface.focus();\n return;\n }\n if (editorState.markdownEditorTextarea) {\n editorState.markdownEditorTextarea.focus();\n }\n}\nfunction applyMarkdownContent(content) {\n if (typeof content !== \"string\") {\n return;\n }\n if (editorState.markdownLexicalApi && editorState.markdownLexicalRenderedContent === content) {\n console.debug(\n \"[StudioBridge] applyMarkdownContent: skipped (content unchanged)\"\n );\n editorState.markdownCurrentContent = content;\n scheduleMarkdownSelectionOverlayRender();\n scheduleMarkdownSlashMenuUpdate();\n scheduleMarkdownInlineToolbarUpdate();\n hideMarkdownBlockDropIndicator();\n return;\n }\n console.debug(\n \"[StudioBridge] applyMarkdownContent: rebuilding Lexical DOM, content length:\",\n content.length,\n \"rendered match:\",\n editorState.markdownLexicalRenderedContent === content,\n \"current match:\",\n editorState.markdownCurrentContent === content\n );\n const mdxImportMap = parseMdxImportMap(content);\n const parts = extractMarkdownParts(content);\n const extracted = extractRawBlocksForEditor(parts.body, mdxImportMap);\n const editorContent = extracted.editorBody;\n const mdxBlocks = Array.isArray(extracted.mdxBlocks) ? extracted.mdxBlocks : [];\n editorState.markdownFrontmatter = parts.frontmatter;\n editorState.markdownRawBlocks = extracted.rawBlocks;\n editorState.markdownRawBlockTokenPrefix = extracted.tokenPrefix;\n editorState.markdownLatestMdxImportMap = mdxImportMap;\n setMarkdownMdxBlocks(mdxBlocks);\n editorState.markdownCurrentContent = content;\n editorState.markdownCurrentEditorContent = editorContent;\n if (editorState.markdownSyncTimer) {\n clearTimeout(editorState.markdownSyncTimer);\n editorState.markdownSyncTimer = null;\n }\n if (editorState.markdownLexicalApi) {\n let savedSelectionOffset = -1;\n if (editorState.markdownEditorSurface) {\n const sel = window.getSelection();\n if (sel && sel.rangeCount > 0) {\n const range = sel.getRangeAt(0);\n if (editorState.markdownEditorSurface.contains(range.startContainer)) {\n savedSelectionOffset = getTextOffsetWithinRoot(\n editorState.markdownEditorSurface,\n range.startContainer,\n range.startOffset\n );\n }\n }\n }\n editorState.markdownLastRemoteContent = content;\n const remoteUpdateToken = editorState.markdownRemoteUpdateToken + 1;\n editorState.markdownRemoteUpdateToken = remoteUpdateToken;\n editorState.markdownPendingLocalReconcile = false;\n editorState.markdownApplyingRemoteUpdate = true;\n editorState.markdownLexicalRenderedContent = content;\n const api = editorState.markdownLexicalApi;\n const remoteContentSnapshot = content;\n api.editor.update(\n function() {\n const lexicalModule = api.lexicalModule;\n const markdownModule = api.markdownModule;\n const root = lexicalModule.$getRoot();\n root.clear();\n markdownModule.$convertFromMarkdownString(\n editorContent,\n markdownModule.TRANSFORMERS,\n void 0,\n true\n );\n if (root.getChildrenSize() === 0) {\n root.append(lexicalModule.$createParagraphNode());\n }\n },\n { discrete: true }\n );\n {\n const renderedText = getDomRenderedText(editorState.markdownEditorSurface);\n const maps = buildEditorRenderedMaps(editorContent, renderedText);\n editorState.markdownEditorToRenderedMap = maps.editorToRendered;\n editorState.markdownRenderedToEditorMap = maps.renderedToEditor;\n }\n setTimeout(function() {\n if (editorState.markdownRemoteUpdateToken !== remoteUpdateToken) {\n return;\n }\n editorState.markdownApplyingRemoteUpdate = false;\n if (editorState.markdownLastRemoteContent === remoteContentSnapshot) {\n editorState.markdownLastRemoteContent = null;\n }\n if (!editorState.markdownPendingLocalReconcile || !editorState.markdownLexicalApi) {\n return;\n }\n editorState.markdownPendingLocalReconcile = false;\n let nextContent = \"\";\n const reconcileApi = editorState.markdownLexicalApi;\n reconcileApi.editor.getEditorState().read(function() {\n nextContent = reconcileApi.markdownModule.$convertToMarkdownString(\n reconcileApi.markdownModule.TRANSFORMERS,\n void 0,\n true\n );\n });\n const renderedText = getDomRenderedText(editorState.markdownEditorSurface);\n const maps = buildEditorRenderedMaps(nextContent, renderedText);\n editorState.markdownEditorToRenderedMap = maps.editorToRendered;\n editorState.markdownRenderedToEditorMap = maps.renderedToEditor;\n const restoredBody = restoreRawBlocksFromEditor(nextContent);\n const fullContent = composeMarkdownContent(restoredBody);\n if (fullContent === editorState.markdownLexicalRenderedContent) {\n return;\n }\n editorState.markdownLexicalRenderedContent = fullContent;\n handleMarkdownLocalChange(nextContent, fullContent);\n scheduleMarkdownSlashMenuUpdate();\n scheduleMarkdownInlineToolbarUpdate();\n }, 0);\n if (savedSelectionOffset >= 0 && editorState.markdownEditorSurface) {\n const maxOffset = editorContent.length;\n const restoredOffset = Math.min(savedSelectionOffset, maxOffset);\n setMarkdownEditorSelection(restoredOffset);\n }\n scheduleMarkdownSelectionOverlayRender();\n scheduleMarkdownSlashMenuUpdate();\n scheduleMarkdownInlineToolbarUpdate();\n hideMarkdownBlockDropIndicator();\n return;\n }\n if (!editorState.markdownEditorTextarea || editorState.markdownEditorTextarea.value === editorContent) {\n clearMarkdownSelectionOverlay();\n hideMarkdownSlashMenu();\n hideMarkdownInlineToolbar();\n hideMarkdownBlockDragUi();\n return;\n }\n const selectionStart = editorState.markdownEditorTextarea.selectionStart;\n const selectionEnd = editorState.markdownEditorTextarea.selectionEnd;\n editorState.markdownEditorTextarea.value = editorContent;\n if (typeof selectionStart === \"number\" && typeof selectionEnd === \"number\") {\n const max = editorState.markdownEditorTextarea.value.length;\n editorState.markdownEditorTextarea.setSelectionRange(\n Math.min(selectionStart, max),\n Math.min(selectionEnd, max)\n );\n }\n clearMarkdownSelectionOverlay();\n hideMarkdownSlashMenu();\n hideMarkdownInlineToolbar();\n hideMarkdownBlockDragUi();\n}\nfunction updateMarkdownOverlaySelections(selections) {\n editorState.markdownLatestSelections = Array.isArray(selections) ? selections : [];\n editorState.markdownOverlaySelections = [];\n if (!Array.isArray(selections) || selections.length === 0) {\n clearMarkdownSelectionOverlay();\n return;\n }\n const validSelections = selections.filter(function(selection) {\n return selection && typeof selection.name === \"string\" && typeof selection.start === \"number\" && typeof selection.end === \"number\";\n });\n if (validSelections.length === 0) {\n clearMarkdownSelectionOverlay();\n return;\n }\n for (const selection of validSelections) {\n const color = typeof selection.color === \"string\" && selection.color ? selection.color : \"#6b7280\";\n const displayName = selection.isCurrentUser ? \"You\" : selection.name;\n const start = Math.max(0, Math.trunc(selection.start));\n const end = Math.max(0, Math.trunc(selection.end));\n const editorRange = sourceSelectionToEditorRange(start, end);\n if (!editorRange) {\n continue;\n }\n editorState.markdownOverlaySelections.push({\n id: selection.id || \"\",\n name: displayName,\n color,\n isCurrentUser: selection.isCurrentUser || false,\n start: editorRange.start,\n end: editorRange.end\n });\n }\n scheduleMarkdownSelectionOverlayRender();\n}\nfunction ensureMarkdownEditor() {\n if (editorState.markdownEditorRoot) {\n return editorState.markdownEditorRoot;\n }\n const editorRoot = el(\"div\", \"vf-markdown-editor\");\n const mdxBlocks = el(\"div\", \"vf-markdown-editor__mdx-blocks\");\n const surface = el(\"div\", \"vf-markdown-editor__surface markdown-body\");\n surface.setAttribute(\"contenteditable\", \"true\");\n surface.setAttribute(\"aria-label\", \"Markdown editor\");\n surface.addEventListener(\"keyup\", scheduleMarkdownSelectionSync);\n surface.addEventListener(\"mouseup\", scheduleMarkdownSelectionSync);\n surface.addEventListener(\"input\", function() {\n scheduleMarkdownSelectionSync();\n scheduleMarkdownSlashMenuUpdate();\n });\n surface.addEventListener(\"keyup\", scheduleMarkdownSlashMenuUpdate);\n surface.addEventListener(\"mouseup\", scheduleMarkdownSlashMenuUpdate);\n surface.addEventListener(\"keydown\", handleMarkdownSlashMenuKeydown);\n surface.addEventListener(\"keydown\", function(event) {\n if (!event.shiftKey || !event.altKey) {\n return;\n }\n if (event.key === \"ArrowUp\") {\n const moved = moveMarkdownCurrentBlockByDelta(-1);\n if (moved) {\n event.preventDefault();\n }\n return;\n }\n if (event.key === \"ArrowDown\") {\n const moved = moveMarkdownCurrentBlockByDelta(1);\n if (moved) {\n event.preventDefault();\n }\n }\n });\n surface.addEventListener(\"scroll\", scheduleMarkdownSelectionOverlayRender);\n surface.addEventListener(\"scroll\", scheduleMarkdownSlashMenuUpdate);\n surface.addEventListener(\"keyup\", scheduleMarkdownInlineToolbarUpdate);\n surface.addEventListener(\"mouseup\", scheduleMarkdownInlineToolbarUpdate);\n surface.addEventListener(\"input\", scheduleMarkdownInlineToolbarUpdate);\n surface.addEventListener(\"scroll\", scheduleMarkdownInlineToolbarUpdate);\n surface.addEventListener(\"scroll\", refreshMarkdownBlockDragHandlePosition);\n const surfaceWrap = el(\"div\", \"vf-markdown-editor__surface-wrap\");\n const selectionOverlay = el(\"div\", \"vf-markdown-editor__selection-overlay\");\n const slashMenu = el(\"div\", \"vf-markdown-editor__slash-menu\");\n const inlineToolbar = el(\"div\", \"vf-markdown-editor__inline-toolbar\");\n function createInlineButton(label, format, handler) {\n const button = btn(\"vf-markdown-editor__inline-button\", label, function(event) {\n event.preventDefault();\n if (handler)\n handler();\n });\n if (format)\n button.setAttribute(\"data-format\", format);\n button.addEventListener(\"mousedown\", function(event) {\n event.preventDefault();\n });\n return button;\n }\n function createSeparator() {\n return el(\"div\", \"vf-markdown-editor__inline-separator\");\n }\n const blockDropdown = el(\"div\", \"vf-markdown-editor__block-dropdown\");\n const blockTypes = [\n { type: \"paragraph\", label: \"Paragraph\" },\n { type: \"h1\", label: \"Heading 1\" },\n { type: \"h2\", label: \"Heading 2\" },\n { type: \"h3\", label: \"Heading 3\" },\n { type: \"bullet\", label: \"Bulleted list\" },\n { type: \"number\", label: \"Numbered list\" },\n { type: \"quote\", label: \"Quote\" }\n ];\n blockTypes.forEach(function(bt) {\n const opt = btn(\"vf-markdown-editor__block-option\", bt.label, function(event) {\n event.preventDefault();\n setMarkdownBlockType(bt.type);\n blockDropdown.style.display = \"none\";\n scheduleMarkdownInlineToolbarUpdate();\n });\n opt.setAttribute(\"data-block-type\", bt.type);\n opt.addEventListener(\"mousedown\", function(event) {\n event.preventDefault();\n });\n blockDropdown.appendChild(opt);\n });\n const blockTrigger = btn(\n \"vf-markdown-editor__block-trigger\",\n String.fromCharCode(182),\n // ¶\n function(event) {\n event.preventDefault();\n const isOpen = blockDropdown.style.display === \"block\";\n blockDropdown.style.display = isOpen ? \"none\" : \"block\";\n }\n );\n blockTrigger.addEventListener(\"mousedown\", function(event) {\n event.preventDefault();\n });\n const row = el(\"div\", \"vf-markdown-editor__inline-row\");\n row.appendChild(blockTrigger);\n row.appendChild(createSeparator());\n row.appendChild(\n createInlineButton(\"B\", \"bold\", function() {\n toggleMarkdownInlineFormat(\"bold\");\n })\n );\n row.appendChild(\n createInlineButton(\"I\", \"italic\", function() {\n toggleMarkdownInlineFormat(\"italic\");\n })\n );\n row.appendChild(\n createInlineButton(\"U\", \"underline\", function() {\n toggleMarkdownInlineFormat(\"underline\");\n })\n );\n row.appendChild(\n createInlineButton(\"S\", \"strikethrough\", function() {\n toggleMarkdownInlineFormat(\"strikethrough\");\n })\n );\n row.appendChild(createSeparator());\n row.appendChild(\n createInlineButton(String.fromCodePoint(128279), null, function() {\n insertMarkdownLink();\n })\n );\n row.appendChild(\n createInlineButton(\"</>\", \"code\", function() {\n toggleMarkdownInlineFormat(\"code\");\n })\n );\n inlineToolbar.appendChild(row);\n inlineToolbar.appendChild(blockDropdown);\n document.addEventListener(\"mousedown\", function(event) {\n if (blockDropdown.style.display === \"block\" && !blockDropdown.contains(event.target) && event.target !== blockTrigger) {\n blockDropdown.style.display = \"none\";\n }\n });\n document.addEventListener(\n \"keydown\",\n function(event) {\n if (event.key === \"Escape\" && blockDropdown.style.display === \"block\") {\n event.preventDefault();\n event.stopPropagation();\n blockDropdown.style.display = \"none\";\n }\n },\n true\n );\n const blockDragHandle = btn(\"vf-markdown-editor__block-handle\", \"::\", function() {\n });\n blockDragHandle.draggable = true;\n blockDragHandle.setAttribute(\"data-dragging\", \"false\");\n blockDragHandle.addEventListener(\"dragstart\", function(event) {\n const indexText = blockDragHandle.getAttribute(\"data-block-index\");\n const index = Number(indexText);\n if (!Number.isInteger(index)) {\n event.preventDefault();\n return;\n }\n editorState.markdownBlockDragSourceIndex = index;\n editorState.markdownBlockDragActive = true;\n blockDragHandle.setAttribute(\"data-dragging\", \"true\");\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = \"move\";\n event.dataTransfer.setData(\"text/plain\", String(index));\n const blocks = getMarkdownTopLevelBlocks();\n const block = blocks[index];\n removeMarkdownDragGhost();\n if (block) {\n const ghost = createMarkdownDragGhost(block);\n document.body.appendChild(ghost);\n editorState.markdownBlockDragGhost = ghost;\n event.dataTransfer.setDragImage(ghost, 14, 14);\n }\n }\n showMarkdownBlockDropIndicator(index);\n });\n blockDragHandle.addEventListener(\"mouseenter\", function() {\n if (editorState.markdownBlockHandleHoverIndex >= 0) {\n blockDragHandle.style.display = \"block\";\n }\n });\n blockDragHandle.addEventListener(\"mouseleave\", function(event) {\n if (editorState.markdownBlockDragActive) {\n return;\n }\n const next = event.relatedTarget;\n if (next && editorState.markdownEditorSurface && editorState.markdownEditorSurface.contains(next)) {\n return;\n }\n hideMarkdownBlockDragHandle();\n });\n blockDragHandle.addEventListener(\"dragend\", function() {\n hideMarkdownBlockDragUi();\n });\n const blockDropIndicator = el(\"div\", \"vf-markdown-editor__block-drop-indicator\");\n const blockDropLabel = el(\"div\", \"vf-markdown-editor__block-drop-label\");\n surfaceWrap.appendChild(surface);\n surfaceWrap.appendChild(selectionOverlay);\n const textarea = el(\"textarea\", \"vf-markdown-editor__textarea\");\n textarea.setAttribute(\"aria-label\", \"Markdown editor\");\n textarea.spellcheck = false;\n textarea.addEventListener(\"input\", function() {\n handleMarkdownLocalChange(textarea.value);\n scheduleMarkdownSelectionSync();\n hideMarkdownSlashMenu();\n });\n textarea.addEventListener(\"select\", scheduleMarkdownSelectionSync);\n textarea.addEventListener(\"keyup\", scheduleMarkdownSelectionSync);\n textarea.addEventListener(\"click\", scheduleMarkdownSelectionSync);\n textarea.addEventListener(\"input\", clearMarkdownSelectionOverlay);\n textarea.addEventListener(\"keydown\", function() {\n hideMarkdownSlashMenu();\n hideMarkdownInlineToolbar();\n hideMarkdownBlockDragUi();\n });\n surface.addEventListener(\"mousemove\", function(event) {\n if (editorState.markdownBlockDragActive) {\n return;\n }\n const index = getMarkdownBlockHoverIndexFromPointer(\n event.target,\n event.clientX,\n event.clientY\n );\n if (index < 0) {\n hideMarkdownBlockDragHandle();\n return;\n }\n const blocks = getMarkdownTopLevelBlocks();\n const block = blocks[index];\n if (!block) {\n hideMarkdownBlockDragHandle();\n return;\n }\n positionMarkdownBlockDragHandle(block, index);\n });\n surface.addEventListener(\"mouseleave\", function(event) {\n if (!editorState.markdownBlockDragActive) {\n const next = event.relatedTarget;\n if (next && editorState.markdownBlockDragHandle && (next === editorState.markdownBlockDragHandle || editorState.markdownBlockDragHandle.contains(next))) {\n return;\n }\n hideMarkdownBlockDragHandle();\n }\n });\n surface.addEventListener(\"dragover\", function(event) {\n if (!editorState.markdownBlockDragActive) {\n return;\n }\n event.preventDefault();\n if (event.dataTransfer) {\n event.dataTransfer.dropEffect = \"move\";\n }\n autoScrollMarkdownSurfaceDuringDrag(event.clientY);\n const slotIndex = getMarkdownDropSlotIndexFromPointer(event.clientY);\n if (slotIndex >= 0) {\n showMarkdownBlockDropIndicator(slotIndex);\n }\n });\n surface.addEventListener(\"drop\", function(event) {\n if (!editorState.markdownBlockDragActive) {\n return;\n }\n event.preventDefault();\n const fallbackSlot = getMarkdownDropSlotIndexFromPointer(event.clientY);\n const slotIndex = editorState.markdownBlockDropSlotIndex >= 0 ? editorState.markdownBlockDropSlotIndex : fallbackSlot;\n const sourceIndex = editorState.markdownBlockDragSourceIndex;\n hideMarkdownBlockDragUi();\n if (sourceIndex < 0 || slotIndex < 0) {\n return;\n }\n moveMarkdownLexicalBlock(sourceIndex, slotIndex);\n });\n editorRoot.appendChild(mdxBlocks);\n editorRoot.appendChild(surfaceWrap);\n editorRoot.appendChild(textarea);\n editorRoot.appendChild(slashMenu);\n editorRoot.appendChild(inlineToolbar);\n editorRoot.appendChild(blockDragHandle);\n editorRoot.appendChild(blockDropIndicator);\n editorRoot.appendChild(blockDropLabel);\n document.body.appendChild(editorRoot);\n editorState.markdownEditorRoot = editorRoot;\n editorState.markdownEditorSurface = surface;\n editorState.markdownEditorTextarea = textarea;\n editorState.markdownMdxBlocksRoot = mdxBlocks;\n editorState.markdownSelectionOverlayRoot = selectionOverlay;\n editorState.markdownSlashMenuRoot = slashMenu;\n editorState.markdownInlineToolbarRoot = inlineToolbar;\n editorState.markdownBlockDragHandle = blockDragHandle;\n editorState.markdownBlockDropIndicator = blockDropIndicator;\n editorState.markdownBlockDropLabel = blockDropLabel;\n setMarkdownMdxBlocks(editorState.markdownLatestMdxBlocks);\n updateMarkdownOverlaySelections(editorState.markdownLatestSelections);\n setupMarkdownLexicalEditor();\n applyMarkdownContent(editorState.markdownCurrentContent);\n return editorRoot;\n}\nfunction registerMarkdownGlobalListeners() {\n for (const cleanup of editorState.markdownGlobalListenerCleanups)\n cleanup();\n editorState.markdownGlobalListenerCleanups = [];\n const onSelectionChange = function() {\n if (!editorState.markdownEditorRoot || editorState.markdownEditorRoot.style.display !== \"block\") {\n return;\n }\n scheduleMarkdownSelectionSync();\n scheduleMarkdownSelectionOverlayRender();\n scheduleMarkdownSlashMenuUpdate();\n scheduleMarkdownInlineToolbarUpdate();\n };\n document.addEventListener(\"selectionchange\", onSelectionChange);\n editorState.markdownGlobalListenerCleanups.push(\n () => document.removeEventListener(\"selectionchange\", onSelectionChange)\n );\n window.addEventListener(\"resize\", scheduleMarkdownSelectionOverlayRender);\n window.addEventListener(\"resize\", scheduleMarkdownSlashMenuUpdate);\n window.addEventListener(\"resize\", scheduleMarkdownInlineToolbarUpdate);\n window.addEventListener(\"resize\", hideMarkdownBlockDragUi);\n editorState.markdownGlobalListenerCleanups.push(\n () => window.removeEventListener(\"resize\", scheduleMarkdownSelectionOverlayRender),\n () => window.removeEventListener(\"resize\", scheduleMarkdownSlashMenuUpdate),\n () => window.removeEventListener(\"resize\", scheduleMarkdownInlineToolbarUpdate),\n () => window.removeEventListener(\"resize\", hideMarkdownBlockDragUi)\n );\n}\nfunction setMarkdownEditMode(enabled) {\n const markdownBody = document.getElementById(\"markdown-body\");\n if (!markdownBody || !isMarkdownPage()) {\n return;\n }\n if (enabled) {\n ensureMarkdownEditor();\n registerMarkdownGlobalListeners();\n setupMarkdownLexicalEditor();\n markdownBody.style.display = \"none\";\n if (editorState.markdownEditorRoot) {\n editorState.markdownEditorRoot.style.display = \"block\";\n }\n editorState.markdownHasUnsavedChanges = false;\n focusMarkdownEditor();\n scheduleMarkdownSelectionSync();\n scheduleMarkdownSelectionOverlayRender();\n scheduleMarkdownSlashMenuUpdate();\n scheduleMarkdownInlineToolbarUpdate();\n postMarkdownEditorReady();\n if (getConfig().wsUrl && getConfig().yjsGuid && !editorState.markdownYDoc) {\n setupMarkdownYjsConnection({\n wsUrl: getConfig().wsUrl,\n guid: getConfig().yjsGuid,\n fileId: editorState.markdownFileId || \"\"\n });\n }\n } else {\n markdownBody.style.display = \"\";\n if (editorState.markdownEditorRoot) {\n editorState.markdownEditorRoot.style.display = \"none\";\n }\n hideMarkdownSlashMenu();\n hideMarkdownInlineToolbar();\n hideMarkdownBlockDragUi();\n editorState.markdownOverlaySelections = [];\n editorState.markdownEditorToRenderedMap = [];\n editorState.markdownRenderedToEditorMap = [];\n clearMarkdownSelectionOverlay();\n clearMarkdownSelectionSync();\n disposeMarkdownYjs();\n for (const cleanup of editorState.markdownGlobalListenerCleanups)\n cleanup();\n editorState.markdownGlobalListenerCleanups = [];\n }\n if (editorState.markdownEditButton) {\n editorState.markdownEditButton.textContent = enabled ? \"Done\" : \"Edit\";\n }\n const nextUrl = new URL(window.location.href);\n if (enabled) {\n nextUrl.searchParams.set(\"edit\", \"true\");\n } else {\n nextUrl.searchParams.delete(\"edit\");\n }\n window.history.replaceState(window.history.state, \"\", nextUrl.toString());\n}\nfunction ensureMarkdownEditButton() {\n if (editorState.markdownEditButton || !isMarkdownPage()) {\n return;\n }\n const button = btn(\"vf-markdown-edit-button\", \"Edit\", function() {\n const isEditing = editorState.markdownEditorRoot && editorState.markdownEditorRoot.style.display === \"block\";\n setMarkdownEditMode(!isEditing);\n });\n document.body.appendChild(button);\n editorState.markdownEditButton = button;\n}\nfunction setupMarkdownEditor(params) {\n if (!isMarkdownPage()) {\n return;\n }\n editorState.markdownFileId = params.get(\"vf_file_id\") || getConfig().pageId || null;\n if (getConfig().studioMode === \"simple\") {\n setMarkdownEditMode(true);\n } else {\n ensureMarkdownEditButton();\n if (params.get(\"edit\") === \"true\") {\n setMarkdownEditMode(true);\n }\n }\n}\n\n// src/studio/bridge/bridge-markdown-yjs.ts\nfunction syncLocalChangeToYText(fullContent) {\n if (!editorState.markdownYText || !editorState.markdownYDoc) {\n return;\n }\n const currentYContent = editorState.markdownYText.toString();\n if (currentYContent === fullContent) {\n return;\n }\n const diff = computeTextDiff(currentYContent, fullContent);\n if (diff.deleteCount === 0 && diff.insertText === \"\") {\n return;\n }\n const ytext = editorState.markdownYText;\n editorState.markdownYDoc.transact(() => {\n if (diff.deleteCount > 0) {\n ytext.delete(diff.index, diff.deleteCount);\n }\n if (diff.insertText) {\n ytext.insert(diff.index, diff.insertText);\n }\n }, LEXICAL_YJS_ORIGIN);\n}\nfunction setupMarkdownYjsConnection(config3) {\n if (editorState.markdownYDoc) {\n return;\n }\n const setupId = ++editorState.markdownYjsSetupId;\n Promise.all([\n import(\"https://esm.sh/yjs@13.6.28?target=es2022\"),\n import(\"https://esm.sh/y-websocket@2.1.0?deps=yjs@13.6.28&target=es2022\")\n ]).then((modules) => {\n if (setupId !== editorState.markdownYjsSetupId) {\n return;\n }\n const Y = modules[0];\n const WebsocketProvider = modules[1].WebsocketProvider;\n editorState.markdownYjsY = Y;\n console.debug(\"[StudioBridge] Yjs setup:\", {\n wsUrl: config3.wsUrl,\n guid: config3.guid,\n fileId: config3.fileId\n });\n const doc = new Y.Doc({ guid: config3.guid });\n const provider = new WebsocketProvider(config3.wsUrl, config3.guid, doc, {\n resyncInterval: -1\n });\n const ytext = doc.getText(config3.fileId);\n editorState.markdownYDoc = doc;\n editorState.markdownYProvider = provider;\n editorState.markdownYText = ytext;\n const isCurrentSetup = () => setupId === editorState.markdownYjsSetupId && editorState.markdownYProvider === provider && editorState.markdownYDoc === doc && editorState.markdownYText === ytext;\n provider.on(\"status\", (event) => {\n if (!isCurrentSetup()) {\n return;\n }\n console.debug(\"[StudioBridge] Yjs status:\", event.status, {\n hasWs: !!provider.ws,\n wsReadyState: provider.ws?.readyState\n });\n if (event.status !== \"connected\") {\n editorState.markdownYjsConnected = false;\n return;\n }\n if (provider.ws) {\n const ws = provider.ws;\n const origOnMessage = ws.onmessage;\n ws.onmessage = function(wsEvent) {\n if (typeof wsEvent.data === \"string\") {\n console.debug(\n \"[StudioBridge] Yjs filtered string message:\",\n wsEvent.data.slice(0, 120)\n );\n return;\n }\n if (origOnMessage) {\n origOnMessage.call(ws, wsEvent);\n }\n };\n }\n });\n const presenceUser = {\n id: \"preview-\" + Math.random().toString(36).slice(2),\n name: \"Preview\"\n };\n try {\n const cookieMatch = document.cookie.match(/authToken=([^;]+)/);\n if (cookieMatch) {\n const parts = cookieMatch[1].split(\".\");\n if (parts.length === 3) {\n const payload = JSON.parse(\n atob(parts[1].replace(/-/g, \"+\").replace(/_/g, \"/\"))\n );\n if (payload.userId) {\n presenceUser.id = payload.userId;\n }\n if (payload.email) {\n const local = payload.email.split(\"@\")[0] || \"\";\n if (local.includes(\".\") || local.includes(\"_\")) {\n presenceUser.name = local.split(/[._]/).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(\" \");\n } else {\n presenceUser.name = local.charAt(0).toUpperCase() + local.slice(1);\n }\n }\n }\n }\n } catch (_e) {\n }\n provider.awareness.setLocalStateField(\"user\", {\n id: presenceUser.id,\n name: presenceUser.name,\n color: \"#10b981\"\n });\n function syncAwareness() {\n if (!isCurrentSetup()) {\n return;\n }\n const states = Array.from(\n provider.awareness.getStates().entries()\n );\n const users = [];\n for (const [clientId, st] of states) {\n const user = st.user;\n if (!user || typeof user.name !== \"string\") {\n continue;\n }\n users.push({\n id: user.id || String(clientId),\n name: user.name,\n color: user.color || \"#6b7280\",\n isCurrentUser: clientId === provider.awareness.clientID,\n isAgent: user.isAgent || false\n });\n }\n editorState.markdownLatestPresenceUsers = users;\n const selections = [];\n for (const [cId, st] of states) {\n const u = st.user;\n const ranges = st.selection;\n if (!u || !Array.isArray(ranges) || ranges.length === 0) {\n continue;\n }\n for (const range of ranges) {\n const anchorPos = Y.createAbsolutePositionFromRelativePosition(\n range.anchor,\n doc\n );\n const markerPos = Y.createAbsolutePositionFromRelativePosition(\n range.marker,\n doc\n );\n if (!anchorPos || !markerPos || anchorPos.type !== ytext || markerPos.type !== ytext) {\n continue;\n }\n selections.push({\n id: u.id || String(cId),\n name: u.name || \"Anonymous\",\n color: u.color || \"#6b7280\",\n isCurrentUser: cId === provider.awareness.clientID,\n start: Math.min(anchorPos.index, markerPos.index),\n end: Math.max(anchorPos.index, markerPos.index)\n });\n }\n }\n updateMarkdownOverlaySelections(selections);\n }\n provider.awareness.on(\"change\", syncAwareness);\n let ytextObserverRegistered = false;\n provider.on(\"sync\", (synced) => {\n if (!isCurrentSetup()) {\n return;\n }\n console.debug(\"[StudioBridge] Yjs sync:\", {\n synced,\n ytextLength: ytext.length,\n contentPreview: ytext.toString().slice(0, 80)\n });\n if (!synced) {\n editorState.markdownYjsConnected = false;\n return;\n }\n if (!editorState.markdownYjsConnected) {\n editorState.markdownYjsConnected = true;\n const ytextContent = ytext.toString();\n if (editorState.markdownHasUnsavedChanges && editorState.markdownCurrentContent !== ytextContent) {\n syncLocalChangeToYText(editorState.markdownCurrentContent);\n } else {\n applyMarkdownContent(ytextContent);\n }\n if (editorState.markdownPendingSelection) {\n const ps = editorState.markdownPendingSelection;\n editorState.markdownPendingSelection = null;\n const cs = Math.max(0, Math.min(ytext.length, ps.start));\n const ce = Math.max(0, Math.min(ytext.length, ps.end));\n provider.awareness.setLocalStateField(\"selection\", [\n {\n anchor: Y.createRelativePositionFromTypeIndex(ytext, cs),\n marker: Y.createRelativePositionFromTypeIndex(ytext, ce)\n }\n ]);\n }\n if (!ytextObserverRegistered) {\n ytextObserverRegistered = true;\n ytext.observe((event) => {\n if (!isCurrentSetup()) {\n return;\n }\n const origin = event.transaction.origin;\n if (origin === LEXICAL_YJS_ORIGIN) {\n return;\n }\n const fullContent = ytext.toString();\n const contentMatch = fullContent === editorState.markdownCurrentContent;\n console.debug(\"[StudioBridge] Yjs Y.Text observer:\", {\n origin: String(origin),\n contentMatch,\n ytextLength: fullContent.length\n });\n if (contentMatch) {\n return;\n }\n applyMarkdownContent(fullContent);\n });\n }\n syncAwareness();\n console.debug(\n \"[StudioBridge] Yjs synced, bound to Y.Text for fileId:\",\n config3.fileId\n );\n }\n });\n }).catch((error) => {\n console.error(\"[StudioBridge] Failed to setup Yjs connection:\", error);\n });\n}\nfunction writeToYText(text, options) {\n if (!editorState.markdownYText || !editorState.markdownYDoc || !editorState.markdownYjsConnected) {\n console.warn(\"[StudioBridge] writeToYText: Yjs not connected or not synced\");\n return false;\n }\n const origin = options?.origin ?? \"agent-write\";\n const pos = options?.position ?? editorState.markdownYText.length;\n const safePos = Math.max(0, Math.min(pos, editorState.markdownYText.length));\n const yt = editorState.markdownYText;\n editorState.markdownYDoc.transact(() => {\n yt.insert(safePos, text);\n }, origin);\n return true;\n}\nfunction replaceYTextContent(content) {\n if (!editorState.markdownYText || !editorState.markdownYDoc || !editorState.markdownYjsConnected) {\n console.warn(\"[StudioBridge] replaceYTextContent: Yjs not connected or not synced\");\n return false;\n }\n const yt2 = editorState.markdownYText;\n editorState.markdownYDoc.transact(() => {\n yt2.delete(0, yt2.length);\n yt2.insert(0, content);\n }, \"agent-write\");\n return true;\n}\nfunction disposeMarkdownYjs() {\n editorState.markdownYjsSetupId++;\n if (editorState.markdownYProvider) {\n editorState.markdownYProvider.awareness.setLocalStateField(\"selection\", null);\n editorState.markdownYProvider.disconnect();\n editorState.markdownYProvider.destroy();\n editorState.markdownYProvider = null;\n }\n if (editorState.markdownYDoc) {\n editorState.markdownYDoc.destroy();\n editorState.markdownYDoc = null;\n }\n editorState.markdownYText = null;\n editorState.markdownYjsConnected = false;\n editorState.markdownYjsY = null;\n editorState.markdownPendingSelection = null;\n editorState.markdownLastRemoteContent = null;\n editorState.markdownApplyingRemoteUpdate = false;\n editorState.markdownLatestPresenceUsers = [];\n updateMarkdownOverlaySelections([]);\n}\n\n// src/studio/bridge/bridge-markdown-core.ts\nfunction getLineNumberForOffset(text, offset) {\n const source = typeof text === \"string\" ? text : \"\";\n const maxOffset = Math.max(0, Math.min(source.length, Math.trunc(offset || 0)));\n let line = 1;\n for (let i = 0; i < maxOffset; i += 1) {\n if (source.charCodeAt(i) === 10) {\n line += 1;\n }\n }\n return line;\n}\nfunction getMdxComponentName(blockText) {\n const source = typeof blockText === \"string\" ? blockText : \"\";\n const fence = String.fromCharCode(96, 96, 96);\n const componentMatch = source.match(/<\\s*([A-Z][\\w.]*)/);\n if (componentMatch && componentMatch[1]) {\n return componentMatch[1];\n }\n if (source.trim().startsWith(fence + \"tsx\")) {\n return \"tsx block\";\n }\n if (source.trim().startsWith(fence + \"jsx\")) {\n return \"jsx block\";\n }\n return \"component block\";\n}\nfunction openFilePathInStudio(filePath, lineNumber, symbolName) {\n if (typeof filePath !== \"string\" || !filePath) {\n return;\n }\n const safeLine = Number.isFinite(lineNumber) ? Math.max(1, Math.trunc(lineNumber)) : 1;\n getEditorCallbacks()?.onOpenFile(\n filePath,\n safeLine,\n 1,\n typeof symbolName === \"string\" && symbolName.trim() ? symbolName.trim() : void 0\n );\n}\nfunction normalizePathSegments(segments) {\n const stack = [];\n for (const segment of segments) {\n if (!segment || segment === \".\") {\n continue;\n }\n if (segment === \"..\") {\n if (stack.length > 0) {\n stack.pop();\n }\n continue;\n }\n stack.push(segment);\n }\n return stack;\n}\nfunction resolveImportPathForPage(importPath) {\n const sourcePath = typeof importPath === \"string\" ? importPath.trim() : \"\";\n if (!sourcePath) {\n return \"\";\n }\n if (sourcePath.startsWith(\"@/\") || sourcePath.startsWith(\"~/\")) {\n return sourcePath.slice(2);\n }\n if (sourcePath.startsWith(\"/\")) {\n let normalizedPath = sourcePath;\n while (normalizedPath.startsWith(\"/\")) {\n normalizedPath = normalizedPath.slice(1);\n }\n return normalizedPath;\n }\n const pagePath = getConfig().pagePath;\n if (!pagePath || !sourcePath.startsWith(\".\")) {\n return sourcePath;\n }\n const baseParts = String(pagePath).split(\"/\");\n baseParts.pop();\n const resolved = normalizePathSegments(baseParts.concat(sourcePath.split(\"/\")));\n return resolved.join(\"/\");\n}\nfunction isLikelyProjectImportPath(importPath) {\n if (typeof importPath !== \"string\") {\n return false;\n }\n const value = importPath.trim();\n if (!value) {\n return false;\n }\n return value.startsWith(\".\") || value.startsWith(\"/\") || value.startsWith(\"@/\") || value.startsWith(\"~/\");\n}\nfunction guessStudioFilePath(filePath) {\n const sourcePath = typeof filePath === \"string\" ? filePath.trim() : \"\";\n if (!sourcePath) {\n return \"\";\n }\n const hasKnownExtension = sourcePath.match(/\\.(tsx?|jsx?|mdx?|json|css|scss|sass|less)$/i);\n if (hasKnownExtension) {\n return sourcePath;\n }\n if (sourcePath.endsWith(\"/\")) {\n return sourcePath + \"index.tsx\";\n }\n return sourcePath + \".tsx\";\n}\nfunction parseMdxImportMap(content) {\n const source = typeof content === \"string\" ? content : \"\";\n const importMap = {};\n if (!source) {\n return importMap;\n }\n const stripImportComments = function(specifierText) {\n return String(specifierText || \"\").replace(/\\/\\*[\\s\\S]*?\\*\\//g, \" \").replace(/\\/\\/[^\\n\\r]*/g, \" \");\n };\n const normalizeImportSpecifier = function(specifierText) {\n return stripImportComments(specifierText).replace(/\\s+/g, \" \").trim();\n };\n const setImportEntry = function(localName, resolvedPath, symbolName, importKind) {\n const key = typeof localName === \"string\" ? localName.trim() : \"\";\n const filePathValue = typeof resolvedPath === \"string\" ? resolvedPath.trim() : \"\";\n if (!key || !filePathValue) {\n return;\n }\n importMap[key] = {\n filePath: filePathValue,\n symbolName: typeof symbolName === \"string\" ? symbolName.trim() : \"\",\n importKind: typeof importKind === \"string\" ? importKind : \"unknown\"\n };\n };\n const mapNamedImports = function(namedSpecifier, resolvedPath) {\n const text = String(namedSpecifier || \"\").trim();\n if (!text.startsWith(\"{\") || !text.endsWith(\"}\")) {\n return;\n }\n const named = text.slice(1, -1).split(\",\");\n for (const entry of named) {\n const part = entry.trim();\n if (!part) {\n continue;\n }\n const normalizedPart = normalizeImportSpecifier(part).trim();\n if (!normalizedPart || /^type\\s+/.test(normalizedPart)) {\n continue;\n }\n const aliasMatch = normalizedPart.match(\n /^([A-Za-z_$][\\w$]*)\\s+as\\s+([A-Za-z_$][\\w$]*)$/\n );\n const sourceName = aliasMatch ? aliasMatch[1] : normalizedPart;\n const localName = aliasMatch ? aliasMatch[2] : normalizedPart;\n if (localName) {\n const isDefaultAlias = sourceName === \"default\";\n setImportEntry(\n localName,\n resolvedPath,\n isDefaultAlias ? \"\" : sourceName,\n isDefaultAlias ? \"default\" : \"named\"\n );\n }\n }\n };\n const importPattern = /^import\\s+([\\s\\S]*?)\\s+from\\s+['\"]([^'\"]+)['\"]\\s*;?/gm;\n let match = importPattern.exec(source);\n while (match) {\n const specifier = normalizeImportSpecifier(match[1] || \"\");\n if (!specifier) {\n match = importPattern.exec(source);\n continue;\n }\n const typeOnlySpecifier = specifier.startsWith(\"type \");\n const normalizedSpecifier = typeOnlySpecifier ? specifier.slice(5).trim() : specifier;\n if (typeOnlySpecifier) {\n match = importPattern.exec(source);\n continue;\n }\n const rawImportPath = String(match[2] || \"\").trim();\n if (!isLikelyProjectImportPath(rawImportPath)) {\n match = importPattern.exec(source);\n continue;\n }\n const resolvedPath = guessStudioFilePath(resolveImportPathForPage(rawImportPath));\n if (!resolvedPath) {\n match = importPattern.exec(source);\n continue;\n }\n if (normalizedSpecifier.startsWith(\"{\") && normalizedSpecifier.endsWith(\"}\")) {\n mapNamedImports(normalizedSpecifier, resolvedPath);\n } else if (normalizedSpecifier.startsWith(\"* as \")) {\n const namespaceName = normalizedSpecifier.slice(5).trim();\n if (namespaceName) {\n setImportEntry(namespaceName, resolvedPath, \"\", \"namespace\");\n }\n } else {\n const commaIndex = normalizedSpecifier.indexOf(\",\");\n if (commaIndex >= 0) {\n const defaultPart = normalizedSpecifier.slice(0, commaIndex).trim();\n const restPart = normalizedSpecifier.slice(commaIndex + 1).trim();\n const normalizedDefaultPart = defaultPart;\n if (normalizedDefaultPart && !/^type\\s+/.test(normalizedDefaultPart)) {\n setImportEntry(normalizedDefaultPart, resolvedPath, \"\", \"default\");\n }\n if (restPart.startsWith(\"{\")) {\n mapNamedImports(restPart, resolvedPath);\n } else if (restPart.startsWith(\"* as \")) {\n const namespaceName = restPart.slice(5).trim();\n if (namespaceName) {\n setImportEntry(namespaceName, resolvedPath, \"\", \"namespace\");\n }\n }\n } else {\n const normalizedDefaultPart = normalizedSpecifier;\n if (normalizedDefaultPart && !/^type\\s+/.test(normalizedDefaultPart)) {\n setImportEntry(normalizedDefaultPart, resolvedPath, \"\", \"default\");\n }\n }\n }\n match = importPattern.exec(source);\n }\n return importMap;\n}\nfunction postMarkdownEditorReady() {\n if (!editorState.markdownFileId) {\n return;\n }\n getEditorCallbacks()?.onEditorReady(editorState.markdownFileId, getConfig().pagePath);\n}\nfunction scheduleMarkdownSync(_content) {\n if (!editorState.markdownFileId) {\n return;\n }\n if (editorState.markdownSyncTimer) {\n clearTimeout(editorState.markdownSyncTimer);\n }\n editorState.markdownSyncTimer = setTimeout(function() {\n getEditorCallbacks()?.onContentChange(\n editorState.markdownFileId,\n getConfig().pagePath,\n editorState.markdownCurrentContent\n );\n editorState.markdownHasUnsavedChanges = false;\n }, 120);\n}\nfunction computeTextDiff(oldText, newText) {\n let prefixLen = 0;\n const minLen = Math.min(oldText.length, newText.length);\n while (prefixLen < minLen && oldText.charCodeAt(prefixLen) === newText.charCodeAt(prefixLen)) {\n prefixLen++;\n }\n let suffixLen = 0;\n const maxSuffix = minLen - prefixLen;\n while (suffixLen < maxSuffix && oldText.charCodeAt(oldText.length - 1 - suffixLen) === newText.charCodeAt(newText.length - 1 - suffixLen)) {\n suffixLen++;\n }\n return {\n index: prefixLen,\n deleteCount: oldText.length - prefixLen - suffixLen,\n insertText: newText.slice(prefixLen, suffixLen > 0 ? newText.length - suffixLen : void 0)\n };\n}\nfunction extractMarkdownParts(content) {\n if (typeof content !== \"string\") {\n return {\n frontmatter: \"\",\n body: \"\"\n };\n }\n const frontmatterPattern = new RegExp(\n \"^---[ \\\\t]*\\\\r?\\\\n[\\\\s\\\\S]*?\\\\r?\\\\n---[ \\\\t]*(?:\\\\r?\\\\n)?\"\n );\n const match = content.match(frontmatterPattern);\n if (!match) {\n return {\n frontmatter: \"\",\n body: content\n };\n }\n return {\n frontmatter: match[0],\n body: content.slice(match[0].length)\n };\n}\nfunction composeMarkdownContent(body) {\n const safeBody = typeof body === \"string\" ? body : \"\";\n if (!editorState.markdownFrontmatter) {\n return safeBody;\n }\n if (!safeBody) {\n return editorState.markdownFrontmatter;\n }\n if (editorState.markdownFrontmatter.endsWith(\"\\n\")) {\n return editorState.markdownFrontmatter + safeBody;\n }\n return editorState.markdownFrontmatter + \"\\n\" + safeBody;\n}\nfunction extractRawBlocksForEditor(body, mdxImportMap) {\n const source = typeof body === \"string\" ? body : \"\";\n const rawBlocks = [];\n const mdxBlocks = [];\n const tokenPrefix = \"VF_RAW_BLOCK_\" + Date.now().toString(36) + \"_\" + Math.random().toString(36).slice(2, 8);\n const trackMdxBlocks = isMdxPage();\n const importMap = mdxImportMap && typeof mdxImportMap === \"object\" ? mdxImportMap : {};\n const createToken = function(index) {\n return \"[[\" + tokenPrefix + \"_\" + index + \"]]\";\n };\n const registerMdxBlock = function(rawBlock, tokenIndex, offset, inputText) {\n if (!trackMdxBlocks) {\n return;\n }\n const trimmed = String(rawBlock || \"\").trimStart();\n const fence = String.fromCharCode(96, 96, 96);\n const startsWithTsxFence = trimmed.startsWith(fence + \"tsx\") || trimmed.startsWith(fence + \"jsx\");\n const startsWithUpperTag = /^<\\s*[A-Z]/.test(trimmed);\n const hasTsxProps = trimmed.indexOf(\"{\") >= 0 && trimmed.indexOf(\"}\") >= 0;\n if (!startsWithTsxFence && !startsWithUpperTag && !hasTsxProps) {\n return;\n }\n const label = startsWithTsxFence ? \"TSX block\" : \"JSX \" + getMdxComponentName(trimmed);\n const componentName = getMdxComponentName(trimmed);\n const componentNamePattern = /^[A-Z][\\w$]*(?:\\.[A-Z][\\w$]*)*$/;\n const normalizedComponentName = componentNamePattern.test(componentName) ? componentName : \"\";\n const componentParts = normalizedComponentName ? normalizedComponentName.split(\".\") : [];\n const namespaceName = componentParts.length > 0 ? componentParts[0] : \"\";\n const fallbackSymbol = componentParts.length > 0 ? componentParts[componentParts.length - 1] : \"\";\n const directEntry = normalizedComponentName ? importMap[normalizedComponentName] : null;\n const namespaceEntry = !directEntry && namespaceName ? importMap[namespaceName] : null;\n const importEntry = directEntry || namespaceEntry || null;\n const entryPath = importEntry && typeof importEntry.filePath === \"string\" ? importEntry.filePath : typeof importEntry === \"string\" ? importEntry : \"\";\n const entrySymbol = importEntry && typeof importEntry.symbolName === \"string\" ? importEntry.symbolName : \"\";\n const entryKind = importEntry && typeof importEntry.importKind === \"string\" ? importEntry.importKind : \"\";\n const componentPath = entryPath || \"\";\n let componentSymbol = fallbackSymbol;\n if (entrySymbol) {\n componentSymbol = entrySymbol;\n } else if (entryKind === \"namespace\" && componentParts.length > 1) {\n componentSymbol = componentParts[componentParts.length - 1];\n }\n mdxBlocks.push({\n tokenIndex,\n label,\n lineNumber: getLineNumberForOffset(inputText, offset),\n filePath: componentPath,\n symbolName: componentSymbol || \"\"\n });\n };\n const replaceWithToken = function(...args) {\n const match = args[0];\n const leadingNewline = args[1];\n const offset = args[args.length - 2];\n const inputText = args[args.length - 1];\n const safeLeading = typeof leadingNewline === \"string\" ? leadingNewline : \"\";\n const tokenIndex = rawBlocks.length;\n const rawBlock = typeof match === \"string\" ? match.trimStart() : \"\";\n rawBlocks.push(rawBlock);\n registerMdxBlock(\n rawBlock,\n tokenIndex,\n Math.max(0, (offset || 0) + safeLeading.length),\n inputText || source\n );\n return safeLeading + createToken(tokenIndex);\n };\n const mermaidFencePattern = new RegExp(\n \"(^|\\\\n)\\\\x60\\\\x60\\\\x60mermaid[^\\\\n]*\\\\n[\\\\s\\\\S]*?\\\\n\\\\x60\\\\x60\\\\x60(?=\\\\n|$)\",\n \"g\"\n );\n const tsxFencePattern = new RegExp(\n \"(^|\\\\n)\\\\x60\\\\x60\\\\x60(?:tsx|jsx)[^\\\\n]*\\\\n[\\\\s\\\\S]*?\\\\n\\\\x60\\\\x60\\\\x60(?=\\\\n|$)\",\n \"g\"\n );\n const htmlBlockPattern = new RegExp(\n \"(^|\\\\n)<([A-Za-z][\\\\w:-]*)(?:\\\\s[^>\\\\n]*)?>[\\\\s\\\\S]*?<\\\\/\\\\2>(?=\\\\n|$)\",\n \"g\"\n );\n const htmlSelfClosingPattern = new RegExp(\n \"(^|\\\\n)<[A-Za-z][\\\\w:-]*(?:\\\\s[^>\\\\n]*)?\\\\/>(?=\\\\n|$)\",\n \"g\"\n );\n let editorBody = source.replace(mermaidFencePattern, replaceWithToken);\n editorBody = editorBody.replace(tsxFencePattern, replaceWithToken);\n editorBody = editorBody.replace(htmlBlockPattern, replaceWithToken);\n editorBody = editorBody.replace(htmlSelfClosingPattern, replaceWithToken);\n return {\n editorBody,\n rawBlocks,\n mdxBlocks,\n tokenPrefix\n };\n}\nfunction restoreRawBlocksFromEditor(editorBody) {\n const source = typeof editorBody === \"string\" ? editorBody : \"\";\n if (!source || editorState.markdownRawBlocks.length === 0) {\n return source;\n }\n const rawBlockTokenPattern = getMarkdownRawBlockTokenPattern();\n return source.replace(rawBlockTokenPattern, function(match, indexText) {\n const index = Number(indexText);\n if (!Number.isInteger(index) || index < 0 || index >= editorState.markdownRawBlocks.length) {\n return match;\n }\n const rawBlock = editorState.markdownRawBlocks[index];\n return typeof rawBlock === \"string\" ? rawBlock : match;\n });\n}\nfunction handleMarkdownLocalChange(content, precomputedFullContent) {\n if (typeof content !== \"string\") {\n return;\n }\n editorState.markdownCurrentEditorContent = content;\n const fullContent = precomputedFullContent ?? composeMarkdownContent(restoreRawBlocksFromEditor(content));\n if (fullContent === editorState.markdownCurrentContent) {\n return;\n }\n editorState.markdownCurrentContent = fullContent;\n editorState.markdownHasUnsavedChanges = true;\n if (editorState.markdownYjsConnected) {\n syncLocalChangeToYText(fullContent);\n }\n scheduleMarkdownSync(fullContent);\n scheduleMarkdownSelectionOverlayRender();\n}\n\n// src/studio/bridge/bridge-messaging.ts\nvar studioOrigin = null;\nfunction postToStudio(message) {\n if (!window.parent || window.parent === window)\n return;\n try {\n window.parent.postMessage(message, studioOrigin || \"*\");\n } catch (e) {\n console.debug(\"[StudioBridge] postMessage failed:\", e);\n }\n}\nfunction isFromStudio(event) {\n try {\n if (!event.source || event.source === window)\n 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\"))\n 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 console.warn(\"[StudioBridge] Inline style injection may be blocked by CSP (style-src).\");\n }\n } catch (error) {\n console.warn(\n \"[StudioBridge] Failed to inject bridge styles. This may be caused by CSP style-src restrictions.\",\n error\n );\n }\n}\n\n// src/studio/bridge/bridge-utils.ts\nfunction debounce(fn, ms) {\n let timer;\n return function(...args) {\n clearTimeout(timer);\n timer = setTimeout(() => {\n fn.apply(this, args);\n }, ms);\n };\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)\n overlay.style.display = \"none\";\n}\nfunction positionOverlay(overlay, element, nodeName) {\n if (!overlay)\n 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)\n return vfId.split(\"_\")[0];\n return element.tagName.toLowerCase();\n}\nfunction findElementById(nodeId) {\n if (!nodeId)\n 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(el2) {\n return !!el2 && el2.nodeType === Node.ELEMENT_NODE && !DOM_IGNORE_TAGS.includes(el2.tagName) && !el2.hasAttribute(DATA_VF_IGNORE) && el2.style.display !== \"none\";\n}\nfunction getNodeType(el2) {\n const vfId = el2.getAttribute(DATA_VF_ID) || \"\";\n if (vfId && /^[A-Z]/.test(vfId))\n return \"component\";\n if (el2.hasAttribute(DATA_VF_TEXT))\n return \"text\";\n if (el2.getAttribute(DATA_NODE_SOURCE) === \"md\")\n return \"markdown\";\n return \"element\";\n}\nfunction buildNavigatorTree(root) {\n const config3 = getConfig();\n let nodeIndex = 0;\n function processElement(el2, parentId) {\n if (!isValidElement(el2)) {\n const children = [];\n Array.from(el2.children || []).forEach((child) => {\n children.push(...processElement(child, parentId));\n });\n return children;\n }\n let id = el2.getAttribute(DATA_VF_ID) || el2.getAttribute(DATA_NODE_ID) || el2.getAttribute(DATA_VF_SELECTOR);\n if (!id) {\n id = \"vf-\" + el2.tagName.toLowerCase() + \"-\" + ++nodeIndex;\n el2.setAttribute(DATA_VF_SELECTOR, id);\n }\n const vfId = el2.getAttribute(DATA_VF_ID);\n const name = vfId ? vfId.split(\"_\")[0] : el2.tagName.toLowerCase();\n const node = {\n id,\n name,\n type: getNodeType(el2),\n path: config3.pagePath,\n parentId,\n start: {\n line: parseInt(el2.getAttribute(DATA_NODE_LINE) || \"0\", 10),\n column: parseInt(el2.getAttribute(DATA_NODE_COLUMN) || \"0\", 10)\n },\n end: { line: 0, column: 0 },\n children: [],\n text: el2.hasAttribute(DATA_VF_TEXT) ? el2.textContent?.trim() : void 0,\n isRemote: false\n };\n Array.from(el2.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 };\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((el2) => isValidElement(el2));\n return validElements.length + \"-\" + validElements.map((el2) => el2.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)\n return;\n const signature = createTreeSignature(root);\n if (signature === state.lastTreeSignature)\n 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: window.__VERYFRONT_SOURCE_HASH__ || null\n });\n}\nfunction debouncedTreeUpdate() {\n if (treeUpdateTimer)\n clearTimeout(treeUpdateTimer);\n treeUpdateTimer = setTimeout(sendTreeUpdate, 150);\n}\nfunction setupMutationObserver() {\n const root = document.getElementById(\"root\") || document.body;\n if (!root)\n return;\n mutationObserver = new MutationObserver(function(mutations) {\n const hasRelevantChanges = mutations.some(\n (m) => m.type === \"childList\" || m.type === \"characterData\"\n );\n if (!hasRelevantChanges)\n 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 el2 = findElementById(nodeId);\n if (!el2) {\n hideOverlay(overlay);\n return;\n }\n positionOverlay(overlay, el2, getNodeName(el2));\n}\nfunction showHoverOverlay(nodeId) {\n showOverlay(state.hoverOverlay, nodeId);\n}\nfunction showSelectionOverlay(nodeId) {\n showOverlay(state.selectionOverlay, nodeId);\n}\nfunction scrollToElement(nodeId) {\n const el2 = document.querySelector(\"[\" + DATA_VF_ID + '=\"' + nodeId + '\"]') || document.querySelector(\"[\" + DATA_NODE_ID + '=\"' + nodeId + '\"]') || document.querySelector(\"[\" + DATA_VF_SELECTOR + '*=\"' + nodeId + '\"]');\n if (el2)\n el2.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n}\nfunction getDirectText(el2) {\n let text = \"\";\n for (let i = 0; i < el2.childNodes.length; i++) {\n if (el2.childNodes[i].nodeType === Node.TEXT_NODE) {\n text += el2.childNodes[i].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(el2) {\n return el2.getAttribute(DATA_VF_ID) || el2.getAttribute(DATA_NODE_ID) || el2.getAttribute(DATA_VF_SELECTOR);\n }\n document.addEventListener(\n \"click\",\n function(event) {\n if (!state.inspectMode)\n 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\")\n return;\n const target = event.target.closest(INSPECTABLE_SELECTOR);\n if (!target)\n return;\n const id = getElementId(target);\n if (id === state.hoveredNodeId)\n return;\n state.hoveredNodeId = id;\n showHoverOverlay(id);\n });\n document.addEventListener(\"pointerout\", function(event) {\n if (!state.inspectMode || event.pointerType === \"touch\")\n return;\n const target = event.target.closest(INSPECTABLE_SELECTOR);\n if (!target)\n return;\n const relatedTarget = event.relatedTarget;\n if (relatedTarget && target.contains(relatedTarget))\n return;\n state.hoveredNodeId = null;\n hideOverlay(state.hoverOverlay);\n });\n const updateOverlays = debounce(function() {\n if (state.inspectMode && state.hoveredNodeId)\n showHoverOverlay(state.hoveredNodeId);\n if (state.selectedNodeId)\n 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 CONSOLE_METHODS.forEach((method) => {\n state.originalConsole[method] = console[method];\n console[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)\n return { __isUndefined: true };\n if (arg === null)\n return null;\n if (typeof arg === \"function\") {\n return { __isFunction: true, name: arg.name || \"anonymous\" };\n }\n if (typeof arg === \"symbol\")\n return { __isSymbol: true, description: arg.description };\n if (typeof arg === \"object\")\n 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)\n return Promise.resolve();\n if (state.html2canvasPromise)\n 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 console.warn(\n \"[StudioBridge] Failed to load html2canvas script. This may be caused by CSP script-src restrictions.\",\n event\n );\n reject(new Error(\"Failed to load html2canvas script\"));\n };\n try {\n document.head.appendChild(script);\n } catch (error) {\n console.warn(\n \"[StudioBridge] Failed to append html2canvas script element. This may be caused by CSP script-src restrictions.\",\n 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 console.error(\n \"[bridge] html2canvas produced empty canvas:\",\n canvas?.width,\n \"x\",\n 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 console.error(\n \"[bridge] html2canvas produced invalid data URL:\",\n 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 console.error(\"[bridge] html2canvas error:\", error);\n window.scrollTo(0, originalScrollY);\n return {\n success: false,\n 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\nfunction handleStudioMessage(event) {\n if (!isFromStudio(event))\n return;\n const message = event.data;\n if (!message?.action)\n return;\n const config3 = getConfig();\n switch (message.action) {\n case \"routeChange\":\n if (message.url) {\n if (state.selectedNodeId) {\n state.selectedNodeId = null;\n hideOverlay(state.selectionOverlay);\n postToStudio({ action: \"setSelectedNode\", id: null });\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)\n return;\n hideOverlay(state.hoverOverlay);\n state.hoveredNodeId = null;\n if (!message.deselectElements)\n return;\n hideOverlay(state.selectionOverlay);\n state.selectedNodeId = null;\n return;\n case \"setSelectedNode\":\n state.selectedNodeId = message.id;\n showSelectionOverlay(message.id);\n if (message.scroll)\n scrollToElement(message.id);\n return;\n case \"setHoveredNode\":\n if (!state.inspectMode)\n showHoverOverlay(message.id);\n return;\n case \"setMarkdownPersistState\":\n return;\n case \"agentWriteMarkdown\": {\n const content = typeof message.content === \"string\" ? message.content : \"\";\n const mode = typeof message.mode === \"string\" ? message.mode : \"replace\";\n if (mode === \"replace\") {\n replaceYTextContent(content);\n } else if (mode === \"append\") {\n writeToYText(content);\n } else if (mode === \"insert_at\") {\n writeToYText(content, {\n position: typeof message.position === \"number\" ? message.position : 0,\n origin: \"agent-write\"\n });\n }\n return;\n }\n case \"contentChanged\": {\n if (!isMarkdownPage())\n return;\n if (message.fileId && editorState.markdownFileId && message.fileId !== editorState.markdownFileId) {\n return;\n }\n const content = typeof message.content === \"string\" ? message.content : null;\n const isEditMode = !!editorState.markdownEditorRoot && editorState.markdownEditorRoot.style.display === \"block\";\n if (isEditMode) {\n if (content !== null && !editorState.markdownYjsConnected) {\n applyMarkdownContent(content);\n }\n } else {\n window.location.reload();\n }\n return;\n }\n case \"screenshot\":\n (async function() {\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 postToStudio({\n action: \"screenshotResult\",\n requestId: message.requestId,\n multiple: false,\n ...result\n });\n })();\n return;\n default:\n console.debug(\"[StudioBridge] Unknown 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 if (!config3.wsUrl) {\n console.debug(\n \"[StudioBridge] Not in iframe and not studio_embed mode, skipping initialization\"\n );\n return;\n }\n }\n console.debug(\"[StudioBridge] Initializing...\");\n if (!isStandalone) {\n injectOverlayStyles();\n state.hoverOverlay = createOverlay(\"hover\");\n state.selectionOverlay = createOverlay(\"selection\");\n setupConsoleCapture();\n setupErrorHandling();\n setupInspectMode();\n }\n registerEditorCallbacks({\n onContentChange(fileId, filePath, content, save) {\n postToStudio({\n action: \"markdownContentChange\",\n fileId,\n filePath,\n content,\n ...save ? { save: true } : {}\n });\n },\n onEditorReady(fileId, filePath) {\n postToStudio({ action: \"markdownEditorReady\", fileId, filePath });\n },\n onOpenFile(filePath, lineNumber, columnNumber, symbolName) {\n const payload = {\n action: \"openFile\",\n filePath,\n lineNumber,\n columnNumber\n };\n if (symbolName) {\n payload.symbolName = symbolName;\n }\n postToStudio(payload);\n }\n });\n setupMarkdownEditor(params);\n window.addEventListener(\"message\", handleStudioMessage);\n if (!isStandalone) {\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", function() {\n notifyAppLoaded();\n setupMutationObserver();\n });\n } else {\n notifyAppLoaded();\n setupMutationObserver();\n }\n window.addEventListener(\"beforeunload\", notifyAppUnloaded);\n }\n const colorMode = params.get(\"color_mode\");\n if (colorMode)\n setColorMode(colorMode);\n if (!isStandalone) {\n const inspectModeParam = params.get(\"inspect_mode\");\n if (inspectModeParam === \"true\") {\n state.inspectMode = true;\n console.debug(\"[StudioBridge] Inspect mode enabled from query param\");\n }\n }\n console.debug(\"[StudioBridge] Initialized successfully\");\n}\n\n// src/studio/bridge/bridge-coordinator.ts\ninitConfig();\nvar config2 = getConfig();\nif (config2.debugExposeInternals && typeof window !== \"undefined\") {\n window.__VF_STUDIO_BRIDGE_DEBUG = {\n parseMdxImportMap,\n extractRawBlocksForEditor,\n getMdxBlockOpenUiState,\n writeToYText,\n replaceYTextContent\n };\n}\nif (!config2.debugSkipInit) {\n init();\n}\n";
|
|
@@ -31,8 +31,6 @@ interface InjectorOptions {
|
|
|
31
31
|
prefix?: string;
|
|
32
32
|
/** Elements to skip (in addition to defaults) */
|
|
33
33
|
skipElements?: string[];
|
|
34
|
-
/** Only inject into elements within this selector */
|
|
35
|
-
rootSelector?: string;
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
/** Inject data-vf-selector attributes into HTML for Studio Navigator */
|
package/src/src/task/runner.ts
CHANGED
|
@@ -4,10 +4,9 @@
|
|
|
4
4
|
* Executes a discovered task by calling its run() function
|
|
5
5
|
* with the appropriate context.
|
|
6
6
|
*/
|
|
7
|
-
import * as dntShim from "../../_dnt.shims.js";
|
|
8
|
-
|
|
9
7
|
|
|
10
8
|
import { logger as baseLogger } from "../utils/index.js";
|
|
9
|
+
import { env as getProcessEnv } from "../platform/compat/process.js";
|
|
11
10
|
import type { DiscoveredTask } from "./discovery.js";
|
|
12
11
|
import type { TaskContext } from "./types.js";
|
|
13
12
|
|
|
@@ -61,12 +60,15 @@ export async function runTask(options: RunTaskOptions): Promise<TaskRunResult> {
|
|
|
61
60
|
logger.info(`Running task "${task.id}" (${task.name})`);
|
|
62
61
|
}
|
|
63
62
|
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
const allEnv = getProcessEnv();
|
|
64
|
+
const env: Record<string, string> = envAllowlist
|
|
65
|
+
? Object.fromEntries(
|
|
66
|
+
envAllowlist.flatMap((k) => {
|
|
67
|
+
const value = allEnv[k];
|
|
68
|
+
return value === undefined ? [] : [[k, value] as const];
|
|
69
|
+
}),
|
|
70
|
+
)
|
|
71
|
+
: { ...allEnv };
|
|
70
72
|
|
|
71
73
|
const ctx: TaskContext = {
|
|
72
74
|
env,
|
package/src/src/tool/factory.ts
CHANGED
|
@@ -40,79 +40,79 @@ function buildSchemaFromShape(
|
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
function permissiveFallback(logPrefix: string, toolId: string, detail: string): JsonSchema {
|
|
44
|
+
agentLogger.info(`[${logPrefix}] ${detail} for "${toolId}"`);
|
|
45
|
+
return { type: "object", properties: {}, additionalProperties: true };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function schemaError(toolId: string, message: string): never {
|
|
49
|
+
throw toError(createError({ type: "agent", message: `Tool "${toolId}" ${message}` }));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function tryConvert(
|
|
53
|
+
fn: () => JsonSchema,
|
|
54
|
+
toolId: string,
|
|
55
|
+
logPrefix: string,
|
|
56
|
+
permissive: boolean,
|
|
57
|
+
errorDetail: string,
|
|
58
|
+
): JsonSchema | null {
|
|
59
|
+
try {
|
|
60
|
+
return fn();
|
|
61
|
+
} catch (error) {
|
|
62
|
+
if (permissive) return permissiveFallback(logPrefix, toolId, "Using permissive schema");
|
|
63
|
+
schemaError(toolId, `${errorDetail}: ${getErrorMessage(error)}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function logSchemaResult(logPrefix: string, toolId: string, method: string, schema: JsonSchema) {
|
|
68
|
+
agentLogger.info(
|
|
69
|
+
`[${logPrefix}] ${method} schema for "${toolId}": ${
|
|
70
|
+
Object.keys(schema.properties || {}).length
|
|
71
|
+
} properties`,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
43
75
|
function convertSchemaToJson(
|
|
44
76
|
schema: unknown,
|
|
45
77
|
toolId: string,
|
|
46
78
|
logPrefix: string,
|
|
47
79
|
permissive = false,
|
|
48
80
|
): JsonSchema {
|
|
49
|
-
const fallbackSchema: JsonSchema = permissive
|
|
50
|
-
? { type: "object", properties: {}, additionalProperties: true }
|
|
51
|
-
: { type: "object", properties: {} };
|
|
52
|
-
|
|
53
|
-
const usePermissiveFallback = (message: string): JsonSchema => {
|
|
54
|
-
agentLogger.info(message);
|
|
55
|
-
return fallbackSchema;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
81
|
if (hasValidZodTypeName(schema)) {
|
|
59
|
-
|
|
82
|
+
const result = tryConvert(
|
|
60
83
|
// deno-lint-ignore no-explicit-any
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
84
|
+
() => zodToJsonSchema(schema as any),
|
|
85
|
+
toolId,
|
|
86
|
+
logPrefix,
|
|
87
|
+
permissive,
|
|
88
|
+
"input schema conversion failed",
|
|
89
|
+
);
|
|
90
|
+
if (result) {
|
|
91
|
+
logSchemaResult(logPrefix, toolId, "Pre-converted", result);
|
|
67
92
|
return result;
|
|
68
|
-
} catch (error) {
|
|
69
|
-
if (permissive) {
|
|
70
|
-
return usePermissiveFallback(`[${logPrefix}] Using permissive schema for "${toolId}"`);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
throw toError(
|
|
74
|
-
createError({
|
|
75
|
-
type: "agent",
|
|
76
|
-
message: `Tool "${toolId}" input schema conversion failed: ${getErrorMessage(error)}`,
|
|
77
|
-
}),
|
|
78
|
-
);
|
|
79
93
|
}
|
|
80
94
|
}
|
|
81
95
|
|
|
82
96
|
const shape = getSchemaShape(schema as ZodLikeSchema);
|
|
83
97
|
if (shape) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
98
|
+
const result = tryConvert(
|
|
99
|
+
() => buildSchemaFromShape(shape, permissive),
|
|
100
|
+
toolId,
|
|
101
|
+
logPrefix,
|
|
102
|
+
permissive,
|
|
103
|
+
"schema introspection failed",
|
|
104
|
+
);
|
|
105
|
+
if (result) {
|
|
106
|
+
logSchemaResult(logPrefix, toolId, "Introspected", result);
|
|
91
107
|
return result;
|
|
92
|
-
} catch (error) {
|
|
93
|
-
if (permissive) {
|
|
94
|
-
return usePermissiveFallback(`[${logPrefix}] Using permissive schema for "${toolId}"`);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
throw toError(
|
|
98
|
-
createError({
|
|
99
|
-
type: "agent",
|
|
100
|
-
message: `Tool "${toolId}" schema introspection failed: ${getErrorMessage(error)}`,
|
|
101
|
-
}),
|
|
102
|
-
);
|
|
103
108
|
}
|
|
104
109
|
}
|
|
105
110
|
|
|
106
|
-
if (permissive)
|
|
107
|
-
return usePermissiveFallback(`[${logPrefix}] Using fully dynamic schema for "${toolId}"`);
|
|
108
|
-
}
|
|
111
|
+
if (permissive) return permissiveFallback(logPrefix, toolId, "Using fully dynamic schema");
|
|
109
112
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
message:
|
|
114
|
-
`Tool "${toolId}" input schema is not a valid Zod schema. Use the same Zod instance or set allowUnknownSchema to true.`,
|
|
115
|
-
}),
|
|
113
|
+
schemaError(
|
|
114
|
+
toolId,
|
|
115
|
+
"input schema is not a valid Zod schema. Use the same Zod instance or set allowUnknownSchema to true.",
|
|
116
116
|
);
|
|
117
117
|
}
|
|
118
118
|
|
|
@@ -46,26 +46,6 @@ export function isHttpUrl(specifier: string): boolean {
|
|
|
46
46
|
return specifier.startsWith("https://") || specifier.startsWith("http://");
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
/**
|
|
50
|
-
* Check if a URL is for React core packages.
|
|
51
|
-
*
|
|
52
|
-
* React core modules (react, react-dom) must NOT be cached/bundled.
|
|
53
|
-
* Instead, all packages use external=react and import from the same esm.sh URL.
|
|
54
|
-
* This prevents multiple React instances which causes "useContext is null" errors.
|
|
55
|
-
*/
|
|
56
|
-
export function _isReactCoreUrl(url: string): boolean {
|
|
57
|
-
try {
|
|
58
|
-
const parsed = new URL(url);
|
|
59
|
-
if (!parsed.hostname.includes("esm.sh")) return false;
|
|
60
|
-
|
|
61
|
-
const pathname = parsed.pathname.replace(/^\/(v\d+|stable)\//, "/");
|
|
62
|
-
const match = pathname.match(/^\/(react|react-dom)(@[\d.]+)?(?:\/|$|\?)/);
|
|
63
|
-
return match !== null;
|
|
64
|
-
} catch {
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
49
|
export function isExternalScheme(specifier: string): boolean {
|
|
70
50
|
return specifier.startsWith("node:") ||
|
|
71
51
|
specifier.startsWith("data:") ||
|