@upstart.gg/vite-plugins 0.1.29 → 0.1.31
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/dist/upstart-editor-api.d.ts +13 -1
- package/dist/upstart-editor-api.d.ts.map +1 -1
- package/dist/upstart-editor-api.js +59 -1
- package/dist/upstart-editor-api.js.map +1 -1
- package/dist/vite-plugin-upstart-attrs.d.ts +12 -7
- package/dist/vite-plugin-upstart-attrs.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-attrs.js +195 -3
- package/dist/vite-plugin-upstart-attrs.js.map +1 -1
- package/dist/vite-plugin-upstart-branding/plugin.d.ts +3 -3
- package/dist/vite-plugin-upstart-branding/plugin.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-branding/plugin.js.map +1 -1
- package/dist/vite-plugin-upstart-branding/runtime.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/plugin.d.ts +3 -3
- package/dist/vite-plugin-upstart-editor/plugin.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-editor/plugin.js +4 -1
- package/dist/vite-plugin-upstart-editor/plugin.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/click-handler.js +27 -16
- package/dist/vite-plugin-upstart-editor/runtime/click-handler.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/error-handler.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/hover-overlay.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/index.d.ts +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/index.js +14 -2
- package/dist/vite-plugin-upstart-editor/runtime/index.js.map +1 -1
- package/dist/{src/vite-plugin-upstart-editor → vite-plugin-upstart-editor}/runtime/state.d.ts +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/state.d.ts.map +1 -0
- package/dist/vite-plugin-upstart-editor/runtime/state.js.map +1 -0
- package/dist/vite-plugin-upstart-editor/runtime/text-editor.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/text-editor.js +212 -28
- package/dist/vite-plugin-upstart-editor/runtime/text-editor.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/types.d.ts +16 -3
- package/dist/vite-plugin-upstart-editor/runtime/types.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/utils.js.map +1 -1
- package/dist/vite-plugin-upstart-theme.d.ts +3 -3
- package/dist/vite-plugin-upstart-theme.d.ts.map +1 -1
- package/dist/vite-plugin-upstart-theme.js +2 -2
- package/dist/vite-plugin-upstart-theme.js.map +1 -1
- package/package.json +7 -7
- package/src/tests/vite-plugin-upstart-attrs.test.ts +298 -37
- package/src/upstart-editor-api.ts +71 -0
- package/src/vite-plugin-upstart-attrs.ts +293 -5
- package/src/vite-plugin-upstart-editor/plugin.ts +11 -1
- package/src/vite-plugin-upstart-editor/runtime/click-handler.ts +35 -21
- package/src/vite-plugin-upstart-editor/runtime/index.ts +21 -1
- package/src/vite-plugin-upstart-editor/runtime/text-editor.ts +260 -41
- package/src/vite-plugin-upstart-editor/runtime/types.ts +17 -4
- package/src/vite-plugin-upstart-theme.ts +4 -1
- package/dist/src/vite-plugin-upstart-editor/runtime/state.d.ts.map +0 -1
- package/dist/src/vite-plugin-upstart-editor/runtime/state.js.map +0 -1
- /package/dist/{src/vite-plugin-upstart-editor → vite-plugin-upstart-editor}/runtime/state.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite-plugin-upstart-attrs.js","names":[],"sources":["../src/vite-plugin-upstart-attrs.ts"],"sourcesContent":["import { createUnplugin } from \"unplugin\";\nimport { parseSync } from \"oxc-parser\";\nimport { walk } from \"zimmerframe\";\nimport MagicString from \"magic-string\";\nimport path from \"path\";\nimport type {\n Program,\n Node,\n JSXElement,\n JSXOpeningElement,\n JSXAttribute,\n Expression,\n CallExpression,\n} from \"estree-jsx\";\n\ninterface Options {\n enabled: boolean;\n emitRegistry?: boolean;\n}\n\ntype NodeWithRange = Node & { start: number; end: number };\n\nfunction hasRange(node: any): node is NodeWithRange {\n return node && typeof node.start === \"number\" && typeof node.end === \"number\";\n}\n\n// Fast, stable hash function (djb2 variant)\n// Produces a short hex string that's stable across rebuilds\nfunction hashContent(content: string): string {\n let hash = 5381;\n for (let i = 0; i < content.length; i++) {\n hash = ((hash << 5) + hash) ^ content.charCodeAt(i);\n }\n // Convert to unsigned 32-bit and then to hex\n return (hash >>> 0).toString(16);\n}\n\ninterface LoopContext {\n itemName: string;\n indexName: string | null;\n arrayExpr: string;\n}\n\ninterface I18nKeyInfo {\n /** The resolved key with namespace, e.g. \"dashboard:features.title\" */\n fullKey: string;\n /** Just the translation key, e.g. \"features.title\" */\n key: string;\n /** The namespace, e.g. \"dashboard\" */\n namespace: string;\n /** Source expression for i18nKey when dynamic (e.g. \"stat.labelKey\"), null when static */\n keyExpr: string | null;\n /** Source expression for namespace when dynamic, null when static */\n nsExpr: string | null;\n}\n\n// Registry entry for editable elements (text and className)\nexport interface EditableEntry {\n file: string;\n type: \"text\" | \"className\";\n startOffset: number;\n endOffset: number;\n originalContent: string;\n context: { parentTag: string };\n}\n\n// Module-level registry (collected across all files during build)\nconst editableRegistry = new Map<string, EditableEntry>();\n\n// Generate stable ID for editable elements\n// Replace the counter-based ID generation with position-based\nfunction generateId(filePath: string, node: NodeWithRange): string {\n // Use file path + start position for a stable, deterministic ID\n return `${filePath}:${node.start}`;\n}\n\n// Export for testing - get a copy of the current registry\nexport function getRegistry(): Record<string, EditableEntry> {\n return Object.fromEntries(editableRegistry);\n}\n\n// Export for testing - clear registry and counters\nexport function clearRegistry(): void {\n editableRegistry.clear();\n}\n\ninterface TransformState {\n filePath: string;\n code: string;\n s: MagicString;\n loopStack: LoopContext[];\n /** Tracks const varName = \"literal\" declarations for resolving dynamic i18nKey expressions */\n constants: Map<string, string>;\n /** Maps t-function variable names to their default namespace, e.g. { \"t\": \"dashboard\" } */\n tFunctions: Map<string, string>;\n}\n\nexport const upstartEditor = createUnplugin<Options>((options) => {\n if (!options.enabled) {\n return { name: \"upstart-editor-disabled\" };\n }\n\n const emitRegistry = options.emitRegistry ?? true;\n let root = process.cwd();\n\n return {\n name: \"upstart-editor\",\n enforce: \"pre\",\n\n vite: {\n configResolved(config) {\n root = config.root;\n },\n generateBundle() {\n if (!emitRegistry || editableRegistry.size === 0) {\n return;\n }\n\n const registry = {\n version: 1,\n generatedAt: new Date().toISOString(),\n elements: Object.fromEntries(editableRegistry),\n };\n\n this.emitFile({\n type: \"asset\",\n fileName: \"upstart-registry.json\",\n source: JSON.stringify(registry, null, 2),\n });\n\n // Clear for next build\n editableRegistry.clear();\n },\n },\n\n transformInclude(id) {\n return /\\.(tsx|jsx)$/.test(id) && !id.includes(\"node_modules\");\n },\n\n transform(code, id) {\n // Fast path: skip files without JSX\n if (!code.includes(\"<\")) {\n return null;\n }\n\n try {\n const relativePath = path.relative(root, id);\n const result = transformWithOxc(code, relativePath);\n\n if (!result) {\n return null;\n }\n\n return {\n code: result.code,\n map: result.map,\n };\n } catch (error) {\n console.error(`Error transforming ${id}:`, error);\n return null;\n }\n },\n };\n});\n\nexport function transformWithOxc(code: string, filePath: string) {\n // Parse with oxc (super fast!)\n const ast = parseSync(filePath, code, {\n sourceType: \"module\",\n });\n\n if (!ast.program) {\n return null;\n }\n\n const s = new MagicString(code);\n const state: TransformState = {\n filePath,\n code,\n s,\n loopStack: [],\n constants: new Map(),\n tFunctions: new Map(),\n };\n\n let modified = false;\n\n // Use zimmerframe to walk and transform the AST\n walk(ast.program as Program, state, {\n _(node: Node, { state, next }: { state: TransformState; next: () => void }) {\n // Track useTranslation() calls to resolve t() function namespaces\n if (node.type === \"VariableDeclaration\") {\n trackUseTranslation(node, state);\n\n // Track const varName = \"literal\" for resolving dynamic i18nKey expressions\n for (const decl of (node as any).declarations) {\n if (\n decl.type === \"VariableDeclarator\" &&\n decl.id?.type === \"Identifier\" &&\n decl.init?.type === \"Literal\" &&\n typeof decl.init.value === \"string\"\n ) {\n state.constants.set(decl.id.name, decl.init.value);\n }\n }\n }\n\n // Handle .map() calls to track loops (must be before JSXElement)\n if (node.type === \"CallExpression\") {\n const mapCallInfo = detectAndPatchMapCall(node as CallExpression, state);\n\n if (mapCallInfo) {\n // Push loop context\n state.loopStack.push(mapCallInfo);\n\n // Continue walking into the callback\n next();\n\n // Pop loop context after traversing\n state.loopStack.pop();\n return;\n }\n }\n\n // Handle JSX elements\n if (node.type === \"JSXElement\") {\n const jsxNode = node as JSXElement;\n const opening = jsxNode.openingElement;\n const tagName = getJSXElementName(opening);\n\n // Skip ALL attribute injection for <Trans> elements.\n // <Trans> renders to a text node at runtime, so DOM attributes are lost.\n // The i18n attributes are promoted to the parent element instead.\n if (tagName === \"Trans\") {\n next();\n return;\n }\n\n const insertPos = getAttributeInsertPosition(opening, state.code);\n\n // Track attributes to inject\n const attributes: string[] = [];\n\n // Compute stable hash from the element's source code (includes all children)\n // This hash changes if ANY part of the element or its children change\n if (hasRange(jsxNode)) {\n const elementSource = state.code.slice(jsxNode.start, jsxNode.end);\n const hash = hashContent(elementSource);\n\n // Collect suffix expressions from all enclosing loops so elements\n // inside .map() get a unique hash per iteration at runtime.\n // detectAndPatchMapCall() attempts to auto-inject an index param when missing.\n // If injection is not possible (no stable source range), fall back to \"0\".\n const loopSuffixes = state.loopStack.map((l) => (l.indexName ? `\\${${l.indexName}}` : \"0\"));\n\n if (loopSuffixes.length > 0) {\n const suffix = loopSuffixes.join(\"-\");\n attributes.push(`data-upstart-hash={\\`${hash}-${suffix}\\`}`);\n } else {\n attributes.push(`data-upstart-hash=\"${hash}\"`);\n }\n }\n\n // --- Editable text detection ---\n // Priority: i18n (true) > non-i18n text (false) > no text (no attribute)\n\n // Step 1: Check for Trans children AND t() calls (editable via i18n, gets \"true\")\n const transChildren = findTransInChildren(jsxNode, state.code, state.constants);\n const tCallChildren = findTCallsInChildren(jsxNode, state.code, state);\n const allI18nKeys = [...transChildren, ...tCallChildren];\n const hasI18n = allI18nKeys.length > 0;\n\n if (hasI18n) {\n attributes.push('data-upstart-editable-text=\"true\"');\n attributes.push('data-upstart-editable-text-mode=\"plain\"');\n\n const hasDynamic = allI18nKeys.some((t) => t.keyExpr || t.nsExpr);\n if (hasDynamic) {\n // Build a runtime JSX template expression for dynamic i18n keys\n const parts = allI18nKeys.map((t) => {\n const nsPart = t.nsExpr ? `\\${${t.nsExpr}}` : t.namespace;\n const keyPart = t.keyExpr ? `\\${${t.keyExpr}}` : t.key;\n return `${nsPart}:${keyPart}`;\n });\n attributes.push(`data-upstart-i18n={\\`${parts.join(\",\")}\\`}`);\n } else {\n const keys = allI18nKeys.map((t: I18nKeyInfo) => escapeProp(t.fullKey)).join(\",\");\n attributes.push(`data-upstart-i18n=\"${keys}\"`);\n }\n }\n\n // Step 2: Text leaf elements — registry tracking + non-editable flag\n if (isTextLeafElement(jsxNode)) {\n if (!hasI18n) {\n attributes.push('data-upstart-editable-text=\"false\"');\n }\n\n // Find the actual JSXText node to track its location\n const textChild = jsxNode.children.find((c) => c.type === \"JSXText\" && (c as any).value?.trim());\n\n if (textChild && hasRange(textChild)) {\n const id = generateId(state.filePath, textChild);\n const textValue = (textChild as any).value as string;\n\n // Calculate trimmed offsets (exclude leading/trailing whitespace)\n const trimmedStart = textChild.start + (textValue.length - textValue.trimStart().length);\n const trimmedEnd = textChild.end - (textValue.length - textValue.trimEnd().length);\n\n editableRegistry.set(id, {\n file: state.filePath,\n type: \"text\",\n startOffset: trimmedStart,\n endOffset: trimmedEnd,\n originalContent: textValue.trim(),\n context: { parentTag: tagName || \"unknown\" },\n });\n\n attributes.push(`data-upstart-id=\"${id}\"`);\n }\n }\n // Step 3: Non-leaf elements with visible text content (expressions, mixed content)\n else if (!hasI18n && hasVisibleTextContent(jsxNode)) {\n attributes.push('data-upstart-editable-text=\"false\"');\n }\n\n // Track className attribute if it's a string literal\n const classNameAttr = opening.attributes.find(\n (attr): attr is JSXAttribute =>\n attr.type === \"JSXAttribute\" &&\n attr.name.type === \"JSXIdentifier\" &&\n attr.name.name === \"className\" &&\n attr.value?.type === \"Literal\" &&\n typeof (attr.value as any).value === \"string\",\n );\n\n if (classNameAttr && classNameAttr.value && hasRange(classNameAttr.value)) {\n const id = generateId(state.filePath, classNameAttr.value);\n const classValue = (classNameAttr.value as any).value as string;\n\n // +1 and -1 to exclude the quotes\n editableRegistry.set(id, {\n file: state.filePath,\n type: \"className\",\n startOffset: classNameAttr.value.start + 1,\n endOffset: classNameAttr.value.end - 1,\n originalContent: classValue,\n context: { parentTag: tagName || \"unknown\" },\n });\n\n attributes.push(`data-upstart-classname-id=\"${id}\"`);\n }\n\n // Process PascalCase components for additional tracking\n if (tagName && /^[A-Z]/.test(tagName)) {\n // File and component tracking\n attributes.push(`data-upstart-file=\"${escapeProp(state.filePath)}\"`);\n attributes.push(`data-upstart-component=\"${escapeProp(tagName)}\"`);\n\n // Loop context tracking\n if (state.loopStack.length > 0) {\n const loop = state.loopStack[state.loopStack.length - 1];\n attributes.push(`data-upstart-loop-item=\"${escapeProp(loop.itemName)}\"`);\n if (loop.indexName) {\n attributes.push(`data-upstart-loop-index={${loop.indexName}}`);\n }\n attributes.push(`data-upstart-loop-array=\"${escapeProp(loop.arrayExpr)}\"`);\n }\n\n // Analyze each prop for data bindings\n for (const attr of opening.attributes) {\n if (attr.type !== \"JSXAttribute\" || attr.name.type !== \"JSXIdentifier\") {\n continue;\n }\n\n const propName = attr.name.name;\n const binding = analyzeBinding(attr.value, state.code);\n\n if (binding) {\n attributes.push(`data-upstart-prop-${propName.toLowerCase()}=\"${escapeProp(binding.path)}\"`);\n\n // Track conditional expressions\n if (binding.isConditional) {\n attributes.push(`data-upstart-conditional-${propName.toLowerCase()}=\"true\"`);\n }\n }\n }\n }\n\n // Inject attributes if any\n if (insertPos !== -1 && attributes.length > 0) {\n const attrString = \" \" + attributes.join(\" \");\n state.s.appendLeft(insertPos, attrString);\n modified = true;\n }\n }\n\n next();\n },\n });\n\n if (!modified) {\n return null;\n }\n\n return {\n code: s.toString(),\n map: s.generateMap({ hires: true }),\n };\n}\n\n// Helper: Get JSX element name\nfunction getJSXElementName(opening: JSXOpeningElement): string | null {\n if (opening.name.type === \"JSXIdentifier\") {\n return opening.name.name;\n }\n\n // Handle JSXMemberExpression like <Foo.Bar>\n if (opening.name.type === \"JSXMemberExpression\") {\n let current = opening.name;\n while (current.property) {\n if (current.property.type === \"JSXIdentifier\") {\n return current.property.name;\n }\n if (current.type === \"JSXMemberExpression\") {\n current = current.property as any;\n } else {\n break;\n }\n }\n }\n\n return null;\n}\n\n// Helper: Find where to insert attributes in JSX opening tag\nfunction getAttributeInsertPosition(opening: JSXOpeningElement, code: string): number {\n // If there are existing attributes, insert before the first one\n if (opening.attributes.length > 0) {\n const firstAttr = opening.attributes[0];\n if (hasRange(firstAttr)) {\n return firstAttr.start;\n }\n }\n\n // Otherwise, insert after the tag name\n if (opening.name.type === \"JSXIdentifier\" && hasRange(opening.name)) {\n return opening.name.end;\n }\n\n if (opening.name.type === \"JSXMemberExpression\" && hasRange(opening.name)) {\n return opening.name.end;\n }\n\n return -1;\n}\n\n// Helper: Analyze a prop value to extract data binding\nfunction analyzeBinding(\n value: JSXAttribute[\"value\"],\n code: string,\n): {\n path: string;\n isConditional?: boolean;\n} | null {\n if (!value) {\n return null;\n }\n\n if (value.type === \"JSXExpressionContainer\") {\n const expr = value.expression;\n\n if (expr.type === \"JSXEmptyExpression\") {\n return null;\n }\n\n return analyzeExpression(expr, code);\n }\n\n return null;\n}\n\n// Helper: Analyze any expression to extract binding info\nfunction analyzeExpression(\n expr: Expression,\n code: string,\n): {\n path: string;\n isConditional?: boolean;\n} | null {\n // Handle member expressions: user.name, product.price, etc.\n if (expr.type === \"MemberExpression\") {\n const path = exprToString(expr, code);\n return { path };\n }\n\n // Handle identifiers: userName, price, etc.\n if (expr.type === \"Identifier\") {\n return { path: expr.name };\n }\n\n // Handle conditional expressions: condition ? value1 : value2\n if (expr.type === \"ConditionalExpression\") {\n // Try to extract from the consequent\n const consequent = analyzeExpression(expr.consequent, code);\n if (consequent) {\n return {\n ...consequent,\n isConditional: true,\n path: exprToString(expr, code),\n };\n }\n }\n\n // Handle logical expressions: value1 && value2, value1 || value2\n if (expr.type === \"LogicalExpression\") {\n const right = analyzeExpression(expr.right, code);\n if (right) {\n return {\n ...right,\n isConditional: true,\n path: exprToString(expr, code),\n };\n }\n }\n\n // For other complex expressions, just return the path\n const exprWithRange = expr as any;\n if (exprWithRange.start !== undefined && exprWithRange.end !== undefined) {\n return {\n path: code.slice(exprWithRange.start, exprWithRange.end),\n };\n }\n\n return null;\n}\n\n// Helper: Convert expression AST to string\nfunction exprToString(expr: Expression, code: string): string {\n // Use the source range to get the actual code\n const exprWithRange = expr as any;\n if (exprWithRange.start !== undefined && exprWithRange.end !== undefined) {\n return code.slice(exprWithRange.start, exprWithRange.end);\n }\n\n // Fallback: reconstruct from AST\n if (expr.type === \"Identifier\") {\n return expr.name;\n }\n\n if (expr.type === \"MemberExpression\") {\n const obj = exprToString(expr.object as Expression, code);\n const prop =\n expr.property.type === \"Identifier\" && !expr.computed\n ? expr.property.name\n : exprToString(expr.property as Expression, code);\n return expr.computed ? `${obj}[${prop}]` : `${obj}.${prop}`;\n }\n\n if (expr.type === \"ConditionalExpression\") {\n const test = exprToString(expr.test, code);\n const consequent = exprToString(expr.consequent, code);\n const alternate = exprToString(expr.alternate, code);\n return `${test} ? ${consequent} : ${alternate}`;\n }\n\n if (expr.type === \"LogicalExpression\") {\n const left = exprToString(expr.left, code);\n const right = exprToString(expr.right, code);\n return `${left} ${expr.operator} ${right}`;\n }\n\n return \"\";\n}\n\n// Helper: Detect .map() calls and auto-inject index parameter if missing\nfunction detectAndPatchMapCall(node: CallExpression, state: TransformState): LoopContext | null {\n // Check if this is a .map() call\n if (\n node.callee.type !== \"MemberExpression\" ||\n node.callee.property.type !== \"Identifier\" ||\n node.callee.property.name !== \"map\"\n ) {\n return null;\n }\n\n const callback = node.arguments[0];\n\n if (!callback) {\n return null;\n }\n\n // Check if callback is an arrow function or function expression\n if (callback.type !== \"ArrowFunctionExpression\" && callback.type !== \"FunctionExpression\") {\n return null;\n }\n\n const params = callback.params;\n const itemParam = params[0];\n const indexParam = params[1];\n\n if (!itemParam) {\n return null;\n }\n\n // Extract parameter names\n const itemName = itemParam.type === \"Identifier\" ? itemParam.name : \"item\";\n let indexName = indexParam?.type === \"Identifier\" ? indexParam.name : null;\n\n // If no index parameter exists in arrow function, auto-inject it\n if (!indexName && callback.type === \"ArrowFunctionExpression\") {\n indexName = \"__i\";\n\n // Find the closing paren of the parameter list safely\n const lastParam = params[params.length - 1];\n\n // ESTree `Pattern` type doesn't guarantee source range fields in typings.\n // Guard before reading offsets.\n if (!hasRange(lastParam)) {\n // Can't patch callback params without a stable source offset.\n // Keep indexName as null so hash suffix generation can handle this case.\n indexName = null;\n return {\n itemName,\n indexName,\n arrayExpr: exprToString(node.callee.object as Expression, state.code),\n };\n }\n\n let searchPos = lastParam.end;\n\n // Find the next ) after the last parameter\n while (searchPos < state.code.length && state.code[searchPos] !== \")\") {\n searchPos++;\n }\n\n if (searchPos < state.code.length && state.code[searchPos] === \")\") {\n // Insert the parameter before the closing paren\n state.s.appendLeft(searchPos, \", __i\");\n }\n }\n\n // Get the array expression\n const arrayExpr = exprToString(node.callee.object as Expression, state.code);\n\n return {\n itemName,\n indexName,\n arrayExpr,\n };\n}\n\n// Helper: Escape prop values for JSX attributes\nfunction escapeProp(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n\n// Helper: Track useTranslation() calls to map t-function variable names to their namespace\nfunction trackUseTranslation(node: Node, state: TransformState): void {\n if (node.type !== \"VariableDeclaration\") return;\n\n const decl = node as any;\n for (const declarator of decl.declarations) {\n if (\n declarator.id?.type !== \"ObjectPattern\" ||\n !declarator.init ||\n declarator.init.type !== \"CallExpression\"\n ) {\n continue;\n }\n\n const callExpr = declarator.init;\n if (callExpr.callee?.type !== \"Identifier\" || callExpr.callee.name !== \"useTranslation\") {\n continue;\n }\n\n const namespace = extractUseTranslationNamespace(callExpr);\n\n for (const prop of declarator.id.properties) {\n if (prop.type !== \"Property\") continue;\n if (prop.key?.type !== \"Identifier\" || prop.key.name !== \"t\") continue;\n\n // Handle aliased destructuring: { t: translate } → maps \"translate\" to namespace\n const localName = prop.value?.type === \"Identifier\" ? prop.value.name : \"t\";\n state.tFunctions.set(localName, namespace);\n }\n }\n}\n\n// Helper: Extract namespace from useTranslation() call arguments\nfunction extractUseTranslationNamespace(callExpr: any): string {\n const firstArg = callExpr.arguments?.[0];\n if (!firstArg) return \"translation\";\n\n // String literal: useTranslation(\"dashboard\")\n if (firstArg.type === \"Literal\" && typeof firstArg.value === \"string\") {\n return firstArg.value;\n }\n\n // Array expression: useTranslation([\"dashboard\", \"common\"])\n if (firstArg.type === \"ArrayExpression\" && firstArg.elements?.length > 0) {\n const first = firstArg.elements[0];\n if (first?.type === \"Literal\" && typeof first.value === \"string\") {\n return first.value;\n }\n }\n\n return \"translation\";\n}\n\n// Helper: Resolve a JSX attribute value to a string.\n// Handles string literals directly, and JSXExpressionContainer by resolving\n// Identifier references via the constants map, or returning the expression source for dynamic values.\nfunction resolveJSXAttrValue(\n value: any,\n code: string,\n constants: Map<string, string>,\n): { value: string; expr: string | null } | null {\n if (!value) return null;\n\n if (value.type === \"Literal\" && typeof value.value === \"string\") {\n return { value: value.value, expr: null };\n }\n\n if (value.type === \"JSXExpressionContainer\") {\n const expression = value.expression;\n if (!expression) return null;\n\n // Resolve simple identifiers via constants map (e.g. i18nKey={labelKey} where const labelKey = \"...\")\n if (expression.type === \"Identifier\" && constants.has(expression.name)) {\n return { value: constants.get(expression.name)!, expr: null };\n }\n\n // Dynamic expression — return source text so caller can emit a runtime JSX expression\n if (hasRange(expression)) {\n const src = code.slice(expression.start, expression.end);\n return { value: src, expr: src };\n }\n }\n\n return null;\n}\n\n// Helper: Detect <Trans i18nKey=\"...\" /> component and extract i18n key info\nfunction detectTransComponent(\n jsxElement: JSXElement,\n code: string,\n constants: Map<string, string>,\n): I18nKeyInfo | null {\n const opening = jsxElement.openingElement;\n const tagName = getJSXElementName(opening);\n\n if (tagName !== \"Trans\") return null;\n\n let keyResult: { value: string; expr: string | null } | null = null;\n let nsResult: { value: string; expr: string | null } | null = null;\n\n for (const attr of opening.attributes) {\n if (attr.type !== \"JSXAttribute\" || attr.name.type !== \"JSXIdentifier\") continue;\n\n const attrName = attr.name.name;\n\n if (attrName === \"i18nKey\") {\n keyResult = resolveJSXAttrValue(attr.value, code, constants);\n }\n\n if (attrName === \"ns\") {\n nsResult = resolveJSXAttrValue(attr.value, code, constants);\n }\n }\n\n if (!keyResult) {\n return null;\n }\n\n const key = keyResult.value;\n const namespace = nsResult?.value ?? \"translation\";\n const keyExpr = keyResult.expr;\n const nsExpr = nsResult?.expr ?? null;\n\n return {\n fullKey: `${namespace}:${key}`,\n key,\n namespace,\n keyExpr,\n nsExpr,\n };\n}\n\n// Helper: Scan an element's direct children for <Trans> components and extract i18n key info.\n// Handles direct <Trans> children and <Trans> inside JSXExpressionContainer (e.g. {show && <Trans>}).\nfunction findTransInChildren(\n jsxElement: JSXElement,\n code: string,\n constants: Map<string, string>,\n): I18nKeyInfo[] {\n const results: I18nKeyInfo[] = [];\n\n for (const child of jsxElement.children) {\n if (child.type === \"JSXElement\") {\n const info = detectTransComponent(child as JSXElement, code, constants);\n if (info) results.push(info);\n }\n\n if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (expr && expr.type !== \"JSXEmptyExpression\") {\n findTransInExpression(expr, code, results, constants);\n }\n }\n }\n\n return results;\n}\n\n// Helper: Recursively search an expression tree for <Trans> JSXElements.\n// Handles LogicalExpression (&&, ||), ConditionalExpression (?:), CallExpression (.map()),\n// ArrowFunctionExpression/FunctionExpression (callbacks), BlockStatement, and direct JSXElement.\nfunction findTransInExpression(\n expr: any,\n code: string,\n results: I18nKeyInfo[],\n constants: Map<string, string>,\n): void {\n if (!expr || !expr.type) return;\n\n if (expr.type === \"JSXElement\") {\n const info = detectTransComponent(expr as JSXElement, code, constants);\n if (info) results.push(info);\n return;\n }\n\n if (expr.type === \"LogicalExpression\") {\n findTransInExpression(expr.left, code, results, constants);\n findTransInExpression(expr.right, code, results, constants);\n return;\n }\n\n if (expr.type === \"ConditionalExpression\") {\n findTransInExpression(expr.consequent, code, results, constants);\n findTransInExpression(expr.alternate, code, results, constants);\n return;\n }\n\n // Handle .map() and other call expressions — recurse into arguments\n if (expr.type === \"CallExpression\") {\n for (const arg of expr.arguments) {\n findTransInExpression(arg, code, results, constants);\n }\n return;\n }\n\n // Handle arrow/function expressions — recurse into body\n if (expr.type === \"ArrowFunctionExpression\" || expr.type === \"FunctionExpression\") {\n findTransInExpression(expr.body, code, results, constants);\n return;\n }\n\n // Handle block bodies (arrow functions with braces)\n if (expr.type === \"BlockStatement\") {\n for (const stmt of expr.body) {\n if (stmt.type === \"ReturnStatement\" && stmt.argument) {\n findTransInExpression(stmt.argument, code, results, constants);\n }\n }\n return;\n }\n}\n\n// Helper: Scan an element's direct children for t() function calls and extract i18n key info.\nfunction findTCallsInChildren(jsxElement: JSXElement, code: string, state: TransformState): I18nKeyInfo[] {\n if (state.tFunctions.size === 0) return [];\n\n const results: I18nKeyInfo[] = [];\n\n for (const child of jsxElement.children) {\n if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (expr && expr.type !== \"JSXEmptyExpression\") {\n findTCallInExpression(expr, code, results, state);\n }\n }\n }\n\n return results;\n}\n\n// Helper: Recursively search an expression tree for t() calls.\n// Handles LogicalExpression (&&, ||), ConditionalExpression (?:), CallExpression (.map()),\n// ArrowFunctionExpression/FunctionExpression (callbacks), BlockStatement, and direct CallExpression.\nfunction findTCallInExpression(expr: any, code: string, results: I18nKeyInfo[], state: TransformState): void {\n if (!expr || !expr.type) return;\n\n if (expr.type === \"CallExpression\") {\n const info = detectTCall(expr, code, state);\n if (info) {\n results.push(info);\n return;\n }\n // Not a t() call — recurse into arguments (for .map() callbacks etc.)\n for (const arg of expr.arguments) {\n findTCallInExpression(arg, code, results, state);\n }\n return;\n }\n\n if (expr.type === \"LogicalExpression\") {\n findTCallInExpression(expr.left, code, results, state);\n findTCallInExpression(expr.right, code, results, state);\n return;\n }\n\n if (expr.type === \"ConditionalExpression\") {\n findTCallInExpression(expr.consequent, code, results, state);\n findTCallInExpression(expr.alternate, code, results, state);\n return;\n }\n\n if (expr.type === \"ArrowFunctionExpression\" || expr.type === \"FunctionExpression\") {\n findTCallInExpression(expr.body, code, results, state);\n return;\n }\n\n if (expr.type === \"BlockStatement\") {\n for (const stmt of expr.body) {\n if (stmt.type === \"ReturnStatement\" && stmt.argument) {\n findTCallInExpression(stmt.argument, code, results, state);\n }\n }\n return;\n }\n}\n\n// Helper: Detect a t(\"key\") call and extract i18n key info.\nfunction detectTCall(callExpr: any, code: string, state: TransformState): I18nKeyInfo | null {\n if (callExpr.callee?.type !== \"Identifier\") return null;\n\n const calleeName = callExpr.callee.name;\n const namespace = state.tFunctions.get(calleeName);\n if (namespace === undefined) return null;\n\n const firstArg = callExpr.arguments?.[0];\n if (!firstArg) return null;\n\n // Static string key: t(\"features.title\")\n if (firstArg.type === \"Literal\" && typeof firstArg.value === \"string\") {\n const key = firstArg.value;\n return { fullKey: `${namespace}:${key}`, key, namespace, keyExpr: null, nsExpr: null };\n }\n\n // Dynamic key — try constant resolution first, then fall back to expression\n if (hasRange(firstArg)) {\n const exprSrc = code.slice(firstArg.start, firstArg.end);\n\n if (firstArg.type === \"Identifier\" && state.constants.has(firstArg.name)) {\n const key = state.constants.get(firstArg.name)!;\n return { fullKey: `${namespace}:${key}`, key, namespace, keyExpr: null, nsExpr: null };\n }\n\n return { fullKey: `${namespace}:${exprSrc}`, key: exprSrc, namespace, keyExpr: exprSrc, nsExpr: null };\n }\n\n return null;\n}\n\n// Helper: Check if element is a \"leaf\" with only static text (no nested elements or expressions)\nfunction isTextLeafElement(jsxElement: JSXElement): boolean {\n let hasText = false;\n\n for (const child of jsxElement.children) {\n if (child.type === \"JSXText\") {\n const textValue = (child as any).value;\n if (textValue?.trim()) {\n hasText = true;\n }\n } else if (\n child.type === \"JSXElement\" ||\n child.type === \"JSXFragment\" ||\n child.type === \"JSXExpressionContainer\" ||\n child.type === \"JSXSpreadChild\"\n ) {\n // Has non-text children, not a leaf element\n return false;\n }\n }\n\n return hasText;\n}\n\n// Helper: Check if element has any visible text content (static or dynamic).\n// Broader than isTextLeafElement — returns true even if the element has other child types.\n// Detects JSXText with non-whitespace content and expression containers with\n// text-producing expressions (Identifier, MemberExpression, Literal, TemplateLiteral).\n// Skips CallExpression, ConditionalExpression, LogicalExpression (these typically produce elements).\nfunction hasVisibleTextContent(jsxElement: JSXElement): boolean {\n for (const child of jsxElement.children) {\n if (child.type === \"JSXText\") {\n const textValue = (child as any).value;\n if (textValue?.trim()) {\n return true;\n }\n continue;\n }\n\n if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (!expr || expr.type === \"JSXEmptyExpression\") {\n continue;\n }\n if (\n expr.type === \"Identifier\" ||\n expr.type === \"MemberExpression\" ||\n expr.type === \"Literal\" ||\n expr.type === \"TemplateLiteral\"\n ) {\n return true;\n }\n }\n }\n\n return false;\n}\n\nexport default upstartEditor.vite;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"vite-plugin-upstart-attrs.js","names":[],"sources":["../src/vite-plugin-upstart-attrs.ts"],"sourcesContent":["import { createUnplugin } from \"unplugin\";\nimport { parseSync } from \"oxc-parser\";\nimport { walk } from \"zimmerframe\";\nimport MagicString from \"magic-string\";\nimport path from \"path\";\nimport fs from \"node:fs\";\nimport type {\n Program,\n Node,\n JSXElement,\n JSXOpeningElement,\n JSXAttribute,\n Expression,\n CallExpression,\n} from \"estree-jsx\";\n\ninterface Options {\n enabled: boolean;\n emitRegistry?: boolean;\n}\n\ntype NodeWithRange = Node & { start: number; end: number };\n\nfunction hasRange(node: any): node is NodeWithRange {\n return node && typeof node.start === \"number\" && typeof node.end === \"number\";\n}\n\n// Fast, stable hash function (djb2 variant)\n// Produces a short hex string that's stable across rebuilds\nfunction hashContent(content: string): string {\n let hash = 5381;\n for (let i = 0; i < content.length; i++) {\n hash = ((hash << 5) + hash) ^ content.charCodeAt(i);\n }\n // Convert to unsigned 32-bit and then to hex\n return (hash >>> 0).toString(16);\n}\n\ninterface LoopContext {\n itemName: string;\n indexName: string | null;\n arrayExpr: string;\n}\n\ninterface I18nKeyInfo {\n /** The resolved key with namespace, e.g. \"dashboard:features.title\" */\n fullKey: string;\n /** Just the translation key, e.g. \"features.title\" */\n key: string;\n /** The namespace, e.g. \"dashboard\" */\n namespace: string;\n /** Source expression for i18nKey when dynamic (e.g. \"stat.labelKey\"), null when static */\n keyExpr: string | null;\n /** Source expression for namespace when dynamic, null when static */\n nsExpr: string | null;\n /** Variable names from the `values` prop (e.g. [\"year\"] from values={{ year }}) */\n valueKeys?: string[];\n}\n\n// A segment of a mixed-text element: either static text or a JSX expression\nexport interface EditableSegment {\n type: \"text\" | \"expr\";\n // For \"text\": the raw JSX text value (with whitespace/newlines as in source)\n // For \"expr\": the full expression source including braces, e.g. \"{new Date().getFullYear()}\"\n raw: string;\n}\n\n// Registry entry for editable elements (text, rich-text, className, mixed-text)\nexport interface EditableEntry {\n file: string;\n type: \"text\" | \"rich-text\" | \"className\" | \"mixed-text\";\n startOffset: number;\n endOffset: number;\n originalContent: string;\n // Only present for mixed-text entries\n segments?: EditableSegment[];\n context: { parentTag: string };\n}\n\n// Module-level registry (collected across all files during build)\nconst editableRegistry = new Map<string, EditableEntry>();\n\n// Generate stable ID for editable elements\n// Replace the counter-based ID generation with position-based\nfunction generateId(filePath: string, node: NodeWithRange): string {\n // Use file path + start position for a stable, deterministic ID\n return `${filePath}:${node.start}`;\n}\n\n// Export for testing - get a copy of the current registry\nexport function getRegistry(): Record<string, EditableEntry> {\n return Object.fromEntries(editableRegistry);\n}\n\n// Export for testing - clear registry and counters\nexport function clearRegistry(): void {\n editableRegistry.clear();\n}\n\ninterface TransformState {\n filePath: string;\n code: string;\n s: MagicString;\n loopStack: LoopContext[];\n /** Tracks const varName = \"literal\" declarations for resolving dynamic i18nKey expressions */\n constants: Map<string, string>;\n /** Maps t-function variable names to their default namespace, e.g. { \"t\": \"dashboard\" } */\n tFunctions: Map<string, string>;\n}\n\nexport const upstartEditor = createUnplugin<Options>((options) => {\n if (!options.enabled) {\n return { name: \"upstart-editor-disabled\" };\n }\n\n const emitRegistry = options.emitRegistry ?? true;\n let root = process.cwd();\n let isDevMode = false;\n let devWriteTimer: ReturnType<typeof setTimeout> | null = null;\n\n const flushDevRegistry = () => {\n if (!isDevMode || editableRegistry.size === 0) return;\n const registryDir = path.join(root, \"build\", \"server\");\n const registryPath = path.join(registryDir, \"upstart-registry.json\");\n const registry = {\n version: 1,\n generatedAt: new Date().toISOString(),\n elements: Object.fromEntries(editableRegistry),\n };\n fs.mkdirSync(registryDir, { recursive: true });\n fs.writeFileSync(registryPath, JSON.stringify(registry, null, 2));\n };\n\n const scheduleDevRegistryWrite = () => {\n if (!isDevMode) return;\n if (devWriteTimer) clearTimeout(devWriteTimer);\n devWriteTimer = setTimeout(flushDevRegistry, 150);\n };\n\n return {\n name: \"upstart-editor\",\n enforce: \"pre\",\n\n vite: {\n configResolved(config) {\n root = config.root;\n isDevMode = config.command === \"serve\";\n },\n configureServer(_server) {\n isDevMode = true;\n },\n generateBundle() {\n if (!emitRegistry || editableRegistry.size === 0) {\n return;\n }\n\n const registry = {\n version: 1,\n generatedAt: new Date().toISOString(),\n elements: Object.fromEntries(editableRegistry),\n };\n\n this.emitFile({\n type: \"asset\",\n fileName: \"upstart-registry.json\",\n source: JSON.stringify(registry, null, 2),\n });\n\n // Clear for next build\n editableRegistry.clear();\n },\n },\n\n transformInclude(id) {\n return /\\.(tsx|jsx)$/.test(id) && !id.includes(\"node_modules\");\n },\n\n transform(code, id) {\n // Fast path: skip files without JSX\n if (!code.includes(\"<\")) {\n return null;\n }\n\n try {\n const relativePath = path.relative(root, id);\n const result = transformWithOxc(code, relativePath);\n\n if (!result) {\n return null;\n }\n\n scheduleDevRegistryWrite();\n return {\n code: result.code,\n map: result.map,\n };\n } catch (error) {\n console.error(`Error transforming ${id}:`, error);\n return null;\n }\n },\n };\n});\n\nexport function transformWithOxc(code: string, filePath: string) {\n // Parse with oxc (super fast!)\n const ast = parseSync(filePath, code, {\n sourceType: \"module\",\n });\n\n if (!ast.program) {\n return null;\n }\n\n const s = new MagicString(code);\n const state: TransformState = {\n filePath,\n code,\n s,\n loopStack: [],\n constants: new Map(),\n tFunctions: new Map(),\n };\n\n let modified = false;\n\n // Use zimmerframe to walk and transform the AST\n walk(ast.program as Program, state, {\n _(node: Node, { state, next }: { state: TransformState; next: () => void }) {\n // Track useTranslation() calls to resolve t() function namespaces\n if (node.type === \"VariableDeclaration\") {\n trackUseTranslation(node, state);\n\n // Track const varName = \"literal\" for resolving dynamic i18nKey expressions\n for (const decl of (node as any).declarations) {\n if (\n decl.type === \"VariableDeclarator\" &&\n decl.id?.type === \"Identifier\" &&\n decl.init?.type === \"Literal\" &&\n typeof decl.init.value === \"string\"\n ) {\n state.constants.set(decl.id.name, decl.init.value);\n }\n }\n }\n\n // Handle .map() calls to track loops (must be before JSXElement)\n if (node.type === \"CallExpression\") {\n const mapCallInfo = detectAndPatchMapCall(node as CallExpression, state);\n\n if (mapCallInfo) {\n // Push loop context\n state.loopStack.push(mapCallInfo);\n\n // Continue walking into the callback\n next();\n\n // Pop loop context after traversing\n state.loopStack.pop();\n return;\n }\n }\n\n // Handle JSX elements\n if (node.type === \"JSXElement\") {\n const jsxNode = node as JSXElement;\n const opening = jsxNode.openingElement;\n const tagName = getJSXElementName(opening);\n\n // Skip ALL attribute injection for <Trans> elements.\n // <Trans> renders to a text node at runtime, so DOM attributes are lost.\n // The i18n attributes are promoted to the parent element instead.\n if (tagName === \"Trans\") {\n next();\n return;\n }\n\n const insertPos = getAttributeInsertPosition(opening, state.code);\n\n // Track attributes to inject\n const attributes: string[] = [];\n\n // Compute stable hash from the element's source code (includes all children)\n // This hash changes if ANY part of the element or its children change\n if (hasRange(jsxNode)) {\n const elementSource = state.code.slice(jsxNode.start, jsxNode.end);\n const hash = hashContent(elementSource);\n\n // Collect suffix expressions from all enclosing loops so elements\n // inside .map() get a unique hash per iteration at runtime.\n // detectAndPatchMapCall() attempts to auto-inject an index param when missing.\n // If injection is not possible (no stable source range), fall back to \"0\".\n const loopSuffixes = state.loopStack.map((l) => (l.indexName ? `\\${${l.indexName}}` : \"0\"));\n\n if (loopSuffixes.length > 0) {\n const suffix = loopSuffixes.join(\"-\");\n attributes.push(`data-upstart-hash={\\`${hash}-${suffix}\\`}`);\n } else {\n attributes.push(`data-upstart-hash=\"${hash}\"`);\n }\n }\n\n // --- Editable text detection ---\n // Priority: i18n (true) > non-i18n text (false) > no text (no attribute)\n\n // Step 1: Check for Trans children AND t() calls (editable via i18n, gets \"true\")\n const transChildren = findTransInChildren(jsxNode, state.code, state.constants);\n const tCallChildren = findTCallsInChildren(jsxNode, state.code, state);\n const allI18nKeys = [...transChildren, ...tCallChildren];\n const hasI18n = allI18nKeys.length > 0;\n\n if (hasI18n && !hasMixedNonI18nContent(jsxNode, state.code, state, state.constants)) {\n attributes.push('data-upstart-editable-text=\"true\"');\n attributes.push('data-upstart-editable-text-mode=\"plain\"');\n\n const hasDynamic = allI18nKeys.some((t) => t.keyExpr || t.nsExpr);\n if (hasDynamic) {\n // Build a runtime JSX template expression for dynamic i18n keys\n const parts = allI18nKeys.map((t) => {\n const nsPart = t.nsExpr ? `\\${${t.nsExpr}}` : t.namespace;\n const keyPart = t.keyExpr ? `\\${${t.keyExpr}}` : t.key;\n return `${nsPart}:${keyPart}`;\n });\n attributes.push(`data-upstart-i18n={\\`${parts.join(\",\")}\\`}`);\n } else {\n const keys = allI18nKeys.map((t: I18nKeyInfo) => escapeProp(t.fullKey)).join(\",\");\n attributes.push(`data-upstart-i18n=\"${keys}\"`);\n }\n\n const allValueKeys = [...new Set(allI18nKeys.flatMap((t) => t.valueKeys ?? []))];\n if (allValueKeys.length > 0) {\n attributes.push(`data-i18n-values=\"${allValueKeys.join(\",\")}\"`);\n }\n }\n\n // Step 2a: Text leaf elements — plain text, editable directly in the TSX source\n if (isTextLeafElement(jsxNode)) {\n if (!hasI18n) {\n attributes.push('data-upstart-editable-text=\"true\"');\n attributes.push('data-upstart-editable-text-mode=\"direct\"');\n }\n\n // Find the actual JSXText node to track its location\n const textChild = jsxNode.children.find((c) => c.type === \"JSXText\" && (c as any).value?.trim());\n\n if (textChild && hasRange(textChild)) {\n const id = generateId(state.filePath, textChild);\n const textValue = (textChild as any).value as string;\n\n // Calculate trimmed offsets (exclude leading/trailing whitespace)\n const trimmedStart = textChild.start + (textValue.length - textValue.trimStart().length);\n const trimmedEnd = textChild.end - (textValue.length - textValue.trimEnd().length);\n\n editableRegistry.set(id, {\n file: state.filePath,\n type: \"text\",\n startOffset: trimmedStart,\n endOffset: trimmedEnd,\n originalContent: textValue.trim(),\n context: { parentTag: tagName || \"unknown\" },\n });\n\n attributes.push(`data-upstart-id=\"${id}\"`);\n }\n }\n // Step 2b: Almost-leaf elements — inline formatting tags only, editable via InspectorPanel\n else if (!hasI18n && isAlmostLeafElement(jsxNode)) {\n attributes.push('data-upstart-editable-text=\"true\"');\n attributes.push('data-upstart-editable-text-mode=\"rich-panel\"');\n\n // Track the full children range so the server can overwrite them in the TSX source\n const children = jsxNode.children.filter((c) => hasRange(c));\n const firstChild = children[0];\n const lastChild = children[children.length - 1];\n\n if (firstChild && lastChild && hasRange(firstChild) && hasRange(lastChild)) {\n const id = generateId(state.filePath, firstChild);\n const originalContent = state.code.slice(firstChild.start, lastChild.end);\n\n editableRegistry.set(id, {\n file: state.filePath,\n type: \"rich-text\",\n startOffset: firstChild.start,\n endOffset: lastChild.end,\n originalContent,\n context: { parentTag: tagName || \"unknown\" },\n });\n\n attributes.push(`data-upstart-id=\"${id}\"`);\n }\n }\n // Step 2c: Mixed-text leaf — JSXText + JSXExpressionContainer children only.\n // Expressions become non-editable chips; surrounding text is editable.\n else if (!hasI18n && isMixedTextLeafElement(jsxNode)) {\n attributes.push('data-upstart-editable-text=\"true\"');\n attributes.push('data-upstart-editable-text-mode=\"plain\"');\n\n const rangedChildren = jsxNode.children.filter((c) => hasRange(c));\n const firstChild = rangedChildren[0];\n const lastChild = rangedChildren[rangedChildren.length - 1];\n\n if (firstChild && lastChild && hasRange(firstChild) && hasRange(lastChild)) {\n const segments: EditableSegment[] = [];\n let exprIndex = 0;\n let templateStr = \"\";\n\n for (const child of jsxNode.children) {\n if (child.type === \"JSXText\") {\n const normalized = normalizeJSXText((child as any).value as string);\n segments.push({ type: \"text\", raw: (child as any).value as string });\n templateStr += normalized;\n } else if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (!expr || expr.type === \"JSXEmptyExpression\") continue;\n const raw = hasRange(child) ? state.code.slice(child.start, child.end) : \"\";\n segments.push({ type: \"expr\", raw });\n templateStr += `{{${exprIndex++}}}`;\n }\n }\n\n const id = generateId(state.filePath, firstChild);\n const originalContent = state.code.slice(firstChild.start, lastChild.end);\n\n editableRegistry.set(id, {\n file: state.filePath,\n type: \"mixed-text\",\n startOffset: firstChild.start,\n endOffset: lastChild.end,\n originalContent,\n segments,\n context: { parentTag: tagName || \"unknown\" },\n });\n\n attributes.push(`data-upstart-id=\"${id}\"`);\n attributes.push(`data-upstart-mixed-template=\"${escapeProp(templateStr.trim())}\"`);\n }\n }\n // Step 3: Non-leaf elements with visible text content (expressions, mixed content)\n else if (!hasI18n && hasVisibleTextContent(jsxNode)) {\n attributes.push('data-upstart-editable-text=\"false\"');\n }\n\n // Track className attribute if it's a string literal\n const classNameAttr = opening.attributes.find(\n (attr): attr is JSXAttribute =>\n attr.type === \"JSXAttribute\" &&\n attr.name.type === \"JSXIdentifier\" &&\n attr.name.name === \"className\" &&\n attr.value?.type === \"Literal\" &&\n typeof (attr.value as any).value === \"string\",\n );\n\n if (classNameAttr && classNameAttr.value && hasRange(classNameAttr.value)) {\n const id = generateId(state.filePath, classNameAttr.value);\n const classValue = (classNameAttr.value as any).value as string;\n\n // +1 and -1 to exclude the quotes\n editableRegistry.set(id, {\n file: state.filePath,\n type: \"className\",\n startOffset: classNameAttr.value.start + 1,\n endOffset: classNameAttr.value.end - 1,\n originalContent: classValue,\n context: { parentTag: tagName || \"unknown\" },\n });\n\n attributes.push(`data-upstart-classname-id=\"${id}\"`);\n }\n\n // Process PascalCase components for additional tracking\n if (tagName && /^[A-Z]/.test(tagName)) {\n // File and component tracking\n attributes.push(`data-upstart-file=\"${escapeProp(state.filePath)}\"`);\n attributes.push(`data-upstart-component=\"${escapeProp(tagName)}\"`);\n\n // Loop context tracking\n if (state.loopStack.length > 0) {\n const loop = state.loopStack[state.loopStack.length - 1];\n attributes.push(`data-upstart-loop-item=\"${escapeProp(loop.itemName)}\"`);\n if (loop.indexName) {\n attributes.push(`data-upstart-loop-index={${loop.indexName}}`);\n }\n attributes.push(`data-upstart-loop-array=\"${escapeProp(loop.arrayExpr)}\"`);\n }\n\n // Analyze each prop for data bindings\n for (const attr of opening.attributes) {\n if (attr.type !== \"JSXAttribute\" || attr.name.type !== \"JSXIdentifier\") {\n continue;\n }\n\n const propName = attr.name.name;\n const binding = analyzeBinding(attr.value, state.code);\n\n if (binding) {\n attributes.push(`data-upstart-prop-${propName.toLowerCase()}=\"${escapeProp(binding.path)}\"`);\n\n // Track conditional expressions\n if (binding.isConditional) {\n attributes.push(`data-upstart-conditional-${propName.toLowerCase()}=\"true\"`);\n }\n }\n }\n }\n\n // Inject attributes if any\n if (insertPos !== -1 && attributes.length > 0) {\n const attrString = \" \" + attributes.join(\" \");\n state.s.appendLeft(insertPos, attrString);\n modified = true;\n }\n }\n\n next();\n },\n });\n\n if (!modified) {\n return null;\n }\n\n return {\n code: s.toString(),\n map: s.generateMap({ hires: true }),\n };\n}\n\n// Helper: Get JSX element name\nfunction getJSXElementName(opening: JSXOpeningElement): string | null {\n if (opening.name.type === \"JSXIdentifier\") {\n return opening.name.name;\n }\n\n // Handle JSXMemberExpression like <Foo.Bar>\n if (opening.name.type === \"JSXMemberExpression\") {\n let current = opening.name;\n while (current.property) {\n if (current.property.type === \"JSXIdentifier\") {\n return current.property.name;\n }\n if (current.type === \"JSXMemberExpression\") {\n current = current.property as any;\n } else {\n break;\n }\n }\n }\n\n return null;\n}\n\n// Helper: Find where to insert attributes in JSX opening tag\nfunction getAttributeInsertPosition(opening: JSXOpeningElement, code: string): number {\n // If there are existing attributes, insert before the first one\n if (opening.attributes.length > 0) {\n const firstAttr = opening.attributes[0];\n if (hasRange(firstAttr)) {\n return firstAttr.start;\n }\n }\n\n // Otherwise, insert after the tag name\n if (opening.name.type === \"JSXIdentifier\" && hasRange(opening.name)) {\n return opening.name.end;\n }\n\n if (opening.name.type === \"JSXMemberExpression\" && hasRange(opening.name)) {\n return opening.name.end;\n }\n\n return -1;\n}\n\n// Helper: Analyze a prop value to extract data binding\nfunction analyzeBinding(\n value: JSXAttribute[\"value\"],\n code: string,\n): {\n path: string;\n isConditional?: boolean;\n} | null {\n if (!value) {\n return null;\n }\n\n if (value.type === \"JSXExpressionContainer\") {\n const expr = value.expression;\n\n if (expr.type === \"JSXEmptyExpression\") {\n return null;\n }\n\n return analyzeExpression(expr, code);\n }\n\n return null;\n}\n\n// Helper: Analyze any expression to extract binding info\nfunction analyzeExpression(\n expr: Expression,\n code: string,\n): {\n path: string;\n isConditional?: boolean;\n} | null {\n // Handle member expressions: user.name, product.price, etc.\n if (expr.type === \"MemberExpression\") {\n const path = exprToString(expr, code);\n return { path };\n }\n\n // Handle identifiers: userName, price, etc.\n if (expr.type === \"Identifier\") {\n return { path: expr.name };\n }\n\n // Handle conditional expressions: condition ? value1 : value2\n if (expr.type === \"ConditionalExpression\") {\n // Try to extract from the consequent\n const consequent = analyzeExpression(expr.consequent, code);\n if (consequent) {\n return {\n ...consequent,\n isConditional: true,\n path: exprToString(expr, code),\n };\n }\n }\n\n // Handle logical expressions: value1 && value2, value1 || value2\n if (expr.type === \"LogicalExpression\") {\n const right = analyzeExpression(expr.right, code);\n if (right) {\n return {\n ...right,\n isConditional: true,\n path: exprToString(expr, code),\n };\n }\n }\n\n // For other complex expressions, just return the path\n const exprWithRange = expr as any;\n if (exprWithRange.start !== undefined && exprWithRange.end !== undefined) {\n return {\n path: code.slice(exprWithRange.start, exprWithRange.end),\n };\n }\n\n return null;\n}\n\n// Helper: Convert expression AST to string\nfunction exprToString(expr: Expression, code: string): string {\n // Use the source range to get the actual code\n const exprWithRange = expr as any;\n if (exprWithRange.start !== undefined && exprWithRange.end !== undefined) {\n return code.slice(exprWithRange.start, exprWithRange.end);\n }\n\n // Fallback: reconstruct from AST\n if (expr.type === \"Identifier\") {\n return expr.name;\n }\n\n if (expr.type === \"MemberExpression\") {\n const obj = exprToString(expr.object as Expression, code);\n const prop =\n expr.property.type === \"Identifier\" && !expr.computed\n ? expr.property.name\n : exprToString(expr.property as Expression, code);\n return expr.computed ? `${obj}[${prop}]` : `${obj}.${prop}`;\n }\n\n if (expr.type === \"ConditionalExpression\") {\n const test = exprToString(expr.test, code);\n const consequent = exprToString(expr.consequent, code);\n const alternate = exprToString(expr.alternate, code);\n return `${test} ? ${consequent} : ${alternate}`;\n }\n\n if (expr.type === \"LogicalExpression\") {\n const left = exprToString(expr.left, code);\n const right = exprToString(expr.right, code);\n return `${left} ${expr.operator} ${right}`;\n }\n\n return \"\";\n}\n\n// Helper: Detect .map() calls and auto-inject index parameter if missing\nfunction detectAndPatchMapCall(node: CallExpression, state: TransformState): LoopContext | null {\n // Check if this is a .map() call\n if (\n node.callee.type !== \"MemberExpression\" ||\n node.callee.property.type !== \"Identifier\" ||\n node.callee.property.name !== \"map\"\n ) {\n return null;\n }\n\n const callback = node.arguments[0];\n\n if (!callback) {\n return null;\n }\n\n // Check if callback is an arrow function or function expression\n if (callback.type !== \"ArrowFunctionExpression\" && callback.type !== \"FunctionExpression\") {\n return null;\n }\n\n const params = callback.params;\n const itemParam = params[0];\n const indexParam = params[1];\n\n if (!itemParam) {\n return null;\n }\n\n // Extract parameter names\n const itemName = itemParam.type === \"Identifier\" ? itemParam.name : \"item\";\n let indexName = indexParam?.type === \"Identifier\" ? indexParam.name : null;\n\n // If no index parameter exists in arrow function, auto-inject it\n if (!indexName && callback.type === \"ArrowFunctionExpression\") {\n indexName = \"__i\";\n\n // Find the closing paren of the parameter list safely\n const lastParam = params[params.length - 1];\n\n // ESTree `Pattern` type doesn't guarantee source range fields in typings.\n // Guard before reading offsets.\n if (!hasRange(lastParam)) {\n // Can't patch callback params without a stable source offset.\n // Keep indexName as null so hash suffix generation can handle this case.\n indexName = null;\n return {\n itemName,\n indexName,\n arrayExpr: exprToString(node.callee.object as Expression, state.code),\n };\n }\n\n let searchPos = lastParam.end;\n\n // Find the next ) after the last parameter\n while (searchPos < state.code.length && state.code[searchPos] !== \")\") {\n searchPos++;\n }\n\n if (searchPos < state.code.length && state.code[searchPos] === \")\") {\n // Insert the parameter before the closing paren\n state.s.appendLeft(searchPos, \", __i\");\n }\n }\n\n // Get the array expression\n const arrayExpr = exprToString(node.callee.object as Expression, state.code);\n\n return {\n itemName,\n indexName,\n arrayExpr,\n };\n}\n\n// Helper: Escape prop values for JSX attributes\nfunction escapeProp(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n\n// Helper: Track useTranslation() calls to map t-function variable names to their namespace\nfunction trackUseTranslation(node: Node, state: TransformState): void {\n if (node.type !== \"VariableDeclaration\") return;\n\n const decl = node as any;\n for (const declarator of decl.declarations) {\n if (\n declarator.id?.type !== \"ObjectPattern\" ||\n !declarator.init ||\n declarator.init.type !== \"CallExpression\"\n ) {\n continue;\n }\n\n const callExpr = declarator.init;\n if (callExpr.callee?.type !== \"Identifier\" || callExpr.callee.name !== \"useTranslation\") {\n continue;\n }\n\n const namespace = extractUseTranslationNamespace(callExpr);\n\n for (const prop of declarator.id.properties) {\n if (prop.type !== \"Property\") continue;\n if (prop.key?.type !== \"Identifier\" || prop.key.name !== \"t\") continue;\n\n // Handle aliased destructuring: { t: translate } → maps \"translate\" to namespace\n const localName = prop.value?.type === \"Identifier\" ? prop.value.name : \"t\";\n state.tFunctions.set(localName, namespace);\n }\n }\n}\n\n// Helper: Extract namespace from useTranslation() call arguments\nfunction extractUseTranslationNamespace(callExpr: any): string {\n const firstArg = callExpr.arguments?.[0];\n if (!firstArg) return \"translation\";\n\n // String literal: useTranslation(\"dashboard\")\n if (firstArg.type === \"Literal\" && typeof firstArg.value === \"string\") {\n return firstArg.value;\n }\n\n // Array expression: useTranslation([\"dashboard\", \"common\"])\n if (firstArg.type === \"ArrayExpression\" && firstArg.elements?.length > 0) {\n const first = firstArg.elements[0];\n if (first?.type === \"Literal\" && typeof first.value === \"string\") {\n return first.value;\n }\n }\n\n return \"translation\";\n}\n\n// Helper: Resolve a JSX attribute value to a string.\n// Handles string literals directly, and JSXExpressionContainer by resolving\n// Identifier references via the constants map, or returning the expression source for dynamic values.\nfunction resolveJSXAttrValue(\n value: any,\n code: string,\n constants: Map<string, string>,\n): { value: string; expr: string | null } | null {\n if (!value) return null;\n\n if (value.type === \"Literal\" && typeof value.value === \"string\") {\n return { value: value.value, expr: null };\n }\n\n if (value.type === \"JSXExpressionContainer\") {\n const expression = value.expression;\n if (!expression) return null;\n\n // Resolve simple identifiers via constants map (e.g. i18nKey={labelKey} where const labelKey = \"...\")\n if (expression.type === \"Identifier\" && constants.has(expression.name)) {\n return { value: constants.get(expression.name)!, expr: null };\n }\n\n // Dynamic expression — return source text so caller can emit a runtime JSX expression\n if (hasRange(expression)) {\n const src = code.slice(expression.start, expression.end);\n return { value: src, expr: src };\n }\n }\n\n return null;\n}\n\n// Helper: Detect <Trans i18nKey=\"...\" /> component and extract i18n key info\nfunction detectTransComponent(\n jsxElement: JSXElement,\n code: string,\n constants: Map<string, string>,\n): I18nKeyInfo | null {\n const opening = jsxElement.openingElement;\n const tagName = getJSXElementName(opening);\n\n if (tagName !== \"Trans\") return null;\n\n let keyResult: { value: string; expr: string | null } | null = null;\n let nsResult: { value: string; expr: string | null } | null = null;\n let valueKeys: string[] | undefined;\n\n for (const attr of opening.attributes) {\n if (attr.type !== \"JSXAttribute\" || attr.name.type !== \"JSXIdentifier\") continue;\n\n const attrName = attr.name.name;\n\n if (attrName === \"i18nKey\") {\n keyResult = resolveJSXAttrValue(attr.value, code, constants);\n }\n\n if (attrName === \"ns\") {\n nsResult = resolveJSXAttrValue(attr.value, code, constants);\n }\n\n if (attrName === \"values\") {\n const container = attr.value as any;\n if (container?.type === \"JSXExpressionContainer\") {\n const expr = container.expression;\n if (expr?.type === \"ObjectExpression\") {\n const keys: string[] = [];\n for (const prop of expr.properties) {\n // oxc-parser uses estree \"Property\"; Babel uses \"ObjectProperty\"\n if ((prop.type === \"Property\" || prop.type === \"ObjectProperty\") && prop.key) {\n if (prop.key.type === \"Identifier\") keys.push(prop.key.name);\n else if (prop.key.type === \"Literal\" || prop.key.type === \"StringLiteral\")\n keys.push(prop.key.value);\n }\n }\n if (keys.length > 0) valueKeys = keys;\n }\n }\n }\n }\n\n if (!keyResult) {\n return null;\n }\n\n const key = keyResult.value;\n const namespace = nsResult?.value ?? \"translation\";\n const keyExpr = keyResult.expr;\n const nsExpr = nsResult?.expr ?? null;\n\n return {\n fullKey: `${namespace}:${key}`,\n key,\n namespace,\n keyExpr,\n nsExpr,\n valueKeys,\n };\n}\n\n// Helper: Scan an element's direct children for <Trans> components and extract i18n key info.\n// Handles direct <Trans> children and <Trans> inside JSXExpressionContainer (e.g. {show && <Trans>}).\nfunction findTransInChildren(\n jsxElement: JSXElement,\n code: string,\n constants: Map<string, string>,\n): I18nKeyInfo[] {\n const results: I18nKeyInfo[] = [];\n\n for (const child of jsxElement.children) {\n if (child.type === \"JSXElement\") {\n const info = detectTransComponent(child as JSXElement, code, constants);\n if (info) results.push(info);\n }\n\n if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (expr && expr.type !== \"JSXEmptyExpression\") {\n findTransInExpression(expr, code, results, constants);\n }\n }\n }\n\n return results;\n}\n\n// Helper: Recursively search an expression tree for <Trans> JSXElements.\n// Handles LogicalExpression (&&, ||), ConditionalExpression (?:), CallExpression (.map()),\n// ArrowFunctionExpression/FunctionExpression (callbacks), BlockStatement, and direct JSXElement.\nfunction findTransInExpression(\n expr: any,\n code: string,\n results: I18nKeyInfo[],\n constants: Map<string, string>,\n): void {\n if (!expr || !expr.type) return;\n\n if (expr.type === \"JSXElement\") {\n const info = detectTransComponent(expr as JSXElement, code, constants);\n if (info) results.push(info);\n return;\n }\n\n if (expr.type === \"LogicalExpression\") {\n findTransInExpression(expr.left, code, results, constants);\n findTransInExpression(expr.right, code, results, constants);\n return;\n }\n\n if (expr.type === \"ConditionalExpression\") {\n findTransInExpression(expr.consequent, code, results, constants);\n findTransInExpression(expr.alternate, code, results, constants);\n return;\n }\n\n // Handle .map() and other call expressions — recurse into arguments\n if (expr.type === \"CallExpression\") {\n for (const arg of expr.arguments) {\n findTransInExpression(arg, code, results, constants);\n }\n return;\n }\n\n // Handle arrow/function expressions — recurse into body\n if (expr.type === \"ArrowFunctionExpression\" || expr.type === \"FunctionExpression\") {\n findTransInExpression(expr.body, code, results, constants);\n return;\n }\n\n // Handle block bodies (arrow functions with braces)\n if (expr.type === \"BlockStatement\") {\n for (const stmt of expr.body) {\n if (stmt.type === \"ReturnStatement\" && stmt.argument) {\n findTransInExpression(stmt.argument, code, results, constants);\n }\n }\n return;\n }\n}\n\n// Helper: Scan an element's direct children for t() function calls and extract i18n key info.\nfunction findTCallsInChildren(jsxElement: JSXElement, code: string, state: TransformState): I18nKeyInfo[] {\n if (state.tFunctions.size === 0) return [];\n\n const results: I18nKeyInfo[] = [];\n\n for (const child of jsxElement.children) {\n if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (expr && expr.type !== \"JSXEmptyExpression\") {\n findTCallInExpression(expr, code, results, state);\n }\n }\n }\n\n return results;\n}\n\n// Returns true if the element mixes i18n content with non-i18n text/expressions.\n// In that case, inline editing would incorrectly overwrite the translation with\n// the full rendered text (including static parts like \"© 2026 Company.\").\n//\n// Safe patterns (NOT mixed):\n// {showWelcome && <Trans>} — boolean guard around Trans\n// {cond ? <Trans a> : <Trans b>} — ternary between two Trans\n// {\" \"} — whitespace-only literal\n//\n// Mixed patterns (flagged):\n// \"© John Doe.\" + <Trans> — non-whitespace JSXText alongside i18n\n// {year} + <Trans> — text-producing Identifier/MemberExpression alongside i18n\nfunction hasMixedNonI18nContent(\n jsxNode: JSXElement,\n code: string,\n state: TransformState,\n constants: Map<string, string>,\n): boolean {\n for (const child of jsxNode.children) {\n if (child.type === \"JSXText\") {\n if (((child as any).value as string).trim().length > 0) return true;\n } else if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (!expr || expr.type === \"JSXEmptyExpression\") continue;\n // Whitespace-only string literal (e.g. {\" \"}) is harmless\n if (expr.type === \"Literal\" && typeof expr.value === \"string\" && expr.value.trim() === \"\") continue;\n // If expression contains i18n content (t() or Trans), it is not non-i18n text\n const tResults: I18nKeyInfo[] = [];\n findTCallInExpression(expr, code, tResults, state);\n if (tResults.length > 0) continue;\n const transResults: I18nKeyInfo[] = [];\n findTransInExpression(expr, code, transResults, constants);\n if (transResults.length > 0) continue;\n // Flag only expressions that produce visible text (same heuristic as hasVisibleTextContent)\n const type = expr.type;\n if (\n type === \"Identifier\" ||\n type === \"MemberExpression\" ||\n type === \"Literal\" ||\n type === \"TemplateLiteral\"\n ) {\n return true;\n }\n }\n }\n return false;\n}\n\n// Helper: Recursively search an expression tree for t() calls.\n// Handles LogicalExpression (&&, ||), ConditionalExpression (?:), CallExpression (.map()),\n// ArrowFunctionExpression/FunctionExpression (callbacks), BlockStatement, and direct CallExpression.\nfunction findTCallInExpression(expr: any, code: string, results: I18nKeyInfo[], state: TransformState): void {\n if (!expr || !expr.type) return;\n\n if (expr.type === \"CallExpression\") {\n const info = detectTCall(expr, code, state);\n if (info) {\n results.push(info);\n return;\n }\n // Not a t() call — recurse into arguments (for .map() callbacks etc.)\n for (const arg of expr.arguments) {\n findTCallInExpression(arg, code, results, state);\n }\n return;\n }\n\n if (expr.type === \"LogicalExpression\") {\n findTCallInExpression(expr.left, code, results, state);\n findTCallInExpression(expr.right, code, results, state);\n return;\n }\n\n if (expr.type === \"ConditionalExpression\") {\n findTCallInExpression(expr.consequent, code, results, state);\n findTCallInExpression(expr.alternate, code, results, state);\n return;\n }\n\n if (expr.type === \"ArrowFunctionExpression\" || expr.type === \"FunctionExpression\") {\n findTCallInExpression(expr.body, code, results, state);\n return;\n }\n\n if (expr.type === \"BlockStatement\") {\n for (const stmt of expr.body) {\n if (stmt.type === \"ReturnStatement\" && stmt.argument) {\n findTCallInExpression(stmt.argument, code, results, state);\n }\n }\n return;\n }\n}\n\n// Helper: Detect a t(\"key\") call and extract i18n key info.\nfunction detectTCall(callExpr: any, code: string, state: TransformState): I18nKeyInfo | null {\n if (callExpr.callee?.type !== \"Identifier\") return null;\n\n const calleeName = callExpr.callee.name;\n const namespace = state.tFunctions.get(calleeName);\n if (namespace === undefined) return null;\n\n const firstArg = callExpr.arguments?.[0];\n if (!firstArg) return null;\n\n // Static string key: t(\"features.title\")\n if (firstArg.type === \"Literal\" && typeof firstArg.value === \"string\") {\n const key = firstArg.value;\n return { fullKey: `${namespace}:${key}`, key, namespace, keyExpr: null, nsExpr: null };\n }\n\n // Dynamic key — try constant resolution first, then fall back to expression\n if (hasRange(firstArg)) {\n const exprSrc = code.slice(firstArg.start, firstArg.end);\n\n if (firstArg.type === \"Identifier\" && state.constants.has(firstArg.name)) {\n const key = state.constants.get(firstArg.name)!;\n return { fullKey: `${namespace}:${key}`, key, namespace, keyExpr: null, nsExpr: null };\n }\n\n return { fullKey: `${namespace}:${exprSrc}`, key: exprSrc, namespace, keyExpr: exprSrc, nsExpr: null };\n }\n\n return null;\n}\n\n// Helper: Check if element is a \"leaf\" with only static text (no nested elements or expressions)\nfunction isTextLeafElement(jsxElement: JSXElement): boolean {\n let hasText = false;\n\n for (const child of jsxElement.children) {\n if (child.type === \"JSXText\") {\n const textValue = (child as any).value;\n if (textValue?.trim()) {\n hasText = true;\n }\n } else if (\n child.type === \"JSXElement\" ||\n child.type === \"JSXFragment\" ||\n child.type === \"JSXExpressionContainer\" ||\n child.type === \"JSXSpreadChild\"\n ) {\n // Has non-text children, not a leaf element\n return false;\n }\n }\n\n return hasText;\n}\n\n// Inline HTML tags that purely style text without introducing block structure.\n// An element whose children are only JSXText + these tags is an \"almost-leaf\"\n// that can be edited as rich text via the InspectorPanel.\nconst INLINE_FORMATTING_TAGS = new Set([\n \"b\",\n \"i\",\n \"em\",\n \"strong\",\n \"u\",\n \"s\",\n \"del\",\n \"ins\",\n \"mark\",\n \"small\",\n \"sub\",\n \"sup\",\n \"code\",\n \"kbd\",\n \"abbr\",\n \"cite\",\n \"time\",\n \"span\",\n \"a\",\n]);\n\n// Returns true if the element's children are exclusively JSXText nodes and\n// known inline formatting tags (no expressions, no block elements, no fragments).\n// Must have at least one inline tag (otherwise it would be a plain text leaf).\n// The inline child tags must themselves be \"simple\" — only JSXText children,\n// no expressions, no attributes — so that <span className=\"...\">{expr}</span>\n// siblings are not mistaken for plain inline formatting wrappers.\nfunction isAlmostLeafElement(jsxElement: JSXElement): boolean {\n let hasInlineElement = false;\n\n for (const child of jsxElement.children) {\n if (child.type === \"JSXText\") {\n continue;\n }\n if (child.type === \"JSXElement\") {\n const childEl = child as JSXElement;\n const tagName = getJSXElementName(childEl.openingElement);\n if (!tagName || !INLINE_FORMATTING_TAGS.has(tagName.toLowerCase())) return false;\n // The inline child must be truly simple: no attributes and only JSXText children.\n // A <span className=\"...\"> or <span>{expr}</span> is not a simple inline wrapper.\n if (childEl.openingElement.attributes.length > 0) return false;\n for (const grandchild of childEl.children) {\n if (grandchild.type !== \"JSXText\") return false;\n }\n hasInlineElement = true;\n } else {\n return false;\n }\n }\n\n return hasInlineElement;\n}\n\n// Normalise a raw JSX text node value the same way React/Babel does at compile time:\n// - Split by newlines\n// - For non-first lines, strip leading whitespace; for non-last lines, strip trailing whitespace\n// - Remove lines that are entirely whitespace\n// - Join surviving lines, appending a space after each non-last non-empty line\nfunction normalizeJSXText(raw: string): string {\n const lines = raw.split(/\\r\\n|\\n|\\r/);\n const lastNonEmptyIdx = lines.reduce((acc, line, i) => (/[^ \\t]/.test(line) ? i : acc), -1);\n let result = \"\";\n for (let i = 0; i < lines.length; i++) {\n let line = lines[i].replace(/\\t/g, \" \");\n if (i > 0) line = line.replace(/^[ ]+/, \"\");\n if (i < lines.length - 1) line = line.replace(/[ ]+$/, \"\");\n if (!line) continue;\n if (i !== lastNonEmptyIdx) line += \" \";\n result += line;\n }\n return result;\n}\n\n// Returns true if the element's children are exclusively JSXText + JSXExpressionContainer nodes\n// (no nested elements), AND it has at least one of each — making the static text parts editable\n// while expressions are rendered as non-editable chips.\nfunction isMixedTextLeafElement(jsxElement: JSXElement): boolean {\n let hasText = false;\n let hasExpr = false;\n\n for (const child of jsxElement.children) {\n if (child.type === \"JSXText\") {\n if (normalizeJSXText((child as any).value as string).length > 0) hasText = true;\n continue;\n }\n if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (!expr || expr.type === \"JSXEmptyExpression\") continue;\n hasExpr = true;\n continue;\n }\n // JSXElement, JSXFragment, JSXSpreadChild → not a pure mixed-text leaf\n return false;\n }\n\n return hasText && hasExpr;\n}\n\n// Helper: Check if element has any visible text content (static or dynamic).\n// Broader than isTextLeafElement — returns true even if the element has other child types.\n// Detects JSXText with non-whitespace content and expression containers with\n// text-producing expressions (Identifier, MemberExpression, Literal, TemplateLiteral).\n// Skips CallExpression, ConditionalExpression, LogicalExpression (these typically produce elements).\nfunction hasVisibleTextContent(jsxElement: JSXElement): boolean {\n for (const child of jsxElement.children) {\n if (child.type === \"JSXText\") {\n const textValue = (child as any).value;\n if (textValue?.trim()) {\n return true;\n }\n continue;\n }\n\n if (child.type === \"JSXExpressionContainer\") {\n const expr = (child as any).expression;\n if (!expr || expr.type === \"JSXEmptyExpression\") {\n continue;\n }\n if (\n expr.type === \"Identifier\" ||\n expr.type === \"MemberExpression\" ||\n expr.type === \"Literal\" ||\n expr.type === \"TemplateLiteral\"\n ) {\n return true;\n }\n }\n }\n\n return false;\n}\n\nexport default upstartEditor.vite;\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { UpstartBrandingPluginOptions } from "./types.js";
|
|
2
|
-
import * as unplugin from "unplugin";
|
|
2
|
+
import * as _$unplugin from "unplugin";
|
|
3
3
|
|
|
4
4
|
//#region src/vite-plugin-upstart-branding/plugin.d.ts
|
|
5
5
|
/**
|
|
@@ -9,8 +9,8 @@ import * as unplugin from "unplugin";
|
|
|
9
9
|
* The badge appears at the bottom of the page, then fades out after a
|
|
10
10
|
* few seconds or when the user scrolls.
|
|
11
11
|
*/
|
|
12
|
-
declare const upstartBranding: unplugin.UnpluginInstance<UpstartBrandingPluginOptions, boolean>;
|
|
13
|
-
declare const _default: (options: UpstartBrandingPluginOptions) => unplugin.VitePlugin<any>[] | unplugin.VitePlugin<any>;
|
|
12
|
+
declare const upstartBranding: _$unplugin.UnpluginInstance<UpstartBrandingPluginOptions, boolean>;
|
|
13
|
+
declare const _default: (options: UpstartBrandingPluginOptions) => _$unplugin.VitePlugin<any>[] | _$unplugin.VitePlugin<any>;
|
|
14
14
|
//#endregion
|
|
15
15
|
export { _default as default, upstartBranding };
|
|
16
16
|
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","names":[],"sources":["../../src/vite-plugin-upstart-branding/plugin.ts"],"mappings":";;;;;;;AAgBA;;;;cAAa,eAAA,EAAe,
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","names":[],"sources":["../../src/vite-plugin-upstart-branding/plugin.ts"],"mappings":";;;;;;;AAgBA;;;;cAAa,eAAA,EAAe,UAAA,CAAA,gBAAA,CAAA,4BAAA;AAAA,cAwCzB,QAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","names":[],"sources":["../../src/vite-plugin-upstart-branding/plugin.ts"],"sourcesContent":["import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { createUnplugin } from \"unplugin\";\nimport type { UpstartBrandingPluginOptions } from \"./types.js\";\n\nconst DEFAULT_OPTIONS: Required<UpstartBrandingPluginOptions> = {\n enabled: false,\n};\n\n/**\n * Upstart Branding Vite plugin (build-time)\n *\n * Injects a \"Made with Upstart\" badge into the app entry during build.\n * The badge appears at the bottom of the page, then fades out after a\n * few seconds or when the user scrolls.\n */\nexport const upstartBranding = createUnplugin<UpstartBrandingPluginOptions>((options = {}) => {\n const { enabled } = { ...DEFAULT_OPTIONS, ...options };\n\n if (!enabled) {\n return { name: \"upstart-branding-disabled\" };\n }\n\n const runtimePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), \"./runtime\");\n\n return {\n name: \"upstart-branding\",\n enforce: \"pre\",\n\n transform(code, id) {\n const [cleanId] = id.split(\"?\");\n if (!cleanId || cleanId.includes(\"node_modules\")) {\n return null;\n }\n\n if (!/\\.(t|j)sx?$/.test(cleanId)) {\n return null;\n }\n\n if (!cleanId.includes(\"entry.client.tsx\")) {\n return null;\n }\n\n if (code.includes(\"initUpstartBranding\")) {\n return null;\n }\n\n const imports = `import { initUpstartBranding } from ${JSON.stringify(runtimePath)};`;\n const injection = \"requestIdleCallback(initUpstartBranding);\";\n\n return {\n code: `${imports}${code}${injection}`,\n map: null,\n };\n },\n };\n});\n\nexport default upstartBranding.vite;\n"],"mappings":";;;;AAKA,MAAM,kBAA0D,EAC9D,SAAS,OACV;;;;;;;;AASD,MAAa,kBAAkB,gBAA8C,UAAU,EAAE,KAAK;CAC5F,MAAM,EAAE,YAAY;EAAE,GAAG;EAAiB,GAAG;EAAS;
|
|
1
|
+
{"version":3,"file":"plugin.js","names":[],"sources":["../../src/vite-plugin-upstart-branding/plugin.ts"],"sourcesContent":["import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { createUnplugin } from \"unplugin\";\nimport type { UpstartBrandingPluginOptions } from \"./types.js\";\n\nconst DEFAULT_OPTIONS: Required<UpstartBrandingPluginOptions> = {\n enabled: false,\n};\n\n/**\n * Upstart Branding Vite plugin (build-time)\n *\n * Injects a \"Made with Upstart\" badge into the app entry during build.\n * The badge appears at the bottom of the page, then fades out after a\n * few seconds or when the user scrolls.\n */\nexport const upstartBranding = createUnplugin<UpstartBrandingPluginOptions>((options = {}) => {\n const { enabled } = { ...DEFAULT_OPTIONS, ...options };\n\n if (!enabled) {\n return { name: \"upstart-branding-disabled\" };\n }\n\n const runtimePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), \"./runtime\");\n\n return {\n name: \"upstart-branding\",\n enforce: \"pre\",\n\n transform(code, id) {\n const [cleanId] = id.split(\"?\");\n if (!cleanId || cleanId.includes(\"node_modules\")) {\n return null;\n }\n\n if (!/\\.(t|j)sx?$/.test(cleanId)) {\n return null;\n }\n\n if (!cleanId.includes(\"entry.client.tsx\")) {\n return null;\n }\n\n if (code.includes(\"initUpstartBranding\")) {\n return null;\n }\n\n const imports = `import { initUpstartBranding } from ${JSON.stringify(runtimePath)};`;\n const injection = \"requestIdleCallback(initUpstartBranding);\";\n\n return {\n code: `${imports}${code}${injection}`,\n map: null,\n };\n },\n };\n});\n\nexport default upstartBranding.vite;\n"],"mappings":";;;;AAKA,MAAM,kBAA0D,EAC9D,SAAS,OACV;;;;;;;;AASD,MAAa,kBAAkB,gBAA8C,UAAU,EAAE,KAAK;CAC5F,MAAM,EAAE,YAAY;EAAE,GAAG;EAAiB,GAAG;EAAS;CAEtD,IAAI,CAAC,SACH,OAAO,EAAE,MAAM,6BAA6B;CAG9C,MAAM,cAAc,KAAK,QAAQ,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC,EAAE,YAAY;CAE3F,OAAO;EACL,MAAM;EACN,SAAS;EAET,UAAU,MAAM,IAAI;GAClB,MAAM,CAAC,WAAW,GAAG,MAAM,IAAI;GAC/B,IAAI,CAAC,WAAW,QAAQ,SAAS,eAAe,EAC9C,OAAO;GAGT,IAAI,CAAC,cAAc,KAAK,QAAQ,EAC9B,OAAO;GAGT,IAAI,CAAC,QAAQ,SAAS,mBAAmB,EACvC,OAAO;GAGT,IAAI,KAAK,SAAS,sBAAsB,EACtC,OAAO;GAMT,OAAO;IACL,MAAM,GAAG,uCAJ4C,KAAK,UAAU,YAAY,CAAC,KAI9D,KAAA;IACnB,KAAK;IACN;;EAEJ;EACD;AAEF,IAAA,iBAAe,gBAAgB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.js","names":[],"sources":["../../src/vite-plugin-upstart-branding/runtime.ts"],"sourcesContent":["const UPSTART_ICON_SVG = `<svg width=\"18\" height=\"18\" viewBox=\"0 0 512 512\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<g clip-path=\"url(#clip0_upstart_branding)\">\n<path d=\"M452 0H60C26.8629 0 0 26.8629 0 60V452C0 485.137 26.8629 512 60 512H452C485.137 512 512 485.137 512 452V60C512 26.8629 485.137 0 452 0Z\" fill=\"#817FCC\"/>\n<path d=\"M346.505 112H410L377.739 307.306C374.122 329.235 365.764 348.423 352.664 364.87C339.564 381.316 322.896 394.141 302.66 403.343C282.424 412.448 259.841 417 234.913 417C209.984 417 188.966 412.448 171.858 403.343C154.75 394.141 142.383 381.316 134.758 364.87C127.133 348.423 125.129 329.235 128.746 307.306L161.006 112H224.501L192.974 301.872C191.214 313.326 192.094 323.508 195.613 332.416C199.23 341.325 205.194 348.325 213.503 353.416C221.813 358.506 232.078 361.052 244.297 361.052C256.615 361.052 267.76 358.506 277.731 353.416C287.801 348.325 296.061 341.325 302.513 332.416C309.063 323.508 313.218 313.326 314.978 301.872L346.505 112Z\" fill=\"white\"/>\n<path d=\"M101 119C101 115.134 104.134 112 108 112H164C167.866 112 171 115.134 171 119V160C171 163.866 167.866 167 164 167H108C104.134 167 101 163.866 101 160V119Z\" fill=\"white\"/>\n</g>\n<defs><clipPath id=\"clip0_upstart_branding\"><rect width=\"512\" height=\"512\" fill=\"white\"/></clipPath></defs>\n</svg>`;\n\nconst BADGE_ID = \"upstart-branding-badge\";\nconst DISMISS_DELAY_MS = 4000;\n\nlet isInitialized = false;\n\n/**\n * Initialize the Upstart branding badge.\n * Injects a fixed-position badge at the bottom center of the viewport.\n * The badge slides up and fades in, then dismisses after ~4 seconds or on scroll.\n */\nexport function initUpstartBranding(): void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return;\n }\n\n if (isInitialized) {\n return;\n }\n\n isInitialized = true;\n\n // Delay initialization to wait for React hydration\n if (\"scheduler\" in window) {\n window.scheduler.postTask(() => injectBranding(), { delay: 250, priority: \"background\" });\n } else {\n setTimeout(() => injectBranding(), 250);\n }\n}\n\nfunction injectBranding(): void {\n const style = document.createElement(\"style\");\n style.textContent = `\n @keyframes upstart-branding-slide-in {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n }\n\n #${BADGE_ID} {\n position: fixed;\n bottom: 16px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 2147483647;\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 14px;\n background: rgba(30, 30, 30, 0.75);\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n border-radius: 9999px;\n box-shadow: 0 4px 24px rgba(0, 0, 0, 0.18), 0 1px 4px rgba(0, 0, 0, 0.1);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n font-size: 13px;\n font-weight: 500;\n color: rgba(255, 255, 255, 0.9);\n text-decoration: none;\n cursor: pointer;\n pointer-events: auto;\n animation: upstart-branding-slide-in 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;\n transition: opacity 0.4s ease, transform 0.4s ease;\n white-space: nowrap;\n line-height: 1;\n }\n\n #${BADGE_ID}:hover {\n background: rgba(30, 30, 30, 0.9);\n color: #fff;\n }\n\n #${BADGE_ID}.upstart-branding-hiding {\n opacity: 0;\n transform: translateX(-50%) translateY(12px);\n pointer-events: none;\n }\n\n #${BADGE_ID} svg {\n flex-shrink: 0;\n border-radius: 3px;\n }\n `;\n document.head.appendChild(style);\n\n const badge = document.createElement(\"a\");\n badge.id = BADGE_ID;\n badge.href = \"https://upstart.gg\";\n badge.target = \"_blank\";\n badge.rel = \"noopener noreferrer\";\n badge.innerHTML = `${UPSTART_ICON_SVG}<span>Made with Upstart</span>`;\n\n document.body.appendChild(badge);\n\n let dismissed = false;\n function dismiss() {\n if (dismissed) return;\n dismissed = true;\n badge.classList.add(\"upstart-branding-hiding\");\n setTimeout(() => {\n badge.remove();\n style.remove();\n }, 500);\n window.removeEventListener(\"scroll\", onScroll);\n clearTimeout(timer);\n }\n\n const timer = setTimeout(dismiss, DISMISS_DELAY_MS);\n\n function onScroll() {\n dismiss();\n }\n window.addEventListener(\"scroll\", onScroll, { passive: true, once: true });\n}\n"],"mappings":";AAAA,MAAM,mBAAmB;;;;;;;;AASzB,MAAM,WAAW;AACjB,MAAM,mBAAmB;AAEzB,IAAI,gBAAgB;;;;;;AAOpB,SAAgB,sBAA4B;
|
|
1
|
+
{"version":3,"file":"runtime.js","names":[],"sources":["../../src/vite-plugin-upstart-branding/runtime.ts"],"sourcesContent":["const UPSTART_ICON_SVG = `<svg width=\"18\" height=\"18\" viewBox=\"0 0 512 512\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<g clip-path=\"url(#clip0_upstart_branding)\">\n<path d=\"M452 0H60C26.8629 0 0 26.8629 0 60V452C0 485.137 26.8629 512 60 512H452C485.137 512 512 485.137 512 452V60C512 26.8629 485.137 0 452 0Z\" fill=\"#817FCC\"/>\n<path d=\"M346.505 112H410L377.739 307.306C374.122 329.235 365.764 348.423 352.664 364.87C339.564 381.316 322.896 394.141 302.66 403.343C282.424 412.448 259.841 417 234.913 417C209.984 417 188.966 412.448 171.858 403.343C154.75 394.141 142.383 381.316 134.758 364.87C127.133 348.423 125.129 329.235 128.746 307.306L161.006 112H224.501L192.974 301.872C191.214 313.326 192.094 323.508 195.613 332.416C199.23 341.325 205.194 348.325 213.503 353.416C221.813 358.506 232.078 361.052 244.297 361.052C256.615 361.052 267.76 358.506 277.731 353.416C287.801 348.325 296.061 341.325 302.513 332.416C309.063 323.508 313.218 313.326 314.978 301.872L346.505 112Z\" fill=\"white\"/>\n<path d=\"M101 119C101 115.134 104.134 112 108 112H164C167.866 112 171 115.134 171 119V160C171 163.866 167.866 167 164 167H108C104.134 167 101 163.866 101 160V119Z\" fill=\"white\"/>\n</g>\n<defs><clipPath id=\"clip0_upstart_branding\"><rect width=\"512\" height=\"512\" fill=\"white\"/></clipPath></defs>\n</svg>`;\n\nconst BADGE_ID = \"upstart-branding-badge\";\nconst DISMISS_DELAY_MS = 4000;\n\nlet isInitialized = false;\n\n/**\n * Initialize the Upstart branding badge.\n * Injects a fixed-position badge at the bottom center of the viewport.\n * The badge slides up and fades in, then dismisses after ~4 seconds or on scroll.\n */\nexport function initUpstartBranding(): void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return;\n }\n\n if (isInitialized) {\n return;\n }\n\n isInitialized = true;\n\n // Delay initialization to wait for React hydration\n if (\"scheduler\" in window) {\n window.scheduler.postTask(() => injectBranding(), { delay: 250, priority: \"background\" });\n } else {\n setTimeout(() => injectBranding(), 250);\n }\n}\n\nfunction injectBranding(): void {\n const style = document.createElement(\"style\");\n style.textContent = `\n @keyframes upstart-branding-slide-in {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n }\n\n #${BADGE_ID} {\n position: fixed;\n bottom: 16px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 2147483647;\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 14px;\n background: rgba(30, 30, 30, 0.75);\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n border-radius: 9999px;\n box-shadow: 0 4px 24px rgba(0, 0, 0, 0.18), 0 1px 4px rgba(0, 0, 0, 0.1);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n font-size: 13px;\n font-weight: 500;\n color: rgba(255, 255, 255, 0.9);\n text-decoration: none;\n cursor: pointer;\n pointer-events: auto;\n animation: upstart-branding-slide-in 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;\n transition: opacity 0.4s ease, transform 0.4s ease;\n white-space: nowrap;\n line-height: 1;\n }\n\n #${BADGE_ID}:hover {\n background: rgba(30, 30, 30, 0.9);\n color: #fff;\n }\n\n #${BADGE_ID}.upstart-branding-hiding {\n opacity: 0;\n transform: translateX(-50%) translateY(12px);\n pointer-events: none;\n }\n\n #${BADGE_ID} svg {\n flex-shrink: 0;\n border-radius: 3px;\n }\n `;\n document.head.appendChild(style);\n\n const badge = document.createElement(\"a\");\n badge.id = BADGE_ID;\n badge.href = \"https://upstart.gg\";\n badge.target = \"_blank\";\n badge.rel = \"noopener noreferrer\";\n badge.innerHTML = `${UPSTART_ICON_SVG}<span>Made with Upstart</span>`;\n\n document.body.appendChild(badge);\n\n let dismissed = false;\n function dismiss() {\n if (dismissed) return;\n dismissed = true;\n badge.classList.add(\"upstart-branding-hiding\");\n setTimeout(() => {\n badge.remove();\n style.remove();\n }, 500);\n window.removeEventListener(\"scroll\", onScroll);\n clearTimeout(timer);\n }\n\n const timer = setTimeout(dismiss, DISMISS_DELAY_MS);\n\n function onScroll() {\n dismiss();\n }\n window.addEventListener(\"scroll\", onScroll, { passive: true, once: true });\n}\n"],"mappings":";AAAA,MAAM,mBAAmB;;;;;;;;AASzB,MAAM,WAAW;AACjB,MAAM,mBAAmB;AAEzB,IAAI,gBAAgB;;;;;;AAOpB,SAAgB,sBAA4B;CAC1C,IAAI,OAAO,WAAW,eAAe,OAAO,aAAa,aACvD;CAGF,IAAI,eACF;CAGF,gBAAgB;CAGhB,IAAI,eAAe,QACjB,OAAO,UAAU,eAAe,gBAAgB,EAAE;EAAE,OAAO;EAAK,UAAU;EAAc,CAAC;MAEzF,iBAAiB,gBAAgB,EAAE,IAAI;;AAI3C,SAAS,iBAAuB;CAC9B,MAAM,QAAQ,SAAS,cAAc,QAAQ;CAC7C,MAAM,cAAc;;;;;;;;;;;;OAYf,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BT,SAAS;;;;;OAKT,SAAS;;;;;;OAMT,SAAS;;;;;CAKd,SAAS,KAAK,YAAY,MAAM;CAEhC,MAAM,QAAQ,SAAS,cAAc,IAAI;CACzC,MAAM,KAAK;CACX,MAAM,OAAO;CACb,MAAM,SAAS;CACf,MAAM,MAAM;CACZ,MAAM,YAAY,GAAG,iBAAiB;CAEtC,SAAS,KAAK,YAAY,MAAM;CAEhC,IAAI,YAAY;CAChB,SAAS,UAAU;EACjB,IAAI,WAAW;EACf,YAAY;EACZ,MAAM,UAAU,IAAI,0BAA0B;EAC9C,iBAAiB;GACf,MAAM,QAAQ;GACd,MAAM,QAAQ;KACb,IAAI;EACP,OAAO,oBAAoB,UAAU,SAAS;EAC9C,aAAa,MAAM;;CAGrB,MAAM,QAAQ,WAAW,SAAS,iBAAiB;CAEnD,SAAS,WAAW;EAClB,SAAS;;CAEX,OAAO,iBAAiB,UAAU,UAAU;EAAE,SAAS;EAAM,MAAM;EAAM,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { UpstartEditorPluginOptions } from "./runtime/types.js";
|
|
2
|
-
import * as unplugin from "unplugin";
|
|
2
|
+
import * as _$unplugin from "unplugin";
|
|
3
3
|
|
|
4
4
|
//#region src/vite-plugin-upstart-editor/plugin.d.ts
|
|
5
5
|
/**
|
|
@@ -7,8 +7,8 @@ import * as unplugin from "unplugin";
|
|
|
7
7
|
*
|
|
8
8
|
* Injects the editor runtime into the app entry during build.
|
|
9
9
|
*/
|
|
10
|
-
declare const upstartEditor: unplugin.UnpluginInstance<UpstartEditorPluginOptions, boolean>;
|
|
11
|
-
declare const _default: (options: UpstartEditorPluginOptions) => unplugin.VitePlugin<any>[] | unplugin.VitePlugin<any>;
|
|
10
|
+
declare const upstartEditor: _$unplugin.UnpluginInstance<UpstartEditorPluginOptions, boolean>;
|
|
11
|
+
declare const _default: (options: UpstartEditorPluginOptions) => _$unplugin.VitePlugin<any>[] | _$unplugin.VitePlugin<any>;
|
|
12
12
|
//#endregion
|
|
13
13
|
export { _default as default, upstartEditor };
|
|
14
14
|
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","names":[],"sources":["../../src/vite-plugin-upstart-editor/plugin.ts"],"mappings":";;;;;;;AAeA;;cAAa,aAAA,EAAa,
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","names":[],"sources":["../../src/vite-plugin-upstart-editor/plugin.ts"],"mappings":";;;;;;;AAeA;;cAAa,aAAA,EAAa,UAAA,CAAA,gBAAA,CAAA,0BAAA;AAAA,cAkDvB,QAAA"}
|
|
@@ -27,8 +27,11 @@ const upstartEditor = createUnplugin((options = {}) => {
|
|
|
27
27
|
if (!cleanId || cleanId.includes("node_modules")) return null;
|
|
28
28
|
if (!cleanId.includes("entry.client.tsx")) return null;
|
|
29
29
|
if (code.includes("initUpstartEditor")) return null;
|
|
30
|
+
const imports = `import { initUpstartEditor, waitForHydration } from ${JSON.stringify(runtimePath)};`;
|
|
31
|
+
const injection = "waitForHydration(initUpstartEditor);";
|
|
32
|
+
const i18nextVarName = code.match(/import\s+(\w+)\s+from\s+["']i18next["']/)?.[1];
|
|
30
33
|
return {
|
|
31
|
-
code: `${
|
|
34
|
+
code: `${imports}${i18nextVarName ? `;(function(){var _i=` + i18nextVarName + `,_o=_i.init.bind(_i);_i.init=function(){return Promise.resolve(_o.apply(_i,arguments)).then(function(r){if(typeof window!=="undefined")window.__i18next=_i;return r;});};})();` : ""}${code}${injection}`,
|
|
32
35
|
map: null
|
|
33
36
|
};
|
|
34
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","names":[],"sources":["../../src/vite-plugin-upstart-editor/plugin.ts"],"sourcesContent":["import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { createUnplugin } from \"unplugin\";\nimport type { UpstartEditorPluginOptions } from \"./runtime/types.js\";\n\nconst DEFAULT_OPTIONS: Required<UpstartEditorPluginOptions> = {\n enabled: false,\n autoInject: true,\n};\n\n/**\n * Upstart Visual Editor Vite plugin (build-time)\n *\n * Injects the editor runtime into the app entry during build.\n */\nexport const upstartEditor = createUnplugin<UpstartEditorPluginOptions>((options = {}) => {\n const { enabled, autoInject } = { ...DEFAULT_OPTIONS, ...options };\n\n if (!enabled) {\n return { name: \"upstart-editor-disabled\" };\n }\n\n const runtimePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), \"./runtime/index\");\n\n return {\n name: \"upstart-editor\",\n enforce: \"pre\",\n\n transform(code, id) {\n if (!autoInject) {\n return null;\n }\n\n const [cleanId] = id.split(\"?\");\n if (!cleanId || cleanId.includes(\"node_modules\")) {\n return null;\n }\n\n if (!cleanId.includes(\"entry.client.tsx\")) {\n return null;\n }\n\n if (code.includes(\"initUpstartEditor\")) {\n return null;\n }\n\n const imports = `import { initUpstartEditor, waitForHydration } from ${JSON.stringify(runtimePath)};`;\n const injection = \"waitForHydration(initUpstartEditor);\";\n\n return {\n code: `${imports}${code}${injection}`,\n map: null,\n };\n },\n };\n});\n\nexport default upstartEditor.vite;\n"],"mappings":";;;;AAKA,MAAM,kBAAwD;CAC5D,SAAS;CACT,YAAY;CACb;;;;;;AAOD,MAAa,gBAAgB,gBAA4C,UAAU,EAAE,KAAK;CACxF,MAAM,EAAE,SAAS,eAAe;EAAE,GAAG;EAAiB,GAAG;EAAS;
|
|
1
|
+
{"version":3,"file":"plugin.js","names":[],"sources":["../../src/vite-plugin-upstart-editor/plugin.ts"],"sourcesContent":["import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { createUnplugin } from \"unplugin\";\nimport type { UpstartEditorPluginOptions } from \"./runtime/types.js\";\n\nconst DEFAULT_OPTIONS: Required<UpstartEditorPluginOptions> = {\n enabled: false,\n autoInject: true,\n};\n\n/**\n * Upstart Visual Editor Vite plugin (build-time)\n *\n * Injects the editor runtime into the app entry during build.\n */\nexport const upstartEditor = createUnplugin<UpstartEditorPluginOptions>((options = {}) => {\n const { enabled, autoInject } = { ...DEFAULT_OPTIONS, ...options };\n\n if (!enabled) {\n return { name: \"upstart-editor-disabled\" };\n }\n\n const runtimePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), \"./runtime/index\");\n\n return {\n name: \"upstart-editor\",\n enforce: \"pre\",\n\n transform(code, id) {\n if (!autoInject) {\n return null;\n }\n\n const [cleanId] = id.split(\"?\");\n if (!cleanId || cleanId.includes(\"node_modules\")) {\n return null;\n }\n\n if (!cleanId.includes(\"entry.client.tsx\")) {\n return null;\n }\n\n if (code.includes(\"initUpstartEditor\")) {\n return null;\n }\n\n const imports = `import { initUpstartEditor, waitForHydration } from ${JSON.stringify(runtimePath)};`;\n const injection = \"waitForHydration(initUpstartEditor);\";\n\n // If this entry file imports i18next, monkey-patch its init() so the\n // resolved instance is exposed on window.__i18next for the editor runtime.\n const i18nextImportMatch = code.match(/import\\s+(\\w+)\\s+from\\s+[\"']i18next[\"']/);\n const i18nextVarName = i18nextImportMatch?.[1];\n const i18nextPatch = i18nextVarName\n ? `;(function(){var _i=` +\n i18nextVarName +\n `,_o=_i.init.bind(_i);_i.init=function(){return Promise.resolve(_o.apply(_i,arguments)).then(function(r){if(typeof window!==\"undefined\")window.__i18next=_i;return r;});};})();`\n : \"\";\n\n return {\n code: `${imports}${i18nextPatch}${code}${injection}`,\n map: null,\n };\n },\n };\n});\n\nexport default upstartEditor.vite;\n"],"mappings":";;;;AAKA,MAAM,kBAAwD;CAC5D,SAAS;CACT,YAAY;CACb;;;;;;AAOD,MAAa,gBAAgB,gBAA4C,UAAU,EAAE,KAAK;CACxF,MAAM,EAAE,SAAS,eAAe;EAAE,GAAG;EAAiB,GAAG;EAAS;CAElE,IAAI,CAAC,SACH,OAAO,EAAE,MAAM,2BAA2B;CAG5C,MAAM,cAAc,KAAK,QAAQ,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC,EAAE,kBAAkB;CAEjG,OAAO;EACL,MAAM;EACN,SAAS;EAET,UAAU,MAAM,IAAI;GAClB,IAAI,CAAC,YACH,OAAO;GAGT,MAAM,CAAC,WAAW,GAAG,MAAM,IAAI;GAC/B,IAAI,CAAC,WAAW,QAAQ,SAAS,eAAe,EAC9C,OAAO;GAGT,IAAI,CAAC,QAAQ,SAAS,mBAAmB,EACvC,OAAO;GAGT,IAAI,KAAK,SAAS,oBAAoB,EACpC,OAAO;GAGT,MAAM,UAAU,uDAAuD,KAAK,UAAU,YAAY,CAAC;GACnG,MAAM,YAAY;GAKlB,MAAM,iBADqB,KAAK,MAAM,0CACG,GAAG;GAO5C,OAAO;IACL,MAAM,GAAG,UAPU,iBACjB,yBACA,iBACA,mLACA,KAGgC,OAAO;IACzC,KAAK;IACN;;EAEJ;EACD;AAEF,IAAA,iBAAe,cAAc"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getCurrentMode } from "
|
|
1
|
+
import { getCurrentMode } from "./state.js";
|
|
2
2
|
import { sendToParent } from "./utils.js";
|
|
3
3
|
//#region src/vite-plugin-upstart-editor/runtime/click-handler.ts
|
|
4
4
|
let isInitialized = false;
|
|
@@ -59,7 +59,15 @@ function handleClick(event) {
|
|
|
59
59
|
if (target.closest("[contenteditable='true']")) {
|
|
60
60
|
const editableEl = target.closest("[data-upstart-editable-text='true']");
|
|
61
61
|
if (editableEl) {
|
|
62
|
-
|
|
62
|
+
let classNameId = editableEl.dataset.upstartClassnameId ?? "";
|
|
63
|
+
let currentClassName = editableEl.className;
|
|
64
|
+
if (!classNameId) {
|
|
65
|
+
const parentWithClassname = editableEl.closest("[data-upstart-classname-id]");
|
|
66
|
+
if (parentWithClassname) {
|
|
67
|
+
classNameId = parentWithClassname.dataset.upstartClassnameId ?? "";
|
|
68
|
+
currentClassName = parentWithClassname.className;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
63
71
|
if (classNameId) {
|
|
64
72
|
const computedStyle = getComputedStyle(document.documentElement);
|
|
65
73
|
const themeColors = {};
|
|
@@ -73,7 +81,7 @@ function handleClick(event) {
|
|
|
73
81
|
componentName: editableEl.dataset.upstartComponent,
|
|
74
82
|
filePath: editableEl.dataset.upstartFile ?? "",
|
|
75
83
|
classNameId,
|
|
76
|
-
currentClassName
|
|
84
|
+
currentClassName,
|
|
77
85
|
themeColors,
|
|
78
86
|
bounds: {
|
|
79
87
|
top: 0,
|
|
@@ -89,11 +97,12 @@ function handleClick(event) {
|
|
|
89
97
|
console.info("[Upstart Editor] Click inside contenteditable: TipTap handles text editing");
|
|
90
98
|
return;
|
|
91
99
|
}
|
|
92
|
-
const
|
|
93
|
-
if (!
|
|
100
|
+
const rawElement = target.closest("[data-upstart-hash]");
|
|
101
|
+
if (!rawElement) {
|
|
94
102
|
console.info("[Upstart Editor] Click ignored: no ancestral element with data-upstart-hash found");
|
|
95
103
|
return;
|
|
96
104
|
}
|
|
105
|
+
const element = rawElement.parentElement?.closest("[data-upstart-editable-text-mode=\"rich-panel\"]") ?? rawElement;
|
|
97
106
|
event.stopPropagation();
|
|
98
107
|
console.log("Element clicked dataset:", element.dataset);
|
|
99
108
|
const hash = element.dataset.upstartHash;
|
|
@@ -112,9 +121,6 @@ function handleClick(event) {
|
|
|
112
121
|
currentClassName = parentWithClassname.className;
|
|
113
122
|
}
|
|
114
123
|
}
|
|
115
|
-
const datasourceEl = target.closest("[data-upstart-datasource][data-upstart-record-id]");
|
|
116
|
-
const datasourceId = datasourceEl?.dataset.upstartDatasource;
|
|
117
|
-
const recordId = datasourceEl?.dataset.upstartRecordId;
|
|
118
124
|
const computedStyle = getComputedStyle(document.documentElement);
|
|
119
125
|
const themeColors = {};
|
|
120
126
|
for (const name of DAISY_VAR_NAMES) {
|
|
@@ -122,6 +128,17 @@ function handleClick(event) {
|
|
|
122
128
|
if (val) themeColors[name] = val;
|
|
123
129
|
}
|
|
124
130
|
const rect = element.getBoundingClientRect();
|
|
131
|
+
const bounds = {
|
|
132
|
+
top: rect.top,
|
|
133
|
+
left: rect.left,
|
|
134
|
+
width: rect.width,
|
|
135
|
+
height: rect.height,
|
|
136
|
+
right: rect.right,
|
|
137
|
+
bottom: rect.bottom
|
|
138
|
+
};
|
|
139
|
+
const datasourceEl = target.closest("[data-upstart-datasource][data-upstart-record-id]");
|
|
140
|
+
const datasourceId = datasourceEl?.dataset.upstartDatasource;
|
|
141
|
+
const recordId = datasourceEl?.dataset.upstartRecordId;
|
|
125
142
|
sendToParent({
|
|
126
143
|
type: "element-clicked",
|
|
127
144
|
hash,
|
|
@@ -132,14 +149,8 @@ function handleClick(event) {
|
|
|
132
149
|
datasourceId,
|
|
133
150
|
recordId,
|
|
134
151
|
themeColors,
|
|
135
|
-
bounds
|
|
136
|
-
|
|
137
|
-
left: rect.left,
|
|
138
|
-
width: rect.width,
|
|
139
|
-
height: rect.height,
|
|
140
|
-
right: rect.right,
|
|
141
|
-
bottom: rect.bottom
|
|
142
|
-
}
|
|
152
|
+
bounds,
|
|
153
|
+
viewportWidth: window.innerWidth
|
|
143
154
|
});
|
|
144
155
|
console.log("[Upstart Editor] Element clicked:", componentName, hash);
|
|
145
156
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"click-handler.js","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/click-handler.ts"],"sourcesContent":["import { getCurrentMode } from \"./state.js\";\nimport { sendToParent } from \"./utils.js\";\n\nlet isInitialized = false;\n\nconst DAISY_VAR_NAMES = [\n \"base-100\",\n \"base-200\",\n \"base-300\",\n \"base-content\",\n \"primary\",\n \"primary-content\",\n \"secondary\",\n \"secondary-content\",\n \"accent\",\n \"accent-content\",\n \"neutral\",\n \"neutral-content\",\n \"info\",\n \"info-content\",\n \"success\",\n \"success-content\",\n \"warning\",\n \"warning-content\",\n \"error\",\n \"error-content\",\n];\n\n/**\n * Initialize click handler for className editing.\n */\nexport function initClickHandler(): void {\n if (typeof document === \"undefined\") {\n return;\n }\n\n if (isInitialized) {\n return;\n }\n\n console.log(\"[Upstart Editor] Initializing click handler...\");\n document.addEventListener(\"click\", handleClick, true);\n isInitialized = true;\n}\n\n/**\n * Cleanup click handler.\n */\nexport function cleanupClickHandler(): void {\n document.removeEventListener(\"click\", handleClick, true);\n isInitialized = false;\n}\n\nfunction handleClick(event: MouseEvent): void {\n if (getCurrentMode() !== \"edit\") {\n return;\n }\n\n console.debug(\"[Upstart Editor] Click event:\", event);\n\n const target = event.target as HTMLElement | null;\n if (!target) {\n console.warn(\"[Upstart Editor] Click target is not an HTMLElement\");\n return;\n }\n\n if (target.closest(\".upstart-editor-bubble-menu\")) {\n console.info(\"[Upstart Editor] Click ignored: target is inside the bubble menu\");\n return;\n }\n\n const activeLink = target.closest(\"a[data-upstart-editor-active]\");\n const isFormControlClick = Boolean(\n (target as HTMLElement | null)?.closest(\"input, textarea, select, button\"),\n );\n if (activeLink || !isFormControlClick) {\n event.preventDefault();\n }\n\n // Clicks inside contenteditable (TipTap active) — still notify style panel if applicable\n if (target.closest(\"[contenteditable='true']\")) {\n const editableEl = target.closest<HTMLElement>(\"[data-upstart-editable-text='true']\");\n if (editableEl) {\n
|
|
1
|
+
{"version":3,"file":"click-handler.js","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/click-handler.ts"],"sourcesContent":["import { getCurrentMode } from \"./state.js\";\nimport { sendToParent } from \"./utils.js\";\n\nlet isInitialized = false;\n\nconst DAISY_VAR_NAMES = [\n \"base-100\",\n \"base-200\",\n \"base-300\",\n \"base-content\",\n \"primary\",\n \"primary-content\",\n \"secondary\",\n \"secondary-content\",\n \"accent\",\n \"accent-content\",\n \"neutral\",\n \"neutral-content\",\n \"info\",\n \"info-content\",\n \"success\",\n \"success-content\",\n \"warning\",\n \"warning-content\",\n \"error\",\n \"error-content\",\n];\n\n/**\n * Initialize click handler for className editing.\n */\nexport function initClickHandler(): void {\n if (typeof document === \"undefined\") {\n return;\n }\n\n if (isInitialized) {\n return;\n }\n\n console.log(\"[Upstart Editor] Initializing click handler...\");\n document.addEventListener(\"click\", handleClick, true);\n isInitialized = true;\n}\n\n/**\n * Cleanup click handler.\n */\nexport function cleanupClickHandler(): void {\n document.removeEventListener(\"click\", handleClick, true);\n isInitialized = false;\n}\n\nfunction handleClick(event: MouseEvent): void {\n if (getCurrentMode() !== \"edit\") {\n return;\n }\n\n console.debug(\"[Upstart Editor] Click event:\", event);\n\n const target = event.target as HTMLElement | null;\n if (!target) {\n console.warn(\"[Upstart Editor] Click target is not an HTMLElement\");\n return;\n }\n\n if (target.closest(\".upstart-editor-bubble-menu\")) {\n console.info(\"[Upstart Editor] Click ignored: target is inside the bubble menu\");\n return;\n }\n\n const activeLink = target.closest(\"a[data-upstart-editor-active]\");\n const isFormControlClick = Boolean(\n (target as HTMLElement | null)?.closest(\"input, textarea, select, button\"),\n );\n if (activeLink || !isFormControlClick) {\n event.preventDefault();\n }\n\n // Clicks inside contenteditable (TipTap active) — still notify style panel if applicable\n if (target.closest(\"[contenteditable='true']\")) {\n const editableEl = target.closest<HTMLElement>(\"[data-upstart-editable-text='true']\");\n if (editableEl) {\n let classNameId = editableEl.dataset.upstartClassnameId ?? \"\";\n let currentClassName = editableEl.className;\n if (!classNameId) {\n const parentWithClassname = editableEl.closest<HTMLElement>(\"[data-upstart-classname-id]\");\n if (parentWithClassname) {\n classNameId = parentWithClassname.dataset.upstartClassnameId ?? \"\";\n currentClassName = parentWithClassname.className;\n }\n }\n if (classNameId) {\n const computedStyle = getComputedStyle(document.documentElement);\n const themeColors: Record<string, string> = {};\n for (const name of DAISY_VAR_NAMES) {\n const val = computedStyle.getPropertyValue(`--color-${name}`).trim();\n if (val) themeColors[name] = val;\n }\n sendToParent({\n type: \"element-clicked\",\n hash: editableEl.dataset.upstartHash ?? \"\",\n componentName: editableEl.dataset.upstartComponent,\n filePath: editableEl.dataset.upstartFile ?? \"\",\n classNameId,\n currentClassName,\n themeColors,\n bounds: { top: 0, left: 0, width: 0, height: 0, right: 0, bottom: 0 },\n });\n }\n }\n console.info(\"[Upstart Editor] Click inside contenteditable: TipTap handles text editing\");\n return;\n }\n\n // Find the closest element with data-upstart-hash\n const rawElement = target.closest<HTMLElement>(\"[data-upstart-hash]\");\n if (!rawElement) {\n console.info(\"[Upstart Editor] Click ignored: no ancestral element with data-upstart-hash found\");\n return;\n }\n\n // If the clicked element is nested inside a rich-panel editable ancestor, bubble up to it\n // so that clicking any inline child (e.g. <strong>, <em>) edits the whole rich-panel region.\n const richPanelAncestor = rawElement.parentElement?.closest<HTMLElement>(\n '[data-upstart-editable-text-mode=\"rich-panel\"]',\n );\n const element = richPanelAncestor ?? rawElement;\n\n event.stopPropagation();\n\n console.log(\"Element clicked dataset:\", element.dataset);\n\n const hash = element.dataset.upstartHash as string;\n const componentName = element.dataset.upstartComponent;\n const filePath = element.dataset.upstartFile ?? \"\";\n\n // If this element has editable-text, focus the TipTap editor (all modes now use inline TipTap)\n if (element.dataset.upstartEditableText === \"true\") {\n const proseMirror = element.querySelector<HTMLElement>(\".ProseMirror\");\n if (proseMirror) {\n proseMirror.focus();\n }\n }\n\n // Find classNameId on the element itself, or walk up to find it on a parent\n let classNameId = element.dataset.upstartClassnameId ?? \"\";\n let currentClassName = element.className;\n if (!classNameId) {\n const parentWithClassname = element.closest<HTMLElement>(\"[data-upstart-classname-id]\");\n if (parentWithClassname) {\n classNameId = parentWithClassname.dataset.upstartClassnameId ?? \"\";\n currentClassName = parentWithClassname.className;\n }\n }\n\n // Read DaisyUI CSS custom property values from the iframe's document\n const computedStyle = getComputedStyle(document.documentElement);\n const themeColors: Record<string, string> = {};\n for (const name of DAISY_VAR_NAMES) {\n const val = computedStyle.getPropertyValue(`--color-${name}`).trim();\n if (val) themeColors[name] = val;\n }\n\n const rect = element.getBoundingClientRect();\n const bounds = {\n top: rect.top,\n left: rect.left,\n width: rect.width,\n height: rect.height,\n right: rect.right,\n bottom: rect.bottom,\n };\n\n // Check if this element (or a nearby ancestor) is a datasource record\n const datasourceEl = target.closest<HTMLElement>(\"[data-upstart-datasource][data-upstart-record-id]\");\n const datasourceId = datasourceEl?.dataset.upstartDatasource;\n const recordId = datasourceEl?.dataset.upstartRecordId;\n\n sendToParent({\n type: \"element-clicked\",\n hash,\n componentName,\n filePath,\n classNameId,\n currentClassName,\n datasourceId,\n recordId,\n themeColors,\n bounds,\n viewportWidth: window.innerWidth,\n });\n\n console.log(\"[Upstart Editor] Element clicked:\", componentName, hash);\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-handler.js","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/error-handler.ts"],"sourcesContent":["import { sendToParent } from \"./utils.js\";\n\nexport function initErrorHandler() {\n // Global error handler for uncaught errors in the editor\n window.addEventListener(\"error\", (event) => {\n console.error(\"[Upstart Editor] Uncaught error in editor:\", event.error);\n sendToParent({\n type: \"editor-error\",\n error: event.error,\n });\n });\n}\n"],"mappings":";;AAEA,SAAgB,mBAAmB;
|
|
1
|
+
{"version":3,"file":"error-handler.js","names":[],"sources":["../../../src/vite-plugin-upstart-editor/runtime/error-handler.ts"],"sourcesContent":["import { sendToParent } from \"./utils.js\";\n\nexport function initErrorHandler() {\n // Global error handler for uncaught errors in the editor\n window.addEventListener(\"error\", (event) => {\n console.error(\"[Upstart Editor] Uncaught error in editor:\", event.error);\n sendToParent({\n type: \"editor-error\",\n error: event.error,\n });\n });\n}\n"],"mappings":";;AAEA,SAAgB,mBAAmB;CAEjC,OAAO,iBAAiB,UAAU,UAAU;EAC1C,QAAQ,MAAM,8CAA8C,MAAM,MAAM;EACxE,aAAa;GACX,MAAM;GACN,OAAO,MAAM;GACd,CAAC;GACF"}
|