@templatical/editor 0.33.0 → 0.34.1
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/{AiChatSidebar-J7WsHRDS.js → AiChatSidebar-CkHJ28sf.js} +3 -3
- package/dist/{AiFeatureMenu-C9mxL_7U.js → AiFeatureMenu-DWdKguDN.js} +3 -3
- package/dist/{BlockIssueBadge-BI6v_eol.js → BlockIssueBadge-DcyHTAqw.js} +2 -2
- package/dist/{BlockPreviewCanvas-mZIr9mY2.js → BlockPreviewCanvas-B621ANn9.js} +4 -4
- package/dist/{CloudHeaderExtras-CLtTtK12.js → CloudHeaderExtras-BJ3Cebhv.js} +2 -2
- package/dist/{CloudPanels-BbLAX1jH.js → CloudPanels-BGz9ygo6.js} +6 -6
- package/dist/{CollaboratorBar-OBbLOhLK.js → CollaboratorBar-KphmSQeZ.js} +2 -2
- package/dist/{ColorPicker-Cs7iK01u.js → ColorPicker-C6EOwtHC.js} +5 -5
- package/dist/{CommentsPanel-G_uNzi1a.js → CommentsPanel-DmWh8f08.js} +1 -1
- package/dist/{CommentsSidebar-DlFlpfkU.js → CommentsSidebar-CL_tffzT.js} +4 -4
- package/dist/{CountdownBlock-Bdt8qZi1.js → CountdownBlock-D1fsNsyS.js} +1 -1
- package/dist/{CountdownToolbar-BWLYViOm.js → CountdownToolbar-B8J4i5Xp.js} +2 -2
- package/dist/{DesignReferenceSidebar-CK_xTLh2.js → DesignReferenceSidebar-BDhEeaAh.js} +4 -4
- package/dist/{IssuesPanel-DIbJ_jaa.js → IssuesPanel-CiOjeRFI.js} +4 -4
- package/dist/MergeTagInput-RytOlh1A.js +448 -0
- package/dist/{MergeTagModeToggle-DeI65T5k.js → MergeTagModeToggle-Bi-FydXj.js} +2 -2
- package/dist/{ParagraphEditor-Dr-u72hu.js → ParagraphEditor-C87tkmtE.js} +12 -12
- package/dist/{RestoreVersionDialog-6sBYYt9f.js → RestoreVersionDialog-Db5rZRMA.js} +2 -2
- package/dist/RichTextEditorContent-BmTcV2-a.js +152 -0
- package/dist/{SaveBlockDialog-DABN4-Jf.js → SaveBlockDialog-CDCzx6Bo.js} +16 -16
- package/dist/{SavedBlocksBrowserModal-BoylzTEq.js → SavedBlocksBrowserModal-DQR6kJB1.js} +101 -101
- package/dist/{SavedBlocksPanels-2np4Yu1H.js → SavedBlocksPanels-DFzV0CRD.js} +1 -1
- package/dist/{SavedBlocksPickBar-BM7aTSK2.js → SavedBlocksPickBar-XyXmnSnO.js} +2 -2
- package/dist/{TemplateScoringPanel-CzWoSM1V.js → TemplateScoringPanel-CpyEKMzC.js} +4 -4
- package/dist/{TestEmailModal-Cb0n7Dhj.js → TestEmailModal-DIfJy9eE.js} +8 -8
- package/dist/{TestEmailPanel-52HjvuU7.js → TestEmailPanel-C8Wx5yPS.js} +1 -1
- package/dist/{TitleEditor-_taBpBfB.js → TitleEditor-iNFOisr4.js} +11 -11
- package/dist/{TplModal-CEYnwBBk.js → TplModal-CowM3o_F.js} +5 -5
- package/dist/{VersionHistoryMenu-CsGlLSO8.js → VersionHistoryMenu-CV90OhXs.js} +2 -2
- package/dist/{VersionHistoryPanels-DPmYk00L.js → VersionHistoryPanels-BE3pplJG.js} +1 -1
- package/dist/{VersionPreviewBanner-VGJbEoXj.js → VersionPreviewBanner-BwUWBtlH.js} +1 -1
- package/dist/{blockTypeIcons-IhdBwbxp.js → blockTypeIcons-DQsKtHik.js} +1 -1
- package/dist/bundle-stats.json +6 -6
- package/dist/cdn/chunks/{AiChatSidebar-6S5LPyRW.js → AiChatSidebar-BEnIWn6s.js} +4 -4
- package/dist/cdn/chunks/AiChatSidebar-BEnIWn6s.js.map +1 -0
- package/dist/cdn/chunks/{AiFeatureMenu-BHCtjIfV.js → AiFeatureMenu-DMl23akc.js} +4 -4
- package/dist/cdn/chunks/{AiFeatureMenu-BHCtjIfV.js.map → AiFeatureMenu-DMl23akc.js.map} +1 -1
- package/dist/cdn/chunks/{BlockIssueBadge-2-os120N.js → BlockIssueBadge-CloK4k3M.js} +3 -3
- package/dist/cdn/chunks/{BlockIssueBadge-2-os120N.js.map → BlockIssueBadge-CloK4k3M.js.map} +1 -1
- package/dist/cdn/chunks/{BlockPreviewCanvas-CYNs-Yn-.js → BlockPreviewCanvas-DSiJFJa0.js} +5 -5
- package/dist/cdn/chunks/{BlockPreviewCanvas-CYNs-Yn-.js.map → BlockPreviewCanvas-DSiJFJa0.js.map} +1 -1
- package/dist/cdn/chunks/{CloudHeaderExtras-DrbFk6Fy.js → CloudHeaderExtras-b4KpVeUN.js} +3 -3
- package/dist/cdn/chunks/{CloudHeaderExtras-DrbFk6Fy.js.map → CloudHeaderExtras-b4KpVeUN.js.map} +1 -1
- package/dist/cdn/chunks/{CloudPanels-CpzYrAPQ.js → CloudPanels-DzwOq9lp.js} +7 -7
- package/dist/cdn/chunks/CloudPanels-DzwOq9lp.js.map +1 -0
- package/dist/cdn/chunks/{CollaboratorBar-VMTCYn_z.js → CollaboratorBar-WXKvpoGK.js} +3 -3
- package/dist/cdn/chunks/{CollaboratorBar-VMTCYn_z.js.map → CollaboratorBar-WXKvpoGK.js.map} +1 -1
- package/dist/cdn/chunks/{ColorPicker-B0l_UF8G.js → ColorPicker-BubTDiS1.js} +6 -6
- package/dist/cdn/chunks/{ColorPicker-B0l_UF8G.js.map → ColorPicker-BubTDiS1.js.map} +1 -1
- package/dist/cdn/chunks/{CommentsPanel-CXMMdLrO.js → CommentsPanel-DAA2VqX4.js} +2 -2
- package/dist/cdn/chunks/{CommentsPanel-CXMMdLrO.js.map → CommentsPanel-DAA2VqX4.js.map} +1 -1
- package/dist/cdn/chunks/{CommentsSidebar-CGkhAJUW.js → CommentsSidebar-Ci0M2ePp.js} +5 -5
- package/dist/cdn/chunks/CommentsSidebar-Ci0M2ePp.js.map +1 -0
- package/dist/cdn/chunks/{CountdownBlock-CPe1ulzk.js → CountdownBlock-rK055jpy.js} +2 -2
- package/dist/cdn/chunks/{CountdownBlock-CPe1ulzk.js.map → CountdownBlock-rK055jpy.js.map} +1 -1
- package/dist/cdn/chunks/{CountdownToolbar-D5xyNd5V.js → CountdownToolbar-CzZq26Vp.js} +3 -3
- package/dist/cdn/chunks/{CountdownToolbar-D5xyNd5V.js.map → CountdownToolbar-CzZq26Vp.js.map} +1 -1
- package/dist/cdn/chunks/{DesignReferenceSidebar-D1NH1WRv.js → DesignReferenceSidebar-C_ehdX5C.js} +5 -5
- package/dist/cdn/chunks/DesignReferenceSidebar-C_ehdX5C.js.map +1 -0
- package/dist/cdn/chunks/{IssuesPanel-07SBSZVN.js → IssuesPanel-BXAN2_al.js} +5 -5
- package/dist/cdn/chunks/{IssuesPanel-07SBSZVN.js.map → IssuesPanel-BXAN2_al.js.map} +1 -1
- package/dist/cdn/chunks/MergeTagInput-1vcI9uQH.js +419 -0
- package/dist/cdn/chunks/MergeTagInput-1vcI9uQH.js.map +1 -0
- package/dist/cdn/chunks/{MergeTagModeToggle-Bg8eO5nc.js → MergeTagModeToggle-B_mUYJo4.js} +3 -3
- package/dist/cdn/chunks/{MergeTagModeToggle-Bg8eO5nc.js.map → MergeTagModeToggle-B_mUYJo4.js.map} +1 -1
- package/dist/cdn/chunks/{ParagraphEditor-x2LTi6DN.js → ParagraphEditor-DCDHMcmx.js} +10 -10
- package/dist/cdn/chunks/ParagraphEditor-DCDHMcmx.js.map +1 -0
- package/dist/cdn/chunks/{RestoreVersionDialog--SjTV0_4.js → RestoreVersionDialog-BkSSwf-Q.js} +3 -3
- package/dist/cdn/chunks/{RestoreVersionDialog--SjTV0_4.js.map → RestoreVersionDialog-BkSSwf-Q.js.map} +1 -1
- package/dist/cdn/chunks/RichTextEditorContent-S4KogjIj.js +122 -0
- package/dist/cdn/chunks/RichTextEditorContent-S4KogjIj.js.map +1 -0
- package/dist/cdn/chunks/{SaveBlockDialog-CS1lqdsQ.js → SaveBlockDialog-BH774Nt0.js} +14 -14
- package/dist/cdn/chunks/{SaveBlockDialog-CS1lqdsQ.js.map → SaveBlockDialog-BH774Nt0.js.map} +1 -1
- package/dist/cdn/chunks/{SavedBlocksBrowserModal-D6nDZ4rq.js → SavedBlocksBrowserModal-CSUqw_dD.js} +6 -6
- package/dist/cdn/chunks/{SavedBlocksBrowserModal-D6nDZ4rq.js.map → SavedBlocksBrowserModal-CSUqw_dD.js.map} +1 -1
- package/dist/cdn/chunks/{SavedBlocksPanels-DdkwvYRR.js → SavedBlocksPanels-B6ApbCjG.js} +2 -2
- package/dist/cdn/chunks/{SavedBlocksPanels-DdkwvYRR.js.map → SavedBlocksPanels-B6ApbCjG.js.map} +1 -1
- package/dist/cdn/chunks/{SavedBlocksPickBar-BLQr_6WM.js → SavedBlocksPickBar-C9MXSRcR.js} +3 -3
- package/dist/cdn/chunks/{SavedBlocksPickBar-BLQr_6WM.js.map → SavedBlocksPickBar-C9MXSRcR.js.map} +1 -1
- package/dist/cdn/chunks/{TemplateScoringPanel-CDC-JC6C.js → TemplateScoringPanel-QVOxc9TZ.js} +50 -50
- package/dist/cdn/chunks/TemplateScoringPanel-QVOxc9TZ.js.map +1 -0
- package/dist/cdn/chunks/{TestEmailModal-B2LcU11Y.js → TestEmailModal-_CZvaO58.js} +9 -9
- package/dist/cdn/chunks/{TestEmailModal-B2LcU11Y.js.map → TestEmailModal-_CZvaO58.js.map} +1 -1
- package/dist/cdn/chunks/{TestEmailPanel-ByqDSx8B.js → TestEmailPanel-DXLxvyrD.js} +2 -2
- package/dist/cdn/chunks/{TestEmailPanel-ByqDSx8B.js.map → TestEmailPanel-DXLxvyrD.js.map} +1 -1
- package/dist/cdn/chunks/{TitleEditor-DCoulgN8.js → TitleEditor-BuCOahW3.js} +9 -9
- package/dist/cdn/chunks/TitleEditor-BuCOahW3.js.map +1 -0
- package/dist/cdn/chunks/{TplModal-CxQDl-tN.js → TplModal-CqY8R3hn.js} +6 -6
- package/dist/cdn/chunks/TplModal-CqY8R3hn.js.map +1 -0
- package/dist/cdn/chunks/{VersionHistoryMenu-BOy8aMB6.js → VersionHistoryMenu-DDjIBvad.js} +3 -3
- package/dist/cdn/chunks/{VersionHistoryMenu-BOy8aMB6.js.map → VersionHistoryMenu-DDjIBvad.js.map} +1 -1
- package/dist/cdn/chunks/{VersionHistoryPanels-BPLhzW9c.js → VersionHistoryPanels-BuTw04T5.js} +2 -2
- package/dist/cdn/chunks/{VersionHistoryPanels-BPLhzW9c.js.map → VersionHistoryPanels-BuTw04T5.js.map} +1 -1
- package/dist/cdn/chunks/{VersionPreviewBanner-CYC9qP2i.js → VersionPreviewBanner-fGAv20qI.js} +2 -2
- package/dist/cdn/chunks/{VersionPreviewBanner-CYC9qP2i.js.map → VersionPreviewBanner-fGAv20qI.js.map} +1 -1
- package/dist/cdn/chunks/{createCloudRuntime-CbSqoOba.js → createCloudRuntime-JFE-sxbT.js} +2 -2
- package/dist/cdn/chunks/{createCloudRuntime-CbSqoOba.js.map → createCloudRuntime-JFE-sxbT.js.map} +1 -1
- package/dist/cdn/chunks/{extensions-CeP0iOPC.js → extensions-7z04EIGg.js} +3 -3
- package/dist/cdn/chunks/{extensions-CeP0iOPC.js.map → extensions-7z04EIGg.js.map} +1 -1
- package/dist/cdn/chunks/{keys-O8clwJ41.js → keys-DQp8x7Nx.js} +4 -4
- package/dist/cdn/chunks/{keys-O8clwJ41.js.map → keys-DQp8x7Nx.js.map} +1 -1
- package/dist/cdn/chunks/{src-C1lP5sIe.js → src-B7p3PQqm.js} +2 -2
- package/dist/cdn/chunks/{src-C1lP5sIe.js.map → src-B7p3PQqm.js.map} +1 -1
- package/dist/cdn/chunks/{useCloudI18n-ZBEJvkf4.js → useCloudI18n-MxgHWrV6.js} +2 -2
- package/dist/cdn/chunks/{useCloudI18n-ZBEJvkf4.js.map → useCloudI18n-MxgHWrV6.js.map} +1 -1
- package/dist/cdn/chunks/{useEditorCore-CZxGbZmY.js → useEditorCore-ehUSEsbw.js} +503 -500
- package/dist/cdn/chunks/useEditorCore-ehUSEsbw.js.map +1 -0
- package/dist/cdn/chunks/{useEditorRoot-DVV-EsNI.js → useEditorRoot-CteADR3y.js} +2 -2
- package/dist/cdn/chunks/{useEditorRoot-DVV-EsNI.js.map → useEditorRoot-CteADR3y.js.map} +1 -1
- package/dist/cdn/chunks/{useI18n-DCbvHv6W.js → useI18n-Ct1VIFcf.js} +2 -2
- package/dist/cdn/chunks/{useI18n-DCbvHv6W.js.map → useI18n-Ct1VIFcf.js.map} +1 -1
- package/dist/cdn/chunks/useMergeTag-TLhF2fGK.js +46 -0
- package/dist/cdn/chunks/useMergeTag-TLhF2fGK.js.map +1 -0
- package/dist/cdn/chunks/{usePopoverPosition-Ozg5iU3y.js → usePopoverPosition-BxVWE5_0.js} +2 -2
- package/dist/cdn/chunks/{usePopoverPosition-Ozg5iU3y.js.map → usePopoverPosition-BxVWE5_0.js.map} +1 -1
- package/dist/cdn/chunks/{useScrollToBlock-CrUCYsIc.js → useScrollToBlock-Cz3fpgTK.js} +2 -2
- package/dist/cdn/chunks/{useScrollToBlock-CrUCYsIc.js.map → useScrollToBlock-Cz3fpgTK.js.map} +1 -1
- package/dist/cdn/editor.css +1 -1
- package/dist/cdn/editor.js +1672 -2075
- package/dist/cdn/editor.js.map +1 -1
- package/dist/{createCloudRuntime-j6a9Z-vL.js → createCloudRuntime-DbTFm1ES.js} +1 -1
- package/dist/{extensions-5xeZR9U9.js → extensions-Mf8iHey9.js} +2 -2
- package/dist/{keys-BJJ4hJ4z.js → keys-C3P1yxnr.js} +3 -3
- package/dist/style.css +1 -1
- package/dist/templatical-editor.js +1835 -2239
- package/dist/{useCloudI18n-DaKcDMDz.js → useCloudI18n-BKoiYsWZ.js} +1 -1
- package/dist/{useEditorCore-BWrxHVKW.js → useEditorCore-DdkQuTHl.js} +1068 -1065
- package/dist/{useEditorRoot-E-oxHaEI.js → useEditorRoot-DkRGTQRs.js} +1 -1
- package/dist/{useI18n-DUVIAju4.js → useI18n-CL6RfPzK.js} +1 -1
- package/dist/useMergeTag-gSuYus-M.js +44 -0
- package/dist/{usePopoverPosition-APlWgXoQ.js → usePopoverPosition-CWxaIWd3.js} +1 -1
- package/dist/{useScrollToBlock--AzCOzlg.js → useScrollToBlock-CdI8fGqt.js} +1 -1
- package/package.json +7 -7
- package/dist/RichTextEditorContent-DKLVe6Io.js +0 -153
- package/dist/cdn/chunks/AiChatSidebar-6S5LPyRW.js.map +0 -1
- package/dist/cdn/chunks/CloudPanels-CpzYrAPQ.js.map +0 -1
- package/dist/cdn/chunks/CommentsSidebar-CGkhAJUW.js.map +0 -1
- package/dist/cdn/chunks/DesignReferenceSidebar-D1NH1WRv.js.map +0 -1
- package/dist/cdn/chunks/ParagraphEditor-x2LTi6DN.js.map +0 -1
- package/dist/cdn/chunks/RichTextEditorContent-CzdMJ1c9.js +0 -123
- package/dist/cdn/chunks/RichTextEditorContent-CzdMJ1c9.js.map +0 -1
- package/dist/cdn/chunks/TemplateScoringPanel-CDC-JC6C.js.map +0 -1
- package/dist/cdn/chunks/TitleEditor-DCoulgN8.js.map +0 -1
- package/dist/cdn/chunks/TplModal-CxQDl-tN.js.map +0 -1
- package/dist/cdn/chunks/useEditorCore-CZxGbZmY.js.map +0 -1
- package/dist/cdn/chunks/useMergeTag-CbuagYNK.js +0 -46
- package/dist/cdn/chunks/useMergeTag-CbuagYNK.js.map +0 -1
- package/dist/scan-line-E7w4yQyv.js +0 -31
- package/dist/useMergeTag-CuZZeiSE.js +0 -44
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IssuesPanel-07SBSZVN.js","names":[],"sources":["../../../src/components/sidebar/IssuesPanel.vue","../../../src/components/sidebar/IssuesPanel.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, inject } from \"vue\";\nimport {\n ListChecks,\n AlertCircle,\n AlertTriangle,\n Info,\n ArrowRight,\n Wrench,\n} from \"@lucide/vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { useScrollToBlock } from \"../../composables/useScrollToBlock\";\nimport { TEMPLATE_LINT_KEY, EDITOR_KEY } from \"../../keys\";\nimport type { LintIssue } from \"../../composables/useTemplateLint\";\n\nconst { t, format } = useI18n();\nconst lint = inject(TEMPLATE_LINT_KEY, null);\nconst editor = inject(EDITOR_KEY, null);\nconst scrollToBlock = useScrollToBlock();\n\nconst errors = computed(() =>\n (lint?.issues.value ?? []).filter((i) => i.severity === \"error\"),\n);\nconst warnings = computed(() =>\n (lint?.issues.value ?? []).filter((i) => i.severity === \"warning\"),\n);\nconst infos = computed(() =>\n (lint?.issues.value ?? []).filter((i) => i.severity === \"info\"),\n);\n\nconst totalCount = computed(\n () => errors.value.length + warnings.value.length + infos.value.length,\n);\n\nfunction jumpTo(issue: LintIssue): void {\n if (!editor) return;\n if (!issue.blockId) return;\n editor.selectBlock(issue.blockId);\n // Selecting is invisible on its own — on a long template the block sits\n // below the fold and the canvas stays put, so \"Jump\" appears to do nothing.\n scrollToBlock(issue.blockId);\n}\n\nfunction applyFix(issue: LintIssue): void {\n lint?.applyFix(issue);\n}\n</script>\n\n<template>\n <div class=\"tpl:flex tpl:flex-col tpl:gap-4 tpl:p-4\">\n <header class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <ListChecks :size=\"16\" :stroke-width=\"1.5\" />\n <h3\n class=\"tpl:m-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.issues.panelTitle }}\n </h3>\n <span\n v-if=\"totalCount > 0\"\n class=\"tpl:ml-auto tpl:rounded-full tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-0.5 tpl:text-[11px] tpl:text-[var(--tpl-text-muted)]\"\n :title=\"format(t.issues.issueCountTooltip, { count: totalCount })\"\n >\n {{ totalCount }}\n </span>\n </header>\n\n <div\n v-if=\"!lint || lint.unavailable.value\"\n class=\"tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n <!-- Quality package not available; render nothing visible beyond a hint. -->\n </div>\n\n <div\n v-else-if=\"totalCount === 0\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3 tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.issues.emptyState }}\n </div>\n\n <template v-else>\n <section\n v-for=\"group in [\n {\n key: 'errors',\n title: t.issues.groupErrors,\n icon: AlertCircle,\n items: errors,\n tone: 'tpl:text-[var(--tpl-danger)]',\n },\n {\n key: 'warnings',\n title: t.issues.groupWarnings,\n icon: AlertTriangle,\n items: warnings,\n tone: 'tpl:text-[var(--tpl-warning)]',\n },\n {\n key: 'info',\n title: t.issues.groupInfo,\n icon: Info,\n items: infos,\n tone: 'tpl:text-[var(--tpl-text-muted)]',\n },\n ]\"\n :key=\"group.key\"\n >\n <header\n v-if=\"group.items.length > 0\"\n class=\"tpl:mb-2 tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-[11px] tpl:font-semibold tpl:uppercase tpl:tracking-wide\"\n :class=\"group.tone\"\n >\n <component :is=\"group.icon\" :size=\"12\" :stroke-width=\"2\" />\n {{ group.title }}\n <span class=\"tpl:ml-auto tpl:font-normal tpl:opacity-70\">\n {{ group.items.length }}\n </span>\n </header>\n <ul\n v-if=\"group.items.length > 0\"\n class=\"tpl:m-0 tpl:mb-3 tpl:flex tpl:list-none tpl:flex-col tpl:gap-2 tpl:p-0\"\n >\n <li\n v-for=\"issue in group.items\"\n :key=\"`${issue.ruleId}-${issue.blockId ?? 'template'}`\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3\"\n >\n <p class=\"tpl:m-0 tpl:mb-2 tpl:text-xs tpl:text-[var(--tpl-text)]\">\n {{ issue.message }}\n </p>\n <p\n class=\"tpl:m-0 tpl:mb-2 tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ issue.ruleId }}\n </p>\n <div class=\"tpl:flex tpl:gap-1.5\">\n <button\n v-if=\"issue.blockId\"\n type=\"button\"\n data-testid=\"issue-jump\"\n class=\"tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-1 tpl:text-[11px] tpl:font-medium tpl:text-[var(--tpl-text)]\"\n @click=\"jumpTo(issue)\"\n >\n <ArrowRight :size=\"10\" :stroke-width=\"2\" />\n {{ t.issues.jump }}\n </button>\n <button\n v-if=\"issue.fix\"\n type=\"button\"\n class=\"tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-md tpl:bg-[var(--tpl-primary)] tpl:px-2 tpl:py-1 tpl:text-[11px] tpl:font-medium tpl:text-[var(--tpl-on-primary)]\"\n :title=\"issue.fix.description\"\n @click=\"applyFix(issue)\"\n >\n <Wrench :size=\"10\" :stroke-width=\"2\" />\n {{ t.issues.fix }}\n </button>\n </div>\n </li>\n </ul>\n </section>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject } from \"vue\";\nimport {\n ListChecks,\n AlertCircle,\n AlertTriangle,\n Info,\n ArrowRight,\n Wrench,\n} from \"@lucide/vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { useScrollToBlock } from \"../../composables/useScrollToBlock\";\nimport { TEMPLATE_LINT_KEY, EDITOR_KEY } from \"../../keys\";\nimport type { LintIssue } from \"../../composables/useTemplateLint\";\n\nconst { t, format } = useI18n();\nconst lint = inject(TEMPLATE_LINT_KEY, null);\nconst editor = inject(EDITOR_KEY, null);\nconst scrollToBlock = useScrollToBlock();\n\nconst errors = computed(() =>\n (lint?.issues.value ?? []).filter((i) => i.severity === \"error\"),\n);\nconst warnings = computed(() =>\n (lint?.issues.value ?? []).filter((i) => i.severity === \"warning\"),\n);\nconst infos = computed(() =>\n (lint?.issues.value ?? []).filter((i) => i.severity === \"info\"),\n);\n\nconst totalCount = computed(\n () => errors.value.length + warnings.value.length + infos.value.length,\n);\n\nfunction jumpTo(issue: LintIssue): void {\n if (!editor) return;\n if (!issue.blockId) return;\n editor.selectBlock(issue.blockId);\n // Selecting is invisible on its own — on a long template the block sits\n // below the fold and the canvas stays put, so \"Jump\" appears to do nothing.\n scrollToBlock(issue.blockId);\n}\n\nfunction applyFix(issue: LintIssue): void {\n lint?.applyFix(issue);\n}\n</script>\n\n<template>\n <div class=\"tpl:flex tpl:flex-col tpl:gap-4 tpl:p-4\">\n <header class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <ListChecks :size=\"16\" :stroke-width=\"1.5\" />\n <h3\n class=\"tpl:m-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.issues.panelTitle }}\n </h3>\n <span\n v-if=\"totalCount > 0\"\n class=\"tpl:ml-auto tpl:rounded-full tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-0.5 tpl:text-[11px] tpl:text-[var(--tpl-text-muted)]\"\n :title=\"format(t.issues.issueCountTooltip, { count: totalCount })\"\n >\n {{ totalCount }}\n </span>\n </header>\n\n <div\n v-if=\"!lint || lint.unavailable.value\"\n class=\"tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n <!-- Quality package not available; render nothing visible beyond a hint. -->\n </div>\n\n <div\n v-else-if=\"totalCount === 0\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3 tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.issues.emptyState }}\n </div>\n\n <template v-else>\n <section\n v-for=\"group in [\n {\n key: 'errors',\n title: t.issues.groupErrors,\n icon: AlertCircle,\n items: errors,\n tone: 'tpl:text-[var(--tpl-danger)]',\n },\n {\n key: 'warnings',\n title: t.issues.groupWarnings,\n icon: AlertTriangle,\n items: warnings,\n tone: 'tpl:text-[var(--tpl-warning)]',\n },\n {\n key: 'info',\n title: t.issues.groupInfo,\n icon: Info,\n items: infos,\n tone: 'tpl:text-[var(--tpl-text-muted)]',\n },\n ]\"\n :key=\"group.key\"\n >\n <header\n v-if=\"group.items.length > 0\"\n class=\"tpl:mb-2 tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-[11px] tpl:font-semibold tpl:uppercase tpl:tracking-wide\"\n :class=\"group.tone\"\n >\n <component :is=\"group.icon\" :size=\"12\" :stroke-width=\"2\" />\n {{ group.title }}\n <span class=\"tpl:ml-auto tpl:font-normal tpl:opacity-70\">\n {{ group.items.length }}\n </span>\n </header>\n <ul\n v-if=\"group.items.length > 0\"\n class=\"tpl:m-0 tpl:mb-3 tpl:flex tpl:list-none tpl:flex-col tpl:gap-2 tpl:p-0\"\n >\n <li\n v-for=\"issue in group.items\"\n :key=\"`${issue.ruleId}-${issue.blockId ?? 'template'}`\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3\"\n >\n <p class=\"tpl:m-0 tpl:mb-2 tpl:text-xs tpl:text-[var(--tpl-text)]\">\n {{ issue.message }}\n </p>\n <p\n class=\"tpl:m-0 tpl:mb-2 tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ issue.ruleId }}\n </p>\n <div class=\"tpl:flex tpl:gap-1.5\">\n <button\n v-if=\"issue.blockId\"\n type=\"button\"\n data-testid=\"issue-jump\"\n class=\"tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-1 tpl:text-[11px] tpl:font-medium tpl:text-[var(--tpl-text)]\"\n @click=\"jumpTo(issue)\"\n >\n <ArrowRight :size=\"10\" :stroke-width=\"2\" />\n {{ t.issues.jump }}\n </button>\n <button\n v-if=\"issue.fix\"\n type=\"button\"\n class=\"tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-md tpl:bg-[var(--tpl-primary)] tpl:px-2 tpl:py-1 tpl:text-[11px] tpl:font-medium tpl:text-[var(--tpl-on-primary)]\"\n :title=\"issue.fix.description\"\n @click=\"applyFix(issue)\"\n >\n <Wrench :size=\"10\" :stroke-width=\"2\" />\n {{ t.issues.fix }}\n </button>\n </div>\n </li>\n </ul>\n </section>\n </template>\n </div>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"IssuesPanel-BXAN2_al.js","names":[],"sources":["../../../src/components/sidebar/IssuesPanel.vue","../../../src/components/sidebar/IssuesPanel.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, inject } from \"vue\";\nimport {\n ListChecks,\n AlertCircle,\n AlertTriangle,\n Info,\n ArrowRight,\n Wrench,\n} from \"@lucide/vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { useScrollToBlock } from \"../../composables/useScrollToBlock\";\nimport { TEMPLATE_LINT_KEY, EDITOR_KEY } from \"../../keys\";\nimport type { LintIssue } from \"../../composables/useTemplateLint\";\n\nconst { t, format } = useI18n();\nconst lint = inject(TEMPLATE_LINT_KEY, null);\nconst editor = inject(EDITOR_KEY, null);\nconst scrollToBlock = useScrollToBlock();\n\nconst errors = computed(() =>\n (lint?.issues.value ?? []).filter((i) => i.severity === \"error\"),\n);\nconst warnings = computed(() =>\n (lint?.issues.value ?? []).filter((i) => i.severity === \"warning\"),\n);\nconst infos = computed(() =>\n (lint?.issues.value ?? []).filter((i) => i.severity === \"info\"),\n);\n\nconst totalCount = computed(\n () => errors.value.length + warnings.value.length + infos.value.length,\n);\n\nfunction jumpTo(issue: LintIssue): void {\n if (!editor) return;\n if (!issue.blockId) return;\n editor.selectBlock(issue.blockId);\n // Selecting is invisible on its own — on a long template the block sits\n // below the fold and the canvas stays put, so \"Jump\" appears to do nothing.\n scrollToBlock(issue.blockId);\n}\n\nfunction applyFix(issue: LintIssue): void {\n lint?.applyFix(issue);\n}\n</script>\n\n<template>\n <div class=\"tpl:flex tpl:flex-col tpl:gap-4 tpl:p-4\">\n <header class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <ListChecks :size=\"16\" :stroke-width=\"1.5\" />\n <h3\n class=\"tpl:m-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.issues.panelTitle }}\n </h3>\n <span\n v-if=\"totalCount > 0\"\n class=\"tpl:ml-auto tpl:rounded-full tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-0.5 tpl:text-[11px] tpl:text-[var(--tpl-text-muted)]\"\n :title=\"format(t.issues.issueCountTooltip, { count: totalCount })\"\n >\n {{ totalCount }}\n </span>\n </header>\n\n <div\n v-if=\"!lint || lint.unavailable.value\"\n class=\"tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n <!-- Quality package not available; render nothing visible beyond a hint. -->\n </div>\n\n <div\n v-else-if=\"totalCount === 0\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3 tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.issues.emptyState }}\n </div>\n\n <template v-else>\n <section\n v-for=\"group in [\n {\n key: 'errors',\n title: t.issues.groupErrors,\n icon: AlertCircle,\n items: errors,\n tone: 'tpl:text-[var(--tpl-danger)]',\n },\n {\n key: 'warnings',\n title: t.issues.groupWarnings,\n icon: AlertTriangle,\n items: warnings,\n tone: 'tpl:text-[var(--tpl-warning)]',\n },\n {\n key: 'info',\n title: t.issues.groupInfo,\n icon: Info,\n items: infos,\n tone: 'tpl:text-[var(--tpl-text-muted)]',\n },\n ]\"\n :key=\"group.key\"\n >\n <header\n v-if=\"group.items.length > 0\"\n class=\"tpl:mb-2 tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-[11px] tpl:font-semibold tpl:uppercase tpl:tracking-wide\"\n :class=\"group.tone\"\n >\n <component :is=\"group.icon\" :size=\"12\" :stroke-width=\"2\" />\n {{ group.title }}\n <span class=\"tpl:ml-auto tpl:font-normal tpl:opacity-70\">\n {{ group.items.length }}\n </span>\n </header>\n <ul\n v-if=\"group.items.length > 0\"\n class=\"tpl:m-0 tpl:mb-3 tpl:flex tpl:list-none tpl:flex-col tpl:gap-2 tpl:p-0\"\n >\n <li\n v-for=\"issue in group.items\"\n :key=\"`${issue.ruleId}-${issue.blockId ?? 'template'}`\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3\"\n >\n <p class=\"tpl:m-0 tpl:mb-2 tpl:text-xs tpl:text-[var(--tpl-text)]\">\n {{ issue.message }}\n </p>\n <p\n class=\"tpl:m-0 tpl:mb-2 tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ issue.ruleId }}\n </p>\n <div class=\"tpl:flex tpl:gap-1.5\">\n <button\n v-if=\"issue.blockId\"\n type=\"button\"\n data-testid=\"issue-jump\"\n class=\"tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-1 tpl:text-[11px] tpl:font-medium tpl:text-[var(--tpl-text)]\"\n @click=\"jumpTo(issue)\"\n >\n <ArrowRight :size=\"10\" :stroke-width=\"2\" />\n {{ t.issues.jump }}\n </button>\n <button\n v-if=\"issue.fix\"\n type=\"button\"\n class=\"tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-md tpl:bg-[var(--tpl-primary)] tpl:px-2 tpl:py-1 tpl:text-[11px] tpl:font-medium tpl:text-[var(--tpl-on-primary)]\"\n :title=\"issue.fix.description\"\n @click=\"applyFix(issue)\"\n >\n <Wrench :size=\"10\" :stroke-width=\"2\" />\n {{ t.issues.fix }}\n </button>\n </div>\n </li>\n </ul>\n </section>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject } from \"vue\";\nimport {\n ListChecks,\n AlertCircle,\n AlertTriangle,\n Info,\n ArrowRight,\n Wrench,\n} from \"@lucide/vue\";\nimport { useI18n } from \"../../composables/useI18n\";\nimport { useScrollToBlock } from \"../../composables/useScrollToBlock\";\nimport { TEMPLATE_LINT_KEY, EDITOR_KEY } from \"../../keys\";\nimport type { LintIssue } from \"../../composables/useTemplateLint\";\n\nconst { t, format } = useI18n();\nconst lint = inject(TEMPLATE_LINT_KEY, null);\nconst editor = inject(EDITOR_KEY, null);\nconst scrollToBlock = useScrollToBlock();\n\nconst errors = computed(() =>\n (lint?.issues.value ?? []).filter((i) => i.severity === \"error\"),\n);\nconst warnings = computed(() =>\n (lint?.issues.value ?? []).filter((i) => i.severity === \"warning\"),\n);\nconst infos = computed(() =>\n (lint?.issues.value ?? []).filter((i) => i.severity === \"info\"),\n);\n\nconst totalCount = computed(\n () => errors.value.length + warnings.value.length + infos.value.length,\n);\n\nfunction jumpTo(issue: LintIssue): void {\n if (!editor) return;\n if (!issue.blockId) return;\n editor.selectBlock(issue.blockId);\n // Selecting is invisible on its own — on a long template the block sits\n // below the fold and the canvas stays put, so \"Jump\" appears to do nothing.\n scrollToBlock(issue.blockId);\n}\n\nfunction applyFix(issue: LintIssue): void {\n lint?.applyFix(issue);\n}\n</script>\n\n<template>\n <div class=\"tpl:flex tpl:flex-col tpl:gap-4 tpl:p-4\">\n <header class=\"tpl:flex tpl:items-center tpl:gap-2\">\n <ListChecks :size=\"16\" :stroke-width=\"1.5\" />\n <h3\n class=\"tpl:m-0 tpl:text-sm tpl:font-semibold tpl:text-[var(--tpl-text)]\"\n >\n {{ t.issues.panelTitle }}\n </h3>\n <span\n v-if=\"totalCount > 0\"\n class=\"tpl:ml-auto tpl:rounded-full tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-0.5 tpl:text-[11px] tpl:text-[var(--tpl-text-muted)]\"\n :title=\"format(t.issues.issueCountTooltip, { count: totalCount })\"\n >\n {{ totalCount }}\n </span>\n </header>\n\n <div\n v-if=\"!lint || lint.unavailable.value\"\n class=\"tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n <!-- Quality package not available; render nothing visible beyond a hint. -->\n </div>\n\n <div\n v-else-if=\"totalCount === 0\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3 tpl:text-xs tpl:text-[var(--tpl-text-muted)]\"\n >\n {{ t.issues.emptyState }}\n </div>\n\n <template v-else>\n <section\n v-for=\"group in [\n {\n key: 'errors',\n title: t.issues.groupErrors,\n icon: AlertCircle,\n items: errors,\n tone: 'tpl:text-[var(--tpl-danger)]',\n },\n {\n key: 'warnings',\n title: t.issues.groupWarnings,\n icon: AlertTriangle,\n items: warnings,\n tone: 'tpl:text-[var(--tpl-warning)]',\n },\n {\n key: 'info',\n title: t.issues.groupInfo,\n icon: Info,\n items: infos,\n tone: 'tpl:text-[var(--tpl-text-muted)]',\n },\n ]\"\n :key=\"group.key\"\n >\n <header\n v-if=\"group.items.length > 0\"\n class=\"tpl:mb-2 tpl:flex tpl:items-center tpl:gap-1.5 tpl:text-[11px] tpl:font-semibold tpl:uppercase tpl:tracking-wide\"\n :class=\"group.tone\"\n >\n <component :is=\"group.icon\" :size=\"12\" :stroke-width=\"2\" />\n {{ group.title }}\n <span class=\"tpl:ml-auto tpl:font-normal tpl:opacity-70\">\n {{ group.items.length }}\n </span>\n </header>\n <ul\n v-if=\"group.items.length > 0\"\n class=\"tpl:m-0 tpl:mb-3 tpl:flex tpl:list-none tpl:flex-col tpl:gap-2 tpl:p-0\"\n >\n <li\n v-for=\"issue in group.items\"\n :key=\"`${issue.ruleId}-${issue.blockId ?? 'template'}`\"\n class=\"tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg)] tpl:p-3\"\n >\n <p class=\"tpl:m-0 tpl:mb-2 tpl:text-xs tpl:text-[var(--tpl-text)]\">\n {{ issue.message }}\n </p>\n <p\n class=\"tpl:m-0 tpl:mb-2 tpl:font-mono tpl:text-[10px] tpl:text-[var(--tpl-text-dim)]\"\n >\n {{ issue.ruleId }}\n </p>\n <div class=\"tpl:flex tpl:gap-1.5\">\n <button\n v-if=\"issue.blockId\"\n type=\"button\"\n data-testid=\"issue-jump\"\n class=\"tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-md tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-1 tpl:text-[11px] tpl:font-medium tpl:text-[var(--tpl-text)]\"\n @click=\"jumpTo(issue)\"\n >\n <ArrowRight :size=\"10\" :stroke-width=\"2\" />\n {{ t.issues.jump }}\n </button>\n <button\n v-if=\"issue.fix\"\n type=\"button\"\n class=\"tpl:flex tpl:items-center tpl:gap-1 tpl:rounded-md tpl:bg-[var(--tpl-primary)] tpl:px-2 tpl:py-1 tpl:text-[11px] tpl:font-medium tpl:text-[var(--tpl-on-primary)]\"\n :title=\"issue.fix.description\"\n @click=\"applyFix(issue)\"\n >\n <Wrench :size=\"10\" :stroke-width=\"2\" />\n {{ t.issues.fix }}\n </button>\n </div>\n </li>\n </ul>\n </section>\n </template>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import { E as e, F as t, K as n, N as r, Q as i, S as a, Y as o, at as s, b as c, c as l, ct as u, f as d, g as f, h as p, k as m, l as h, m as g, p as _, q as v, u as y, ut as b, y as x } from "./draggable-kbOhCgnk.js";
|
|
2
|
+
import { a as S, c as C, d as w, l as T } from "./src-CLoCCDfs.js";
|
|
3
|
+
import { C as E } from "./useEditorCore-ehUSEsbw.js";
|
|
4
|
+
import { W as D } from "./keys-DQp8x7Nx.js";
|
|
5
|
+
import { t as O } from "./useI18n-Ct1VIFcf.js";
|
|
6
|
+
import { n as k } from "./usePopoverPosition-BxVWE5_0.js";
|
|
7
|
+
import { P as A, Rt as j, n as M } from "./icons-SP5Itd-A.js";
|
|
8
|
+
import { t as N } from "./_plugin-vue_export-helper-B3ysoDQm.js";
|
|
9
|
+
import { t as P } from "./useMergeTag-TLhF2fGK.js";
|
|
10
|
+
import { p as F } from "./styleConstants-C_YAPdQS.js";
|
|
11
|
+
import { n as I, r as L } from "./MergeTagSuggestion-t8Uj00Er.js";
|
|
12
|
+
//#region src/utils/mergeTagTrigger.ts
|
|
13
|
+
function R(e, t, n, r) {
|
|
14
|
+
if (!n) return null;
|
|
15
|
+
let i = e.slice(0, t), a = i.lastIndexOf(n);
|
|
16
|
+
if (a === -1) return null;
|
|
17
|
+
let o = i.slice(a + n.length);
|
|
18
|
+
return /\s/.test(o) || r && o.includes(r) ? null : {
|
|
19
|
+
triggerStart: a,
|
|
20
|
+
caret: t,
|
|
21
|
+
query: o
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/utils/textFieldCaret.ts
|
|
26
|
+
var z = /* @__PURE__ */ "boxSizing.width.borderTopWidth.borderRightWidth.borderBottomWidth.borderLeftWidth.borderStyle.paddingTop.paddingRight.paddingBottom.paddingLeft.fontStyle.fontVariant.fontWeight.fontStretch.fontSize.lineHeight.fontFamily.textAlign.textTransform.textIndent.letterSpacing.wordSpacing.tabSize.whiteSpace.wordWrap.wordBreak".split(".");
|
|
27
|
+
function B(e, t, n) {
|
|
28
|
+
return {
|
|
29
|
+
left: e,
|
|
30
|
+
top: t,
|
|
31
|
+
bottom: n,
|
|
32
|
+
right: e,
|
|
33
|
+
width: 0,
|
|
34
|
+
height: n - t,
|
|
35
|
+
x: e,
|
|
36
|
+
y: t,
|
|
37
|
+
toJSON: () => ({})
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function V(e, t) {
|
|
41
|
+
let n = e.ownerDocument, r = n.defaultView;
|
|
42
|
+
if (!r) return null;
|
|
43
|
+
let i = e.tagName === "INPUT", a = r.getComputedStyle(e), o = n.createElement("div");
|
|
44
|
+
o.setAttribute("aria-hidden", "true");
|
|
45
|
+
let s = o.style;
|
|
46
|
+
s.position = "absolute", s.visibility = "hidden", s.top = "0", s.left = "0", s.overflow = "hidden", s.whiteSpace = i ? "pre" : "pre-wrap", s.wordWrap = i ? "normal" : "break-word";
|
|
47
|
+
let c = s, l = a;
|
|
48
|
+
for (let e of z) c[e] = l[e];
|
|
49
|
+
i && (s.width = "auto", s.whiteSpace = "pre");
|
|
50
|
+
let u = e.value, d = u.slice(0, t);
|
|
51
|
+
i && (d = d.replace(/\s/g, "\xA0")), o.textContent = d;
|
|
52
|
+
let f = n.createElement("span");
|
|
53
|
+
f.textContent = u.slice(t) || ".", o.appendChild(f), n.body.appendChild(o);
|
|
54
|
+
let p = f.offsetTop, m = f.offsetLeft;
|
|
55
|
+
n.body.removeChild(o);
|
|
56
|
+
let h = e.getBoundingClientRect(), g = parseFloat(a.borderTopWidth) || 0, _ = parseFloat(a.borderLeftWidth) || 0, v = parseFloat(a.lineHeight) || parseFloat(a.fontSize) || 0, y = h.left + m + _ - e.scrollLeft;
|
|
57
|
+
if (i) return B(y, h.top, h.bottom);
|
|
58
|
+
let b = h.top + p + g - e.scrollTop;
|
|
59
|
+
return B(y, b, b + v);
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/composables/useMergeTagAutocomplete.ts
|
|
63
|
+
var H = /* @__PURE__ */ new Set([
|
|
64
|
+
"ArrowLeft",
|
|
65
|
+
"ArrowRight",
|
|
66
|
+
"Home",
|
|
67
|
+
"End"
|
|
68
|
+
]);
|
|
69
|
+
function U(t) {
|
|
70
|
+
let { elementRef: r, modelValue: i, emit: a, mergeTags: s, syntax: c, enabled: l, onInsert: u } = t, d = T(c), f = C(c), p = l && d !== null && s.length > 0, h = e(D, null), g = (v(h) ? h.value : h)?.mergeTag?.suggestionEmpty ?? "No matching merge tags", _ = k(), y = I(g, _), b = !1;
|
|
71
|
+
n() && o(() => {
|
|
72
|
+
b = !0, y.close();
|
|
73
|
+
});
|
|
74
|
+
function x(e) {
|
|
75
|
+
let t = e.value ?? "";
|
|
76
|
+
return R(t, e.selectionStart ?? t.length, d, f);
|
|
77
|
+
}
|
|
78
|
+
function S(e, t, n) {
|
|
79
|
+
let o = e.value ?? i(), s = o.slice(t.caret), c = f && s.startsWith(f) ? f.length : 0, l = o.slice(0, t.triggerStart), d = o.slice(t.caret + c), p = l + n.value + d, h = l.length + n.value.length;
|
|
80
|
+
u?.(), a(p), y.close(), m(() => {
|
|
81
|
+
if (b) return;
|
|
82
|
+
let e = r.value;
|
|
83
|
+
e?.focus(), e?.setSelectionRange(h, h);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function w() {
|
|
87
|
+
if (!p || b) return;
|
|
88
|
+
let e = r.value;
|
|
89
|
+
if (!e) {
|
|
90
|
+
y.close();
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
let t = x(e);
|
|
94
|
+
if (!t) {
|
|
95
|
+
y.close();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
let n = L(s, t.query), i = () => V(e, t.triggerStart), a = (n) => S(e, t, n);
|
|
99
|
+
y.isOpen() ? y.update({
|
|
100
|
+
items: n,
|
|
101
|
+
getRect: i,
|
|
102
|
+
onCommand: a
|
|
103
|
+
}) : y.open({
|
|
104
|
+
items: n,
|
|
105
|
+
getRect: i,
|
|
106
|
+
anchorEl: e,
|
|
107
|
+
onCommand: a
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function E(e) {
|
|
111
|
+
return y.isOpen() ? e.key === "Escape" ? (y.close(), !0) : H.has(e.key) ? (y.close(), !1) : y.handleKeyDown(e) : !1;
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
available: p,
|
|
115
|
+
refresh: w,
|
|
116
|
+
handleKeydown: E,
|
|
117
|
+
close: () => y.close(),
|
|
118
|
+
isOpen: () => y.isOpen()
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region src/composables/useMergeTagField.ts
|
|
123
|
+
function W(e) {
|
|
124
|
+
let { modelValue: t, emit: r, elementRef: a } = e, { mergeTags: s, canRequestMergeTag: c, isRequesting: l, isMergeTagValue: u, getMergeTagLabel: f, requestMergeTag: p, syntax: h, autocomplete: g } = P(), { canInsertLogicTag: _, isRequesting: v, requestLogicTag: y } = E(), b = i(!1), x = !1, C = !1;
|
|
125
|
+
n() && o(() => {
|
|
126
|
+
C = !0;
|
|
127
|
+
});
|
|
128
|
+
let T = U({
|
|
129
|
+
elementRef: a,
|
|
130
|
+
modelValue: t,
|
|
131
|
+
emit: r,
|
|
132
|
+
mergeTags: s,
|
|
133
|
+
syntax: h,
|
|
134
|
+
enabled: g,
|
|
135
|
+
onInsert: () => {
|
|
136
|
+
b.value = !0;
|
|
137
|
+
}
|
|
138
|
+
}), D = d(() => {
|
|
139
|
+
let e = t();
|
|
140
|
+
if (!e) return [];
|
|
141
|
+
let n = [], r = `(${h.value.source}|${h.logic.source})`, i = new RegExp(r, "g"), a = 0, o;
|
|
142
|
+
for (; (o = i.exec(e)) !== null;) {
|
|
143
|
+
o.index > a && n.push({
|
|
144
|
+
type: "text",
|
|
145
|
+
value: e.slice(a, o.index)
|
|
146
|
+
});
|
|
147
|
+
let t = o[0];
|
|
148
|
+
u(t) ? n.push({
|
|
149
|
+
type: "mergeTag",
|
|
150
|
+
value: t,
|
|
151
|
+
label: f(t)
|
|
152
|
+
}) : w(t, h) ? n.push({
|
|
153
|
+
type: "logicMergeTag",
|
|
154
|
+
value: t,
|
|
155
|
+
keyword: S(t, h)
|
|
156
|
+
}) : n.push({
|
|
157
|
+
type: "text",
|
|
158
|
+
value: t
|
|
159
|
+
}), a = o.index + t.length;
|
|
160
|
+
}
|
|
161
|
+
return a < e.length && n.push({
|
|
162
|
+
type: "text",
|
|
163
|
+
value: e.slice(a)
|
|
164
|
+
}), n;
|
|
165
|
+
}), O = d(() => D.value.some((e) => e.type === "mergeTag" || e.type === "logicMergeTag"));
|
|
166
|
+
function k() {
|
|
167
|
+
b.value = !0, m(() => {
|
|
168
|
+
a.value?.focus();
|
|
169
|
+
let e = t()?.length || 0;
|
|
170
|
+
a.value?.setSelectionRange(e, e);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
function A() {
|
|
174
|
+
x || (b.value = !1);
|
|
175
|
+
}
|
|
176
|
+
function j(e) {
|
|
177
|
+
r(e.target.value), T.refresh();
|
|
178
|
+
}
|
|
179
|
+
function M(e) {
|
|
180
|
+
return T.handleKeydown(e) ? (e.preventDefault(), !0) : (e.key === "Escape" && A(), !1);
|
|
181
|
+
}
|
|
182
|
+
function N() {
|
|
183
|
+
T.refresh();
|
|
184
|
+
}
|
|
185
|
+
function F() {
|
|
186
|
+
T.close(), A();
|
|
187
|
+
}
|
|
188
|
+
function I() {
|
|
189
|
+
r("");
|
|
190
|
+
}
|
|
191
|
+
async function L() {
|
|
192
|
+
let e = b.value && a.value ? a.value.selectionStart ?? t().length : t().length;
|
|
193
|
+
x = !0;
|
|
194
|
+
let n;
|
|
195
|
+
try {
|
|
196
|
+
n = await p();
|
|
197
|
+
} finally {
|
|
198
|
+
x = !1;
|
|
199
|
+
}
|
|
200
|
+
if (!C && n) {
|
|
201
|
+
let i = t().slice(0, e), o = t().slice(e), s = i + n.value + o;
|
|
202
|
+
r(s), b.value = !0, m(() => {
|
|
203
|
+
if (C) return;
|
|
204
|
+
let t = e + n.value.length;
|
|
205
|
+
a.value?.focus(), a.value?.setSelectionRange(t, t);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async function R() {
|
|
210
|
+
let e = a.value, n = e?.selectionStart ?? t().length, i = e?.selectionEnd ?? n;
|
|
211
|
+
x = !0;
|
|
212
|
+
let o;
|
|
213
|
+
try {
|
|
214
|
+
o = await y();
|
|
215
|
+
} finally {
|
|
216
|
+
x = !1;
|
|
217
|
+
}
|
|
218
|
+
if (C || !o) return;
|
|
219
|
+
let s = t(), c, l;
|
|
220
|
+
"before" in o ? (c = s.slice(0, n) + o.before + s.slice(n, i) + o.after + s.slice(i), l = n + o.before.length + (i - n)) : (c = s.slice(0, n) + o.value + s.slice(n), l = n + o.value.length), r(c), b.value = !0, m(() => {
|
|
221
|
+
C || (a.value?.focus(), a.value?.setSelectionRange(l, l));
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
segments: D,
|
|
226
|
+
hasMergeTags: O,
|
|
227
|
+
canRequestMergeTag: c,
|
|
228
|
+
isRequestingMergeTag: l,
|
|
229
|
+
canInsertLogicTag: _,
|
|
230
|
+
isRequestingLogicTag: v,
|
|
231
|
+
isEditing: b,
|
|
232
|
+
startEditing: k,
|
|
233
|
+
stopEditing: A,
|
|
234
|
+
handleInput: j,
|
|
235
|
+
handleKeydown: M,
|
|
236
|
+
handleClick: N,
|
|
237
|
+
handleBlur: F,
|
|
238
|
+
clearValue: I,
|
|
239
|
+
insertMergeTag: L,
|
|
240
|
+
insertLogicTag: R,
|
|
241
|
+
autocompleteAvailable: T.available
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
//#endregion
|
|
245
|
+
//#region src/components/MergeTagSegments.vue?vue&type=script&setup=true&lang.ts
|
|
246
|
+
var G = ["aria-label", "onKeydown"], K = ["data-tooltip"], q = ["data-tooltip"], J = {
|
|
247
|
+
key: 2,
|
|
248
|
+
class: "tpl:text-sm tpl:text-[var(--tpl-text)]"
|
|
249
|
+
}, Y = ["aria-label", "title"], X = /* @__PURE__ */ a({
|
|
250
|
+
__name: "MergeTagSegments",
|
|
251
|
+
props: {
|
|
252
|
+
segments: {},
|
|
253
|
+
displayClass: {},
|
|
254
|
+
pulse: { type: Boolean }
|
|
255
|
+
},
|
|
256
|
+
emits: ["edit", "clear"],
|
|
257
|
+
setup(e, { emit: n }) {
|
|
258
|
+
let i = n, { t: a } = O();
|
|
259
|
+
function o() {
|
|
260
|
+
i("edit");
|
|
261
|
+
}
|
|
262
|
+
return (n, d) => (r(), f("div", {
|
|
263
|
+
role: "button",
|
|
264
|
+
tabindex: "0",
|
|
265
|
+
"aria-label": s(a).mergeTag.clickToEdit,
|
|
266
|
+
class: u([e.displayClass, { "tpl-pulse-fill": e.pulse }]),
|
|
267
|
+
onClick: o,
|
|
268
|
+
onKeydown: [l(o, ["enter"]), l(h(o, ["prevent"]), ["space"])]
|
|
269
|
+
}, [(r(!0), f(y, null, t(e.segments, (e, t) => (r(), f(y, { key: `${e.type}-${t}-${e.value}` }, [e.type === "mergeTag" ? (r(), f("span", {
|
|
270
|
+
key: 0,
|
|
271
|
+
class: "tpl-tooltip tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.9em] tpl:font-medium",
|
|
272
|
+
"data-tooltip": e.value,
|
|
273
|
+
style: {
|
|
274
|
+
"background-color": "color-mix(\n in srgb,\n var(--tpl-primary) 20%,\n transparent\n )",
|
|
275
|
+
color: "var(--tpl-primary)"
|
|
276
|
+
}
|
|
277
|
+
}, b(e.label), 9, K)) : e.type === "logicMergeTag" ? (r(), f("span", {
|
|
278
|
+
key: 1,
|
|
279
|
+
class: "tpl-tooltip tpl:inline-flex tpl:items-center tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.8em] tpl:font-bold tpl:tracking-wide tpl:uppercase",
|
|
280
|
+
"data-tooltip": e.value,
|
|
281
|
+
style: {
|
|
282
|
+
"background-color": "transparent",
|
|
283
|
+
border: "1.5px solid\n color-mix(in srgb, var(--tpl-primary) 50%, transparent)",
|
|
284
|
+
color: "var(--tpl-primary)"
|
|
285
|
+
}
|
|
286
|
+
}, b(e.keyword), 9, q)) : (r(), f("span", J, b(e.value), 1))], 64))), 128)), _("button", {
|
|
287
|
+
type: "button",
|
|
288
|
+
class: "tpl:ml-auto tpl:flex tpl:size-6 tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:text-[var(--tpl-text-dim)] tpl:opacity-60 tpl:transition-all tpl:hover:text-[var(--tpl-danger)] tpl:hover:opacity-100",
|
|
289
|
+
"aria-label": s(a).mergeTag.remove,
|
|
290
|
+
title: s(a).mergeTag.remove,
|
|
291
|
+
onClick: d[0] ||= h((e) => i("clear"), ["stop"])
|
|
292
|
+
}, [c(s(M), {
|
|
293
|
+
size: 12,
|
|
294
|
+
"stroke-width": 2.5
|
|
295
|
+
})], 8, Y)], 42, G));
|
|
296
|
+
}
|
|
297
|
+
}), Z = [
|
|
298
|
+
"aria-label",
|
|
299
|
+
"title",
|
|
300
|
+
"disabled"
|
|
301
|
+
], ee = "tpl:flex tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-1 tpl:text-xs tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-[120ms] tpl:hover:bg-[var(--tpl-primary-light)] tpl:hover:text-[var(--tpl-primary)] tpl:hover:border-[var(--tpl-primary)]", Q = /* @__PURE__ */ a({
|
|
302
|
+
__name: "MergeTagInsertButton",
|
|
303
|
+
props: { disabled: { type: Boolean } },
|
|
304
|
+
emits: ["insert"],
|
|
305
|
+
setup(e) {
|
|
306
|
+
let { t } = O();
|
|
307
|
+
return (n, i) => (r(), f("button", {
|
|
308
|
+
type: "button",
|
|
309
|
+
class: u([ee, "tpl:mt-1.5"]),
|
|
310
|
+
"aria-label": s(t).mergeTag.insert,
|
|
311
|
+
title: s(t).mergeTag.insert,
|
|
312
|
+
disabled: e.disabled,
|
|
313
|
+
onMousedown: i[0] ||= h(() => {}, ["prevent"]),
|
|
314
|
+
onClick: i[1] ||= (e) => n.$emit("insert")
|
|
315
|
+
}, [c(s(A), {
|
|
316
|
+
size: 12,
|
|
317
|
+
"stroke-width": 2
|
|
318
|
+
}), x(" " + b(s(t).mergeTag.insertShort), 1)], 42, Z));
|
|
319
|
+
}
|
|
320
|
+
}), te = [
|
|
321
|
+
"aria-label",
|
|
322
|
+
"title",
|
|
323
|
+
"disabled"
|
|
324
|
+
], ne = "tpl:flex tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-1 tpl:text-xs tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-[120ms] tpl:hover:bg-[var(--tpl-primary-light)] tpl:hover:text-[var(--tpl-primary)] tpl:hover:border-[var(--tpl-primary)]", $ = /* @__PURE__ */ a({
|
|
325
|
+
__name: "LogicTagInsertButton",
|
|
326
|
+
props: { disabled: { type: Boolean } },
|
|
327
|
+
emits: ["insert"],
|
|
328
|
+
setup(e) {
|
|
329
|
+
let { t } = O();
|
|
330
|
+
return (n, i) => (r(), f("button", {
|
|
331
|
+
type: "button",
|
|
332
|
+
class: u([ne, "tpl:mt-1.5"]),
|
|
333
|
+
"aria-label": s(t).logicTag.insert,
|
|
334
|
+
title: s(t).logicTag.insert,
|
|
335
|
+
disabled: e.disabled,
|
|
336
|
+
"data-testid": "field-insert-logic-button",
|
|
337
|
+
onMousedown: i[0] ||= h(() => {}, ["prevent"]),
|
|
338
|
+
onClick: i[1] ||= (e) => n.$emit("insert")
|
|
339
|
+
}, [c(s(j), {
|
|
340
|
+
size: 12,
|
|
341
|
+
"stroke-width": 2
|
|
342
|
+
}), x(" " + b(s(t).logicTag.insertShort), 1)], 42, te));
|
|
343
|
+
}
|
|
344
|
+
}), re = { key: 0 }, ie = {
|
|
345
|
+
key: 0,
|
|
346
|
+
class: "tpl:flex tpl:flex-wrap tpl:gap-1.5"
|
|
347
|
+
}, ae = { key: 1 }, oe = [
|
|
348
|
+
"type",
|
|
349
|
+
"value",
|
|
350
|
+
"placeholder"
|
|
351
|
+
], se = {
|
|
352
|
+
key: 0,
|
|
353
|
+
class: "tpl:flex tpl:flex-wrap tpl:gap-1.5"
|
|
354
|
+
}, ce = "tpl:flex tpl:w-full tpl:min-h-10 tpl:cursor-pointer tpl:items-center tpl:flex-wrap tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:shadow-[var(--tpl-shadow-sm)] tpl:bg-[var(--tpl-bg)] tpl:border-[var(--tpl-border)] tpl:px-3.5 tpl:py-1.5 tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]", le = /*#__PURE__*/ N(/* @__PURE__ */ a({
|
|
355
|
+
__name: "MergeTagInput",
|
|
356
|
+
props: {
|
|
357
|
+
modelValue: {},
|
|
358
|
+
type: { default: "text" },
|
|
359
|
+
placeholder: { default: "" },
|
|
360
|
+
pulse: {
|
|
361
|
+
type: Boolean,
|
|
362
|
+
default: !1
|
|
363
|
+
}
|
|
364
|
+
},
|
|
365
|
+
emits: ["update:modelValue", "keydown"],
|
|
366
|
+
setup(e, { emit: t }) {
|
|
367
|
+
let n = e, a = t, o = i(null), { segments: l, hasMergeTags: d, canRequestMergeTag: m, isRequestingMergeTag: h, canInsertLogicTag: v, isRequestingLogicTag: y, isEditing: b, startEditing: x, handleInput: S, handleKeydown: C, handleClick: w, handleBlur: T, clearValue: E, insertMergeTag: D, insertLogicTag: O } = W({
|
|
368
|
+
modelValue: () => n.modelValue,
|
|
369
|
+
emit: (e) => a("update:modelValue", e),
|
|
370
|
+
elementRef: o
|
|
371
|
+
});
|
|
372
|
+
function k(e) {
|
|
373
|
+
C(e) || a("keydown", e);
|
|
374
|
+
}
|
|
375
|
+
return (t, n) => s(d) && !s(b) ? (r(), f("div", re, [c(X, {
|
|
376
|
+
segments: s(l),
|
|
377
|
+
"display-class": ce,
|
|
378
|
+
pulse: e.pulse,
|
|
379
|
+
onEdit: s(x),
|
|
380
|
+
onClear: s(E)
|
|
381
|
+
}, null, 8, [
|
|
382
|
+
"segments",
|
|
383
|
+
"pulse",
|
|
384
|
+
"onEdit",
|
|
385
|
+
"onClear"
|
|
386
|
+
]), s(m) || s(v) ? (r(), f("div", ie, [s(m) ? (r(), g(Q, {
|
|
387
|
+
key: 0,
|
|
388
|
+
disabled: s(h),
|
|
389
|
+
onInsert: s(D)
|
|
390
|
+
}, null, 8, ["disabled", "onInsert"])) : p("", !0), s(v) ? (r(), g($, {
|
|
391
|
+
key: 1,
|
|
392
|
+
disabled: s(y),
|
|
393
|
+
onInsert: s(O)
|
|
394
|
+
}, null, 8, ["disabled", "onInsert"])) : p("", !0)])) : p("", !0)])) : (r(), f("div", ae, [_("input", {
|
|
395
|
+
ref_key: "inputRef",
|
|
396
|
+
ref: o,
|
|
397
|
+
type: e.type,
|
|
398
|
+
class: u([s(F), { "tpl-pulse-fill": e.pulse }]),
|
|
399
|
+
value: e.modelValue,
|
|
400
|
+
placeholder: e.placeholder,
|
|
401
|
+
onInput: n[0] ||= (...e) => s(S) && s(S)(...e),
|
|
402
|
+
onKeydown: k,
|
|
403
|
+
onClick: n[1] ||= (...e) => s(w) && s(w)(...e),
|
|
404
|
+
onBlur: n[2] ||= (...e) => s(T) && s(T)(...e)
|
|
405
|
+
}, null, 42, oe), s(m) || s(v) ? (r(), f("div", se, [s(m) ? (r(), g(Q, {
|
|
406
|
+
key: 0,
|
|
407
|
+
disabled: s(h),
|
|
408
|
+
onInsert: s(D)
|
|
409
|
+
}, null, 8, ["disabled", "onInsert"])) : p("", !0), s(v) ? (r(), g($, {
|
|
410
|
+
key: 1,
|
|
411
|
+
disabled: s(y),
|
|
412
|
+
onInsert: s(O)
|
|
413
|
+
}, null, 8, ["disabled", "onInsert"])) : p("", !0)])) : p("", !0)]));
|
|
414
|
+
}
|
|
415
|
+
}), [["__scopeId", "data-v-31b31866"]]);
|
|
416
|
+
//#endregion
|
|
417
|
+
export { W as a, X as i, $ as n, Q as r, le as t };
|
|
418
|
+
|
|
419
|
+
//# sourceMappingURL=MergeTagInput-1vcI9uQH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MergeTagInput-1vcI9uQH.js","names":["$emit","$emit"],"sources":["../../../src/utils/mergeTagTrigger.ts","../../../src/utils/textFieldCaret.ts","../../../src/composables/useMergeTagAutocomplete.ts","../../../src/composables/useMergeTagField.ts","../../../src/components/MergeTagSegments.vue","../../../src/components/MergeTagSegments.vue","../../../src/components/MergeTagInsertButton.vue","../../../src/components/MergeTagInsertButton.vue","../../../src/components/LogicTagInsertButton.vue","../../../src/components/LogicTagInsertButton.vue","../../../src/components/MergeTagInput.vue","../../../src/components/MergeTagInput.vue"],"sourcesContent":["/**\n * Result of a merge-tag trigger scan — the caret sits inside an open tag.\n */\nexport interface OpenMergeTagTrigger {\n /** Index in the field value where the trigger opener starts. */\n triggerStart: number;\n /** Caret index the scan ran at (also the end of `query`). */\n caret: number;\n /** Text typed between the opener and the caret — the autocomplete filter. */\n query: string;\n}\n\n/**\n * Detects whether the caret sits inside an *open* merge-tag trigger: the user\n * has typed the syntax opener (e.g. `{{`) and is mid-typing the tag name with\n * no closing delimiter yet. Returns the opener position + query so the caller\n * can drive the autocomplete popup, or `null` when the caret is not inside an\n * open trigger.\n *\n * Mirrors `@tiptap/suggestion`'s behavior with `allowSpaces: false`: the run\n * between the opener and the caret ends at the first whitespace, and a closing\n * delimiter inside that run means the tag is already complete (`{{first_name}}`)\n * so the popup must not reappear over it.\n */\nexport function findOpenMergeTagTrigger(\n value: string,\n caret: number,\n triggerChar: string,\n closingChar: string | null,\n): OpenMergeTagTrigger | null {\n if (!triggerChar) return null;\n\n const before = value.slice(0, caret);\n const triggerStart = before.lastIndexOf(triggerChar);\n if (triggerStart === -1) return null;\n\n const query = before.slice(triggerStart + triggerChar.length);\n\n // Whitespace ends the query (allowSpaces: false parity).\n if (/\\s/.test(query)) return null;\n\n // A closing delimiter between the opener and the caret means the tag is\n // already complete — don't reopen the popup over a finished tag.\n if (closingChar && query.includes(closingChar)) return null;\n\n return { triggerStart, caret, query };\n}\n","/**\n * Viewport-space caret geometry for `<input>` / `<textarea>` elements.\n *\n * Native text controls expose no caret coordinates (unlike ProseMirror, which\n * hands the merge-tag popup a `clientRect()`). The standard workaround is a\n * hidden \"mirror\" element that clones the field's text metrics; the caret's\n * pixel position is read from a marker span placed at the caret offset. This\n * lets the input/textarea autocomplete anchor its popup at the caret exactly\n * like the rich-text editor does.\n *\n * Adapted from component/textarea-caret-position (MIT). Returns a viewport\n * `DOMRect` (same coordinate space as `Range.getBoundingClientRect`) so the\n * shared merge-tag popup controller positions it identically to the TipTap path.\n */\n\n// Computed-style properties copied onto the mirror so its text wraps and\n// measures exactly like the source field.\nconst MIRRORED_PROPERTIES = [\n \"boxSizing\",\n \"width\",\n \"borderTopWidth\",\n \"borderRightWidth\",\n \"borderBottomWidth\",\n \"borderLeftWidth\",\n \"borderStyle\",\n \"paddingTop\",\n \"paddingRight\",\n \"paddingBottom\",\n \"paddingLeft\",\n \"fontStyle\",\n \"fontVariant\",\n \"fontWeight\",\n \"fontStretch\",\n \"fontSize\",\n \"lineHeight\",\n \"fontFamily\",\n \"textAlign\",\n \"textTransform\",\n \"textIndent\",\n \"letterSpacing\",\n \"wordSpacing\",\n \"tabSize\",\n \"whiteSpace\",\n \"wordWrap\",\n \"wordBreak\",\n] as const;\n\nfunction toRect(left: number, top: number, bottom: number): DOMRect {\n const height = bottom - top;\n return {\n left,\n top,\n bottom,\n right: left,\n width: 0,\n height,\n x: left,\n y: top,\n toJSON: () => ({}),\n } as DOMRect;\n}\n\n/**\n * Returns the viewport rect of the caret at `position` in a text field, or\n * `null` when the field isn't laid out (detached / no view). The rect is a\n * zero-width vertical strip at the caret, one line tall — matching the shape\n * `Range.getBoundingClientRect()` produces for a collapsed selection.\n */\nexport function getCaretRect(\n el: HTMLInputElement | HTMLTextAreaElement,\n position: number,\n): DOMRect | null {\n const doc = el.ownerDocument;\n const win = doc.defaultView;\n if (!win) return null;\n\n const isInput = el.tagName === \"INPUT\";\n const style = win.getComputedStyle(el);\n\n const mirror = doc.createElement(\"div\");\n mirror.setAttribute(\"aria-hidden\", \"true\");\n const s = mirror.style;\n s.position = \"absolute\";\n s.visibility = \"hidden\";\n s.top = \"0\";\n s.left = \"0\";\n s.overflow = \"hidden\";\n s.whiteSpace = isInput ? \"pre\" : \"pre-wrap\";\n s.wordWrap = isInput ? \"normal\" : \"break-word\";\n const target = s as unknown as Record<string, string>;\n const source = style as unknown as Record<string, string>;\n for (const prop of MIRRORED_PROPERTIES) {\n target[prop] = source[prop];\n }\n // A single-line input never wraps; let the mirror grow with the text so the\n // marker's horizontal offset tracks the caret.\n if (isInput) {\n s.width = \"auto\";\n s.whiteSpace = \"pre\";\n }\n\n const value = el.value;\n let head = value.slice(0, position);\n if (isInput) {\n // <input> collapses whitespace runs to a single rendered space; replace\n // with non-breaking spaces so the mirror's width matches the field.\n head = head.replace(/\\s/g, \" \");\n }\n mirror.textContent = head;\n\n const marker = doc.createElement(\"span\");\n // Remaining text (or a placeholder) gives the marker the caret line's height.\n marker.textContent = value.slice(position) || \".\";\n mirror.appendChild(marker);\n\n doc.body.appendChild(mirror);\n const markerTop = marker.offsetTop;\n const markerLeft = marker.offsetLeft;\n doc.body.removeChild(mirror);\n\n const elRect = el.getBoundingClientRect();\n const borderTop = parseFloat(style.borderTopWidth) || 0;\n const borderLeft = parseFloat(style.borderLeftWidth) || 0;\n const lineHeight =\n parseFloat(style.lineHeight) || parseFloat(style.fontSize) || 0;\n\n const left = elRect.left + markerLeft + borderLeft - el.scrollLeft;\n\n // A single-line input vertically centers its text, which the block-flow\n // mirror can't replicate — anchor vertically to the field itself so the\n // popup sits directly under the input. Textareas use the measured caret line.\n if (isInput) {\n return toRect(left, elRect.top, elRect.bottom);\n }\n\n const top = elRect.top + markerTop + borderTop - el.scrollTop;\n return toRect(left, top, top + lineHeight);\n}\n","import {\n getSyntaxClosingChar,\n getSyntaxTriggerChar,\n type MergeTag,\n type SyntaxPreset,\n} from \"@templatical/types\";\nimport {\n getCurrentScope,\n inject,\n isRef,\n nextTick,\n onScopeDispose,\n type Ref,\n} from \"vue\";\nimport type { Translations } from \"../i18n\";\nimport { TRANSLATIONS_KEY } from \"../keys\";\nimport {\n createMergeTagPopup,\n filterMergeTags,\n} from \"../extensions/MergeTagSuggestion\";\nimport {\n findOpenMergeTagTrigger,\n type OpenMergeTagTrigger,\n} from \"../utils/mergeTagTrigger\";\nimport { getCaretRect } from \"../utils/textFieldCaret\";\nimport { usePopoverRoot } from \"./usePopoverRoot\";\n\nexport interface UseMergeTagAutocompleteOptions {\n /** The `<input>` / `<textarea>` the popup anchors to. */\n elementRef: Ref<HTMLInputElement | HTMLTextAreaElement | null>;\n /** Reads the current field value. */\n modelValue: () => string;\n /** Emits a new field value. */\n emit: (value: string) => void;\n /** Available merge tags (the autocomplete filter source). */\n mergeTags: MergeTag[];\n /** Resolved merge-tag syntax. */\n syntax: SyntaxPreset;\n /** Consumer's `mergeTags.autocomplete` flag. */\n enabled: boolean;\n /**\n * Invoked right before a picked tag is emitted, so the field can keep edit\n * mode and not flip to the chip display while the caret is still active.\n */\n onInsert?: () => void;\n}\n\nexport interface UseMergeTagAutocompleteReturn {\n /**\n * Whether autocomplete can ever fire. Mirrors the rich-text gate exactly:\n * enabled + a built-in trigger syntax + at least one tag to filter.\n */\n available: boolean;\n /** Recompute the popup after the value or caret changed. */\n refresh: () => void;\n /**\n * Field `keydown` handler. Returns `true` when the popup consumed the key\n * (the caller must then `preventDefault`); `false` to let the field handle it.\n */\n handleKeydown: (event: KeyboardEvent) => boolean;\n /** Close the popup (e.g. on blur). Idempotent. */\n close: () => void;\n /** Whether the popup is currently open. */\n isOpen: () => boolean;\n}\n\n// Horizontal caret moves take the caret out of the query run — dismiss the\n// popup (like navigating away in the rich-text editor) and let the caret move.\nconst CARET_MOVE_KEYS = new Set([\"ArrowLeft\", \"ArrowRight\", \"Home\", \"End\"]);\n\n/**\n * Type-ahead merge-tag autocomplete for a native `<input>` / `<textarea>`.\n *\n * Drives the SAME popup controller as the rich-text (`@tiptap/suggestion`)\n * path — see `createMergeTagPopup` — so the popup's rendering, positioning,\n * keyboard behavior, and ARIA are identical across both surfaces. The only\n * field-specific work is here: detecting the open trigger in the raw string\n * (`findOpenMergeTagTrigger`), measuring the caret rect without ProseMirror\n * (`getCaretRect`), and replacing the typed fragment with the picked tag.\n */\nexport function useMergeTagAutocomplete(\n options: UseMergeTagAutocompleteOptions,\n): UseMergeTagAutocompleteReturn {\n const { elementRef, modelValue, emit, mergeTags, syntax, enabled, onInsert } =\n options;\n\n const triggerChar = getSyntaxTriggerChar(syntax);\n const closingChar = getSyntaxClosingChar(syntax);\n const available = enabled && triggerChar !== null && mergeTags.length > 0;\n\n // Resolve the empty-state label the same way the rich-text path does —\n // inject with a null default so headless callers (no translations provider)\n // don't throw, falling back to the English string.\n const injectedTranslations = inject(TRANSLATIONS_KEY, null) as\n Translations | Ref<Translations> | null;\n const resolvedTranslations = isRef(injectedTranslations)\n ? injectedTranslations.value\n : injectedTranslations;\n const emptyText =\n resolvedTranslations?.mergeTag?.suggestionEmpty ?? \"No matching merge tags\";\n\n const popoverRoot = usePopoverRoot();\n const popup = createMergeTagPopup(emptyText, popoverRoot);\n\n let disposed = false;\n if (getCurrentScope()) {\n onScopeDispose(() => {\n disposed = true;\n popup.close();\n });\n }\n\n function currentMatch(el: HTMLInputElement | HTMLTextAreaElement) {\n const value = el.value ?? \"\";\n const caret = el.selectionStart ?? value.length;\n return findOpenMergeTagTrigger(\n value,\n caret,\n triggerChar as string,\n closingChar,\n );\n }\n\n function applySelection(\n el: HTMLInputElement | HTMLTextAreaElement,\n match: OpenMergeTagTrigger,\n tag: MergeTag,\n ): void {\n const value = el.value ?? modelValue();\n // Absorb a matching closer sitting immediately after the caret so\n // `{{fir}}` → pick → `{{first_name}}`, never `{{first_name}}}}`.\n const afterCaret = value.slice(match.caret);\n const trailing =\n closingChar && afterCaret.startsWith(closingChar)\n ? closingChar.length\n : 0;\n const before = value.slice(0, match.triggerStart);\n const after = value.slice(match.caret + trailing);\n const newValue = before + tag.value + after;\n const newPos = before.length + tag.value.length;\n\n // Keep the field in edit mode BEFORE emitting: once the value gains a tag,\n // `hasMergeTags` flips true, and without this the field would swap to the\n // chip display and unmount the input mid-insert.\n onInsert?.();\n emit(newValue);\n popup.close();\n\n nextTick(() => {\n if (disposed) return;\n const node = elementRef.value;\n node?.focus();\n node?.setSelectionRange(newPos, newPos);\n });\n }\n\n function refresh(): void {\n if (!available || disposed) return;\n const el = elementRef.value;\n if (!el) {\n popup.close();\n return;\n }\n const match = currentMatch(el);\n if (!match) {\n popup.close();\n return;\n }\n\n const items = filterMergeTags(mergeTags, match.query);\n const getRect = () => getCaretRect(el, match.triggerStart);\n const onCommand = (tag: MergeTag) => applySelection(el, match, tag);\n\n if (popup.isOpen()) {\n popup.update({ items, getRect, onCommand });\n } else {\n popup.open({ items, getRect, anchorEl: el, onCommand });\n }\n }\n\n function handleKeydown(event: KeyboardEvent): boolean {\n if (!popup.isOpen()) return false;\n if (event.key === \"Escape\") {\n popup.close();\n return true;\n }\n if (CARET_MOVE_KEYS.has(event.key)) {\n popup.close();\n return false;\n }\n return popup.handleKeyDown(event);\n }\n\n return {\n available,\n refresh,\n handleKeydown,\n close: () => popup.close(),\n isOpen: () => popup.isOpen(),\n };\n}\n","import {\n getLogicMergeTagKeyword,\n isLogicMergeTagValue,\n} from \"@templatical/types\";\nimport {\n computed,\n getCurrentScope,\n nextTick,\n onScopeDispose,\n ref,\n type ComputedRef,\n type Ref,\n} from \"vue\";\nimport { useMergeTag } from \"./useMergeTag\";\nimport { useLogicTag } from \"./useLogicTag\";\nimport { useMergeTagAutocomplete } from \"./useMergeTagAutocomplete\";\n\nexport type MergeTagSegment =\n | { type: \"text\"; value: string }\n | { type: \"mergeTag\"; value: string; label: string }\n | { type: \"logicMergeTag\"; value: string; keyword: string };\n\nexport interface UseMergeTagFieldOptions {\n modelValue: () => string;\n emit: (value: string) => void;\n elementRef: Ref<HTMLInputElement | HTMLTextAreaElement | null>;\n}\n\nexport interface UseMergeTagFieldReturn {\n segments: ComputedRef<MergeTagSegment[]>;\n hasMergeTags: ComputedRef<boolean>;\n canRequestMergeTag: boolean;\n isRequestingMergeTag: Ref<boolean>;\n canInsertLogicTag: boolean;\n isRequestingLogicTag: Ref<boolean>;\n isEditing: Ref<boolean>;\n startEditing: () => void;\n stopEditing: () => void;\n handleInput: (event: Event) => void;\n /**\n * Returns whether the autocomplete popup consumed the event. A host with\n * keys of its own (the link dialog submits on Enter) acts only on `false`.\n */\n handleKeydown: (event: KeyboardEvent) => boolean;\n handleClick: () => void;\n handleBlur: () => void;\n clearValue: () => void;\n insertMergeTag: () => Promise<void>;\n insertLogicTag: () => Promise<void>;\n /** Whether type-ahead autocomplete is active for this field. */\n autocompleteAvailable: boolean;\n}\n\nexport function useMergeTagField(\n options: UseMergeTagFieldOptions,\n): UseMergeTagFieldReturn {\n const { modelValue, emit, elementRef } = options;\n\n const {\n mergeTags,\n canRequestMergeTag,\n isRequesting: isRequestingMergeTag,\n isMergeTagValue,\n getMergeTagLabel,\n requestMergeTag,\n syntax,\n autocomplete,\n } = useMergeTag();\n\n const {\n canInsertLogicTag,\n isRequesting: isRequestingLogicTag,\n requestLogicTag,\n } = useLogicTag();\n\n const isEditing = ref(false);\n let insertingMergeTag = false;\n let disposed = false;\n if (getCurrentScope()) {\n onScopeDispose(() => {\n disposed = true;\n });\n }\n\n // Type-ahead autocomplete for the raw <input>/<textarea>, sharing the exact\n // popup used by the rich-text editor. `onInsert` keeps the field in edit mode\n // so the freshly inserted tag stays visible instead of flipping to chips.\n const typeahead = useMergeTagAutocomplete({\n elementRef,\n modelValue,\n emit,\n mergeTags,\n syntax,\n enabled: autocomplete,\n onInsert: () => {\n isEditing.value = true;\n },\n });\n\n const segments = computed((): MergeTagSegment[] => {\n const val = modelValue();\n if (!val) return [];\n\n const result: MergeTagSegment[] = [];\n const combinedSource = `(${syntax.value.source}|${syntax.logic.source})`;\n const regex = new RegExp(combinedSource, \"g\");\n let lastIndex = 0;\n let match;\n\n while ((match = regex.exec(val)) !== null) {\n if (match.index > lastIndex) {\n result.push({\n type: \"text\",\n value: val.slice(lastIndex, match.index),\n });\n }\n\n const matched = match[0];\n if (isMergeTagValue(matched)) {\n result.push({\n type: \"mergeTag\",\n value: matched,\n label: getMergeTagLabel(matched),\n });\n } else if (isLogicMergeTagValue(matched, syntax)) {\n result.push({\n type: \"logicMergeTag\",\n value: matched,\n keyword: getLogicMergeTagKeyword(matched, syntax),\n });\n } else {\n result.push({ type: \"text\", value: matched });\n }\n\n lastIndex = match.index + matched.length;\n }\n\n if (lastIndex < val.length) {\n result.push({ type: \"text\", value: val.slice(lastIndex) });\n }\n\n return result;\n });\n\n const hasMergeTags = computed(() =>\n segments.value.some(\n (s) => s.type === \"mergeTag\" || s.type === \"logicMergeTag\",\n ),\n );\n\n function startEditing(): void {\n isEditing.value = true;\n nextTick(() => {\n elementRef.value?.focus();\n const len = modelValue()?.length || 0;\n elementRef.value?.setSelectionRange(len, len);\n });\n }\n\n function stopEditing(): void {\n if (insertingMergeTag) return;\n isEditing.value = false;\n }\n\n function handleInput(event: Event): void {\n emit((event.target as HTMLInputElement | HTMLTextAreaElement).value);\n typeahead.refresh();\n }\n\n /**\n * Field keydown: let the autocomplete popup consume navigation/selection\n * keys first (Arrow/Enter/Tab/Escape). If it doesn't, Escape falls through\n * to leaving edit mode.\n *\n * Escape is reported as NOT consumed even though it acts here: a host\n * dialog should close on the same press rather than swallow the first one.\n */\n function handleKeydown(event: KeyboardEvent): boolean {\n if (typeahead.handleKeydown(event)) {\n event.preventDefault();\n return true;\n }\n if (event.key === \"Escape\") {\n stopEditing();\n }\n return false;\n }\n\n /** A click may move the caret into or out of a trigger — re-evaluate. */\n function handleClick(): void {\n typeahead.refresh();\n }\n\n /** Blur closes the popup, then leaves edit mode. */\n function handleBlur(): void {\n typeahead.close();\n stopEditing();\n }\n\n function clearValue(): void {\n emit(\"\");\n }\n\n async function insertMergeTag(): Promise<void> {\n const cursorPos =\n isEditing.value && elementRef.value\n ? (elementRef.value.selectionStart ?? modelValue().length)\n : modelValue().length;\n\n insertingMergeTag = true;\n let mergeTag: Awaited<ReturnType<typeof requestMergeTag>>;\n try {\n mergeTag = await requestMergeTag();\n } finally {\n insertingMergeTag = false;\n }\n\n if (disposed) return;\n\n if (mergeTag) {\n const before = modelValue().slice(0, cursorPos);\n const after = modelValue().slice(cursorPos);\n const newValue = before + mergeTag.value + after;\n emit(newValue);\n\n isEditing.value = true;\n nextTick(() => {\n if (disposed) return;\n const newPos = cursorPos + mergeTag.value.length;\n elementRef.value?.focus();\n elementRef.value?.setSelectionRange(newPos, newPos);\n });\n }\n }\n\n async function insertLogicTag(): Promise<void> {\n // Read the selection straight from the element (kept focused via the\n // button's mousedown.prevent). Null only in display mode → append at end.\n const el = elementRef.value;\n const start = el?.selectionStart ?? modelValue().length;\n const end = el?.selectionEnd ?? start;\n\n insertingMergeTag = true;\n let item: Awaited<ReturnType<typeof requestLogicTag>>;\n try {\n item = await requestLogicTag();\n } finally {\n insertingMergeTag = false;\n }\n\n if (disposed || !item) return;\n\n const val = modelValue();\n let newValue: string;\n let newPos: number;\n if (\"before\" in item) {\n // Pair: wrap the selection with before/after. With no selection\n // (start === end) the caret lands between them so the user types the\n // wrapped content — mirrors the rich-text behavior.\n newValue =\n val.slice(0, start) +\n item.before +\n val.slice(start, end) +\n item.after +\n val.slice(end);\n newPos = start + item.before.length + (end - start);\n } else {\n // Tag: insert at the cursor.\n newValue = val.slice(0, start) + item.value + val.slice(start);\n newPos = start + item.value.length;\n }\n emit(newValue);\n\n isEditing.value = true;\n nextTick(() => {\n if (disposed) return;\n elementRef.value?.focus();\n elementRef.value?.setSelectionRange(newPos, newPos);\n });\n }\n\n return {\n segments,\n hasMergeTags,\n canRequestMergeTag,\n isRequestingMergeTag,\n canInsertLogicTag,\n isRequestingLogicTag,\n isEditing,\n startEditing,\n stopEditing,\n handleInput,\n handleKeydown,\n handleClick,\n handleBlur,\n clearValue,\n insertMergeTag,\n insertLogicTag,\n autocompleteAvailable: typeahead.available,\n };\n}\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport type { MergeTagSegment } from \"../composables/useMergeTagField\";\nimport { X } from \"@lucide/vue\";\n\ndefineProps<{\n segments: MergeTagSegment[];\n displayClass: string;\n pulse?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"edit\"): void;\n (e: \"clear\"): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction onEdit(): void {\n emit(\"edit\");\n}\n</script>\n\n<template>\n <div\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.clickToEdit\"\n :class=\"[displayClass, { 'tpl-pulse-fill': pulse }]\"\n @click=\"onEdit\"\n @keydown.enter=\"onEdit\"\n @keydown.space.prevent=\"onEdit\"\n >\n <template\n v-for=\"(seg, i) in segments\"\n :key=\"`${seg.type}-${i}-${seg.value}`\"\n >\n <span\n v-if=\"seg.type === 'mergeTag'\"\n class=\"tpl-tooltip tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.9em] tpl:font-medium\"\n :data-tooltip=\"seg.value\"\n style=\"\n background-color: color-mix(\n in srgb,\n var(--tpl-primary) 20%,\n transparent\n );\n color: var(--tpl-primary);\n \"\n >\n {{ seg.label }}\n </span>\n <span\n v-else-if=\"seg.type === 'logicMergeTag'\"\n class=\"tpl-tooltip tpl:inline-flex tpl:items-center tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.8em] tpl:font-bold tpl:tracking-wide tpl:uppercase\"\n :data-tooltip=\"seg.value\"\n style=\"\n background-color: transparent;\n border: 1.5px solid\n color-mix(in srgb, var(--tpl-primary) 50%, transparent);\n color: var(--tpl-primary);\n \"\n >\n {{ seg.keyword }}\n </span>\n <span v-else class=\"tpl:text-sm tpl:text-[var(--tpl-text)]\">{{\n seg.value\n }}</span>\n </template>\n <button\n type=\"button\"\n class=\"tpl:ml-auto tpl:flex tpl:size-6 tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:text-[var(--tpl-text-dim)] tpl:opacity-60 tpl:transition-all tpl:hover:text-[var(--tpl-danger)] tpl:hover:opacity-100\"\n :aria-label=\"t.mergeTag.remove\"\n :title=\"t.mergeTag.remove\"\n @click.stop=\"emit('clear')\"\n >\n <X :size=\"12\" :stroke-width=\"2.5\" />\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport type { MergeTagSegment } from \"../composables/useMergeTagField\";\nimport { X } from \"@lucide/vue\";\n\ndefineProps<{\n segments: MergeTagSegment[];\n displayClass: string;\n pulse?: boolean;\n}>();\n\nconst emit = defineEmits<{\n (e: \"edit\"): void;\n (e: \"clear\"): void;\n}>();\n\nconst { t } = useI18n();\n\nfunction onEdit(): void {\n emit(\"edit\");\n}\n</script>\n\n<template>\n <div\n role=\"button\"\n tabindex=\"0\"\n :aria-label=\"t.mergeTag.clickToEdit\"\n :class=\"[displayClass, { 'tpl-pulse-fill': pulse }]\"\n @click=\"onEdit\"\n @keydown.enter=\"onEdit\"\n @keydown.space.prevent=\"onEdit\"\n >\n <template\n v-for=\"(seg, i) in segments\"\n :key=\"`${seg.type}-${i}-${seg.value}`\"\n >\n <span\n v-if=\"seg.type === 'mergeTag'\"\n class=\"tpl-tooltip tpl:inline-flex tpl:items-center tpl:gap-1 tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.9em] tpl:font-medium\"\n :data-tooltip=\"seg.value\"\n style=\"\n background-color: color-mix(\n in srgb,\n var(--tpl-primary) 20%,\n transparent\n );\n color: var(--tpl-primary);\n \"\n >\n {{ seg.label }}\n </span>\n <span\n v-else-if=\"seg.type === 'logicMergeTag'\"\n class=\"tpl-tooltip tpl:inline-flex tpl:items-center tpl:rounded tpl:px-1.5 tpl:py-0.5 tpl:text-[0.8em] tpl:font-bold tpl:tracking-wide tpl:uppercase\"\n :data-tooltip=\"seg.value\"\n style=\"\n background-color: transparent;\n border: 1.5px solid\n color-mix(in srgb, var(--tpl-primary) 50%, transparent);\n color: var(--tpl-primary);\n \"\n >\n {{ seg.keyword }}\n </span>\n <span v-else class=\"tpl:text-sm tpl:text-[var(--tpl-text)]\">{{\n seg.value\n }}</span>\n </template>\n <button\n type=\"button\"\n class=\"tpl:ml-auto tpl:flex tpl:size-6 tpl:shrink-0 tpl:cursor-pointer tpl:items-center tpl:justify-center tpl:rounded-full tpl:border-none tpl:bg-transparent tpl:p-0 tpl:text-[var(--tpl-text-dim)] tpl:opacity-60 tpl:transition-all tpl:hover:text-[var(--tpl-danger)] tpl:hover:opacity-100\"\n :aria-label=\"t.mergeTag.remove\"\n :title=\"t.mergeTag.remove\"\n @click.stop=\"emit('clear')\"\n >\n <X :size=\"12\" :stroke-width=\"2.5\" />\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { ScanLine } from \"@lucide/vue\";\n\ndefineProps<{\n disabled?: boolean;\n}>();\n\ndefineEmits<{\n (e: \"insert\"): void;\n}>();\n\nconst { t } = useI18n();\n\nconst mergeTagBtnClass =\n \"tpl:flex tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-1 tpl:text-xs tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-[120ms] tpl:hover:bg-[var(--tpl-primary-light)] tpl:hover:text-[var(--tpl-primary)] tpl:hover:border-[var(--tpl-primary)]\";\n</script>\n\n<template>\n <button\n type=\"button\"\n :class=\"[mergeTagBtnClass, 'tpl:mt-1.5']\"\n :aria-label=\"t.mergeTag.insert\"\n :title=\"t.mergeTag.insert\"\n :disabled=\"disabled\"\n @mousedown.prevent\n @click=\"$emit('insert')\"\n >\n <ScanLine :size=\"12\" :stroke-width=\"2\" />\n {{ t.mergeTag.insertShort }}\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { ScanLine } from \"@lucide/vue\";\n\ndefineProps<{\n disabled?: boolean;\n}>();\n\ndefineEmits<{\n (e: \"insert\"): void;\n}>();\n\nconst { t } = useI18n();\n\nconst mergeTagBtnClass =\n \"tpl:flex tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-1 tpl:text-xs tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-[120ms] tpl:hover:bg-[var(--tpl-primary-light)] tpl:hover:text-[var(--tpl-primary)] tpl:hover:border-[var(--tpl-primary)]\";\n</script>\n\n<template>\n <button\n type=\"button\"\n :class=\"[mergeTagBtnClass, 'tpl:mt-1.5']\"\n :aria-label=\"t.mergeTag.insert\"\n :title=\"t.mergeTag.insert\"\n :disabled=\"disabled\"\n @mousedown.prevent\n @click=\"$emit('insert')\"\n >\n <ScanLine :size=\"12\" :stroke-width=\"2\" />\n {{ t.mergeTag.insertShort }}\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { Braces } from \"@lucide/vue\";\n\ndefineProps<{\n disabled?: boolean;\n}>();\n\ndefineEmits<{\n (e: \"insert\"): void;\n}>();\n\nconst { t } = useI18n();\n\nconst logicBtnClass =\n \"tpl:flex tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-1 tpl:text-xs tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-[120ms] tpl:hover:bg-[var(--tpl-primary-light)] tpl:hover:text-[var(--tpl-primary)] tpl:hover:border-[var(--tpl-primary)]\";\n</script>\n\n<template>\n <button\n type=\"button\"\n :class=\"[logicBtnClass, 'tpl:mt-1.5']\"\n :aria-label=\"t.logicTag.insert\"\n :title=\"t.logicTag.insert\"\n :disabled=\"disabled\"\n data-testid=\"field-insert-logic-button\"\n @mousedown.prevent\n @click=\"$emit('insert')\"\n >\n <Braces :size=\"12\" :stroke-width=\"2\" />\n {{ t.logicTag.insertShort }}\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { useI18n } from \"../composables/useI18n\";\nimport { Braces } from \"@lucide/vue\";\n\ndefineProps<{\n disabled?: boolean;\n}>();\n\ndefineEmits<{\n (e: \"insert\"): void;\n}>();\n\nconst { t } = useI18n();\n\nconst logicBtnClass =\n \"tpl:flex tpl:items-center tpl:justify-center tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:border-[var(--tpl-border)] tpl:bg-[var(--tpl-bg-hover)] tpl:px-2 tpl:py-1 tpl:text-xs tpl:text-[var(--tpl-text-muted)] tpl:transition-all tpl:duration-[120ms] tpl:hover:bg-[var(--tpl-primary-light)] tpl:hover:text-[var(--tpl-primary)] tpl:hover:border-[var(--tpl-primary)]\";\n</script>\n\n<template>\n <button\n type=\"button\"\n :class=\"[logicBtnClass, 'tpl:mt-1.5']\"\n :aria-label=\"t.logicTag.insert\"\n :title=\"t.logicTag.insert\"\n :disabled=\"disabled\"\n data-testid=\"field-insert-logic-button\"\n @mousedown.prevent\n @click=\"$emit('insert')\"\n >\n <Braces :size=\"12\" :stroke-width=\"2\" />\n {{ t.logicTag.insertShort }}\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { useMergeTagField } from \"../composables/useMergeTagField\";\nimport { inputClass } from \"../constants/styleConstants\";\nimport MergeTagSegments from \"./MergeTagSegments.vue\";\nimport MergeTagInsertButton from \"./MergeTagInsertButton.vue\";\nimport LogicTagInsertButton from \"./LogicTagInsertButton.vue\";\nimport { ref } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string;\n type?: \"text\" | \"url\";\n placeholder?: string;\n pulse?: boolean;\n }>(),\n {\n type: \"text\",\n placeholder: \"\",\n pulse: false,\n },\n);\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n /**\n * Keydown the autocomplete popup declined. Declared (rather than left to\n * attribute fallthrough) so a host binding `@keydown` never sees the key\n * that just picked a tag — the link dialog would submit on it.\n */\n (e: \"keydown\", event: KeyboardEvent): void;\n}>();\n\nconst inputRef = ref<HTMLInputElement | null>(null);\n\nconst {\n segments,\n hasMergeTags,\n canRequestMergeTag,\n isRequestingMergeTag,\n canInsertLogicTag,\n isRequestingLogicTag,\n isEditing,\n startEditing,\n handleInput,\n handleKeydown: handleFieldKeydown,\n handleClick,\n handleBlur,\n clearValue,\n insertMergeTag,\n insertLogicTag,\n} = useMergeTagField({\n modelValue: () => props.modelValue,\n emit: (value) => emit(\"update:modelValue\", value),\n elementRef: inputRef,\n});\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (!handleFieldKeydown(event)) {\n emit(\"keydown\", event);\n }\n}\n\nconst displayClass =\n \"tpl:flex tpl:w-full tpl:min-h-10 tpl:cursor-pointer tpl:items-center tpl:flex-wrap tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:shadow-[var(--tpl-shadow-sm)] tpl:bg-[var(--tpl-bg)] tpl:border-[var(--tpl-border)] tpl:px-3.5 tpl:py-1.5 tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\";\n</script>\n\n<template>\n <div v-if=\"hasMergeTags && !isEditing\">\n <MergeTagSegments\n :segments=\"segments\"\n :display-class=\"displayClass\"\n :pulse=\"pulse\"\n @edit=\"startEditing\"\n @clear=\"clearValue\"\n />\n <div\n v-if=\"canRequestMergeTag || canInsertLogicTag\"\n class=\"tpl:flex tpl:flex-wrap tpl:gap-1.5\"\n >\n <MergeTagInsertButton\n v-if=\"canRequestMergeTag\"\n :disabled=\"isRequestingMergeTag\"\n @insert=\"insertMergeTag\"\n />\n <LogicTagInsertButton\n v-if=\"canInsertLogicTag\"\n :disabled=\"isRequestingLogicTag\"\n @insert=\"insertLogicTag\"\n />\n </div>\n </div>\n <div v-else>\n <input\n ref=\"inputRef\"\n :type=\"type\"\n :class=\"[inputClass, { 'tpl-pulse-fill': pulse }]\"\n :value=\"modelValue\"\n :placeholder=\"placeholder\"\n @input=\"handleInput\"\n @keydown=\"handleKeydown\"\n @click=\"handleClick\"\n @blur=\"handleBlur\"\n />\n <div\n v-if=\"canRequestMergeTag || canInsertLogicTag\"\n class=\"tpl:flex tpl:flex-wrap tpl:gap-1.5\"\n >\n <MergeTagInsertButton\n v-if=\"canRequestMergeTag\"\n :disabled=\"isRequestingMergeTag\"\n @insert=\"insertMergeTag\"\n />\n <LogicTagInsertButton\n v-if=\"canInsertLogicTag\"\n :disabled=\"isRequestingLogicTag\"\n @insert=\"insertLogicTag\"\n />\n </div>\n </div>\n</template>\n\n<style scoped>\n.tpl-pulse-fill {\n animation: tpl-field-pulse 1s ease-out;\n}\n\n@keyframes tpl-field-pulse {\n 0% {\n box-shadow: 0 0 0 0 color-mix(in srgb, var(--tpl-warning) 30%, transparent);\n background-color: color-mix(in srgb, var(--tpl-warning) 6%, transparent);\n }\n 40% {\n box-shadow: 0 0 0 3px\n color-mix(in srgb, var(--tpl-warning) 15%, transparent);\n background-color: color-mix(in srgb, var(--tpl-warning) 5%, transparent);\n }\n 100% {\n box-shadow: 0 0 0 0 transparent;\n background-color: transparent;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .tpl-pulse-fill {\n animation: none;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { useMergeTagField } from \"../composables/useMergeTagField\";\nimport { inputClass } from \"../constants/styleConstants\";\nimport MergeTagSegments from \"./MergeTagSegments.vue\";\nimport MergeTagInsertButton from \"./MergeTagInsertButton.vue\";\nimport LogicTagInsertButton from \"./LogicTagInsertButton.vue\";\nimport { ref } from \"vue\";\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string;\n type?: \"text\" | \"url\";\n placeholder?: string;\n pulse?: boolean;\n }>(),\n {\n type: \"text\",\n placeholder: \"\",\n pulse: false,\n },\n);\n\nconst emit = defineEmits<{\n (e: \"update:modelValue\", value: string): void;\n /**\n * Keydown the autocomplete popup declined. Declared (rather than left to\n * attribute fallthrough) so a host binding `@keydown` never sees the key\n * that just picked a tag — the link dialog would submit on it.\n */\n (e: \"keydown\", event: KeyboardEvent): void;\n}>();\n\nconst inputRef = ref<HTMLInputElement | null>(null);\n\nconst {\n segments,\n hasMergeTags,\n canRequestMergeTag,\n isRequestingMergeTag,\n canInsertLogicTag,\n isRequestingLogicTag,\n isEditing,\n startEditing,\n handleInput,\n handleKeydown: handleFieldKeydown,\n handleClick,\n handleBlur,\n clearValue,\n insertMergeTag,\n insertLogicTag,\n} = useMergeTagField({\n modelValue: () => props.modelValue,\n emit: (value) => emit(\"update:modelValue\", value),\n elementRef: inputRef,\n});\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (!handleFieldKeydown(event)) {\n emit(\"keydown\", event);\n }\n}\n\nconst displayClass =\n \"tpl:flex tpl:w-full tpl:min-h-10 tpl:cursor-pointer tpl:items-center tpl:flex-wrap tpl:gap-1 tpl:rounded-[var(--tpl-radius-sm)] tpl:border tpl:shadow-[var(--tpl-shadow-sm)] tpl:bg-[var(--tpl-bg)] tpl:border-[var(--tpl-border)] tpl:px-3.5 tpl:py-1.5 tpl:transition-all tpl:duration-[120ms] tpl:ease-[cubic-bezier(0.16,1,0.3,1)]\";\n</script>\n\n<template>\n <div v-if=\"hasMergeTags && !isEditing\">\n <MergeTagSegments\n :segments=\"segments\"\n :display-class=\"displayClass\"\n :pulse=\"pulse\"\n @edit=\"startEditing\"\n @clear=\"clearValue\"\n />\n <div\n v-if=\"canRequestMergeTag || canInsertLogicTag\"\n class=\"tpl:flex tpl:flex-wrap tpl:gap-1.5\"\n >\n <MergeTagInsertButton\n v-if=\"canRequestMergeTag\"\n :disabled=\"isRequestingMergeTag\"\n @insert=\"insertMergeTag\"\n />\n <LogicTagInsertButton\n v-if=\"canInsertLogicTag\"\n :disabled=\"isRequestingLogicTag\"\n @insert=\"insertLogicTag\"\n />\n </div>\n </div>\n <div v-else>\n <input\n ref=\"inputRef\"\n :type=\"type\"\n :class=\"[inputClass, { 'tpl-pulse-fill': pulse }]\"\n :value=\"modelValue\"\n :placeholder=\"placeholder\"\n @input=\"handleInput\"\n @keydown=\"handleKeydown\"\n @click=\"handleClick\"\n @blur=\"handleBlur\"\n />\n <div\n v-if=\"canRequestMergeTag || canInsertLogicTag\"\n class=\"tpl:flex tpl:flex-wrap tpl:gap-1.5\"\n >\n <MergeTagInsertButton\n v-if=\"canRequestMergeTag\"\n :disabled=\"isRequestingMergeTag\"\n @insert=\"insertMergeTag\"\n />\n <LogicTagInsertButton\n v-if=\"canInsertLogicTag\"\n :disabled=\"isRequestingLogicTag\"\n @insert=\"insertLogicTag\"\n />\n </div>\n </div>\n</template>\n\n<style scoped>\n.tpl-pulse-fill {\n animation: tpl-field-pulse 1s ease-out;\n}\n\n@keyframes tpl-field-pulse {\n 0% {\n box-shadow: 0 0 0 0 color-mix(in srgb, var(--tpl-warning) 30%, transparent);\n background-color: color-mix(in srgb, var(--tpl-warning) 6%, transparent);\n }\n 40% {\n box-shadow: 0 0 0 3px\n color-mix(in srgb, var(--tpl-warning) 15%, transparent);\n background-color: color-mix(in srgb, var(--tpl-warning) 5%, transparent);\n }\n 100% {\n box-shadow: 0 0 0 0 transparent;\n background-color: transparent;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .tpl-pulse-fill {\n animation: none;\n }\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { E as e, F as t, N as n, S as r, at as i, f as a, g as o, h as s, lt as c, m as l, p as u, u as d, ut as f } from "./draggable-kbOhCgnk.js";
|
|
2
2
|
import { u as p } from "./src-CLoCCDfs.js";
|
|
3
|
-
import {
|
|
4
|
-
import { t as h } from "./useI18n-
|
|
3
|
+
import { E as m } from "./keys-DQp8x7Nx.js";
|
|
4
|
+
import { t as h } from "./useI18n-Ct1VIFcf.js";
|
|
5
5
|
import { $ as g, D as _, E as v, p as y } from "./icons-SP5Itd-A.js";
|
|
6
6
|
//#region src/components/ViewportToggle.vue?vue&type=script&setup=true&lang.ts
|
|
7
7
|
var b = ["aria-label"], x = [
|
|
@@ -124,4 +124,4 @@ var b = ["aria-label"], x = [
|
|
|
124
124
|
//#endregion
|
|
125
125
|
export { S as n, T as t };
|
|
126
126
|
|
|
127
|
-
//# sourceMappingURL=MergeTagModeToggle-
|
|
127
|
+
//# sourceMappingURL=MergeTagModeToggle-B_mUYJo4.js.map
|