@bikdotai/bik-component-library 0.0.838-beta.3 → 0.0.838-beta.4

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.
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const _=require("@tiptap/extension-character-count"),E=require("@tiptap/extension-color"),k=require("@tiptap/extension-font-family"),b=require("@tiptap/extension-hard-break"),C=require("@tiptap/extension-highlight"),M=require("@tiptap/extension-image"),y=require("@tiptap/extension-link"),P=require("../../node_modules/@tiptap/extension-paragraph/dist/index.js"),T=require("@tiptap/extension-placeholder"),g=require("@tiptap/extension-subscript"),L=require("@tiptap/extension-superscript"),D=require("@tiptap/extension-text-align"),H=require("@tiptap/extension-text-style"),w=require("@tiptap/extension-underline"),A=require("@tiptap/starter-kit"),z=require("../../helpers/sanitize.js"),B=require("./characterLimit/CharacterLimitExtension.js"),F=require("./FontSizeExtension.js"),I=require("./jsonHighlight/JsonHighlightExtension.js"),i=require("./mention/MentionExtension.js"),O=require("./paste/PasteExtension.js"),R=require("./plainClipboard/PasteNormalizationExtension.js"),N=require("./sectionDivider/SectionDividerNode.js"),l=require("./sendShortcut/SendShortcutExtension.js"),U=require("./slashCommand/SlashCommandExtension.js"),j=require("./variable/VariableDecorationExtension.js"),n=e=>e&&typeof e=="object"&&"default"in e?e:{default:e},u=n(_),d=n(E),v=n(k),K=n(b),J=n(C),V=n(M),W=n(y),c=n(T),Z=n(g),G=n(L),Q=n(D),X=n(w),h=n(A);function Y(e){if(e.features?.jsonMode)return[h.default.configure({link:!1,underline:!1,bold:!1,italic:!1,strike:!1,code:!1,heading:!1,bulletList:!1,orderedList:!1,blockquote:!1,horizontalRule:!1}),c.default.configure({placeholder:e.placeholder??`{
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const E=require("@tiptap/extension-character-count"),_=require("@tiptap/extension-color"),k=require("@tiptap/extension-font-family"),b=require("@tiptap/extension-hard-break"),C=require("@tiptap/extension-highlight"),M=require("@tiptap/extension-image"),y=require("@tiptap/extension-link"),T=require("../../node_modules/@tiptap/extension-paragraph/dist/index.js"),g=require("@tiptap/extension-placeholder"),P=require("@tiptap/extension-subscript"),L=require("@tiptap/extension-superscript"),H=require("@tiptap/extension-text-align"),D=require("@tiptap/extension-text-style"),w=require("@tiptap/extension-underline"),A=require("@tiptap/starter-kit"),z=require("../../helpers/sanitize.js"),B=require("./characterLimit/CharacterLimitExtension.js"),F=require("./FontSizeExtension.js"),I=require("./htmlTagStrip/HtmlTagStripExtension.js"),O=require("./jsonHighlight/JsonHighlightExtension.js"),i=require("./mention/MentionExtension.js"),R=require("./paste/PasteExtension.js"),N=require("./plainClipboard/PasteNormalizationExtension.js"),U=require("./sectionDivider/SectionDividerNode.js"),l=require("./sendShortcut/SendShortcutExtension.js"),j=require("./slashCommand/SlashCommandExtension.js"),v=require("./variable/VariableDecorationExtension.js"),n=e=>e&&typeof e=="object"&&"default"in e?e:{default:e},u=n(E),d=n(_),K=n(k),J=n(b),V=n(C),W=n(M),Z=n(y),c=n(g),G=n(P),Q=n(L),X=n(H),Y=n(w),h=n(A);function $(e){if(e.features?.jsonMode)return[h.default.configure({link:!1,underline:!1,bold:!1,italic:!1,strike:!1,code:!1,heading:!1,bulletList:!1,orderedList:!1,blockquote:!1,horizontalRule:!1}),c.default.configure({placeholder:e.placeholder??`{
2
2
 
3
- }`}),I.JsonHighlightExtension,l.SendShortcutExtension.configure({onSend:e.onSend,sendShortcut:e.sendShortcut,extraShortcuts:e.shortcuts??[]}),...e.maxCharacters?[u.default.configure({limit:e.maxCharacters})]:[]];const s=e.features?.richPaste??!1,m=e.features?.richTypography??!1,a=e.features?.allowedMarks,r=t=>!a||a.includes(t),f=K.default.extend({addKeyboardShortcuts(){return{"Shift-Enter":()=>this.editor.commands.setHardBreak()}}}),o=t=>t==="0"||t==="0px"||t==="0pt"||t==="0em",x=P.default.extend({addAttributes(){return{...this.parent?.()??{},margin:{default:null,parseHTML:t=>o(t.style.marginTop)&&o(t.style.marginBottom)?"0":null,renderHTML:t=>t.margin==="0"?{style:"margin: 0px"}:{}}}}}),S=[h.default.configure({link:!1,underline:!1,hardBreak:!1,paragraph:!1,bold:r("bold")?{}:!1,italic:r("italic")?{}:!1,strike:r("strike")?{}:!1,code:r("code")?{}:!1}),x,f,...r("underline")?[X.default]:[],W.default.extend({addPasteRules:()=>[],addInputRules:()=>[]}).configure({openOnClick:!1,autolink:!1,linkOnPaste:!1,isAllowedUri:t=>z.isAllowedLinkUrl(t),HTMLAttributes:{rel:"noopener noreferrer",class:"bik-link"}}),H.TextStyle,c.default.configure({placeholder:e.placeholder??"Type a message..."}),...e.onPaste?[O.PasteExtension.configure({onPaste:e.onPaste})]:[],R.PasteNormalizationExtension.configure({preserveMarks:s,plainOnly:!s}),l.SendShortcutExtension.configure({onSend:e.onSend,sendShortcut:e.sendShortcut,extraShortcuts:e.shortcuts??[]}),j.VariableDecorationExtension,...e.maxCharacters?[u.default.configure({limit:e.maxCharacters})]:[],...e.characterLimit?[B.CharacterLimitExtension.configure({limit:e.characterLimit})]:[],N.SectionDividerNode],p=[...e.mentions?.agents?[i.buildAgentMentionExtension(e.mentions.agents,e.onMentionSelected,e.renderMentionItem,e.renderMentionDropdown,e.removeMentionTriggerOnDismiss)]:[],...e.mentions?.teams?[i.buildTeamMentionExtension(e.mentions.teams,e.onMentionSelected,e.renderMentionItem,e.renderMentionDropdown,e.removeMentionTriggerOnDismiss)]:[],...e.slashCommands?[U.buildSlashCommandExtension(e.slashCommands,e.onSlashCommandSelected,e.renderSlashCommandItem,e.renderSlashCommandDropdown)]:[]],q=m?[d.default,J.default.configure({multicolor:!0}),v.default,F.FontSizeExtension,Q.default.configure({types:["heading","paragraph"]}),Z.default,G.default,V.default]:[d.default];return[...S,...p,...q]}exports.buildExtensions=Y;
3
+ }`}),O.JsonHighlightExtension,l.SendShortcutExtension.configure({onSend:e.onSend,sendShortcut:e.sendShortcut,extraShortcuts:e.shortcuts??[]}),...e.maxCharacters?[u.default.configure({limit:e.maxCharacters})]:[]];const s=e.features?.richPaste??!1,m=e.features?.richTypography??!1,a=e.features?.allowedMarks,r=t=>!a||a.includes(t),f=J.default.extend({addKeyboardShortcuts(){return{"Shift-Enter":()=>this.editor.commands.setHardBreak()}}}),o=t=>t==="0"||t==="0px"||t==="0pt"||t==="0em",x=T.default.extend({addAttributes(){return{...this.parent?.()??{},margin:{default:null,parseHTML:t=>o(t.style.marginTop)&&o(t.style.marginBottom)?"0":null,renderHTML:t=>t.margin==="0"?{style:"margin: 0px"}:{}}}}}),S=[h.default.configure({link:!1,underline:!1,hardBreak:!1,paragraph:!1,bold:r("bold")?{}:!1,italic:r("italic")?{}:!1,strike:r("strike")?{}:!1,code:r("code")?{}:!1}),x,f,...r("underline")?[Y.default]:[],Z.default.extend({addPasteRules:()=>[],addInputRules:()=>[]}).configure({openOnClick:!1,autolink:!1,linkOnPaste:!1,isAllowedUri:t=>z.isAllowedLinkUrl(t),HTMLAttributes:{rel:"noopener noreferrer",class:"bik-link"}}),D.TextStyle,c.default.configure({placeholder:e.placeholder??"Type a message..."}),...e.onPaste?[R.PasteExtension.configure({onPaste:e.onPaste})]:[],N.PasteNormalizationExtension.configure({preserveMarks:s,plainOnly:!s}),l.SendShortcutExtension.configure({onSend:e.onSend,sendShortcut:e.sendShortcut,extraShortcuts:e.shortcuts??[]}),v.VariableDecorationExtension,...s?[]:[I.HtmlTagStripExtension],...e.maxCharacters?[u.default.configure({limit:e.maxCharacters})]:[],...e.characterLimit?[B.CharacterLimitExtension.configure({limit:e.characterLimit})]:[],U.SectionDividerNode],p=[...e.mentions?.agents?[i.buildAgentMentionExtension(e.mentions.agents,e.onMentionSelected,e.renderMentionItem,e.renderMentionDropdown,e.removeMentionTriggerOnDismiss)]:[],...e.mentions?.teams?[i.buildTeamMentionExtension(e.mentions.teams,e.onMentionSelected,e.renderMentionItem,e.renderMentionDropdown,e.removeMentionTriggerOnDismiss)]:[],...e.slashCommands?[j.buildSlashCommandExtension(e.slashCommands,e.onSlashCommandSelected,e.renderSlashCommandItem,e.renderSlashCommandDropdown)]:[]],q=m?[d.default,V.default.configure({multicolor:!0}),K.default,F.FontSizeExtension,X.default.configure({types:["heading","paragraph"]}),G.default,Q.default,W.default]:[d.default];return[...S,...p,...q]}exports.buildExtensions=$;
4
4
  //# sourceMappingURL=buildExtensions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"buildExtensions.js","sources":["../../../../src/editor/extensions/buildExtensions.ts"],"sourcesContent":["import CharacterCount from '@tiptap/extension-character-count';\nimport Color from '@tiptap/extension-color';\nimport FontFamily from '@tiptap/extension-font-family';\nimport HardBreak from '@tiptap/extension-hard-break';\nimport Highlight from '@tiptap/extension-highlight';\nimport Image from '@tiptap/extension-image';\nimport Link from '@tiptap/extension-link';\nimport Paragraph from '@tiptap/extension-paragraph';\nimport Placeholder from '@tiptap/extension-placeholder';\nimport Subscript from '@tiptap/extension-subscript';\nimport Superscript from '@tiptap/extension-superscript';\nimport TextAlign from '@tiptap/extension-text-align';\nimport { TextStyle } from '@tiptap/extension-text-style';\nimport Underline from '@tiptap/extension-underline';\nimport StarterKit from '@tiptap/starter-kit';\nimport type { ReactNode } from 'react';\nimport { isAllowedLinkUrl } from '../../helpers/sanitize';\nimport type {\n\tEditorFeatures,\n\tEditorSnapshot,\n\tKeyboardShortcut,\n\tMentionDropdownRenderProps,\n\tMentionItem,\n\tPasteData,\n\tSlashCommandDropdownRenderProps,\n\tSlashCommandItem,\n} from '../BikEditor.types';\nimport { CharacterLimitExtension } from './characterLimit/CharacterLimitExtension';\nimport { FontSizeExtension } from './FontSizeExtension';\nimport { JsonHighlightExtension } from './jsonHighlight/JsonHighlightExtension';\nimport {\n\tbuildAgentMentionExtension,\n\tbuildTeamMentionExtension,\n} from './mention/MentionExtension';\nimport { PasteExtension } from './paste/PasteExtension';\nimport { PasteNormalizationExtension } from './plainClipboard/PasteNormalizationExtension';\nimport { SectionDividerNode } from './sectionDivider/SectionDividerNode';\nimport { SendShortcutExtension } from './sendShortcut/SendShortcutExtension';\nimport { buildSlashCommandExtension } from './slashCommand/SlashCommandExtension';\nimport { VariableDecorationExtension } from './variable/VariableDecorationExtension';\n\ninterface ExtensionConfig {\n\tfeatures?: EditorFeatures;\n\tplaceholder?: string;\n\tmaxCharacters?: number;\n\t/**\n\t * Soft character limit. Unlike `maxCharacters` (which hard-blocks all input),\n\t * this blocks typing past the limit but ALLOWS paste to overflow, rendering\n\t * the excess characters greyed (`.bik-editor-overflow`). Purely visual +\n\t * typing-guard; the consumer shows the count/error and gates send itself.\n\t */\n\tcharacterLimit?: number;\n\thasSections?: boolean;\n\tmentions?: {\n\t\tagents?: { current: MentionItem[] };\n\t\tteams?: { current: MentionItem[] };\n\t};\n\tslashCommands?: { current: SlashCommandItem[] };\n\tonPaste?: (data: PasteData) => boolean | void;\n\tonSend?: (content: EditorSnapshot) => void;\n\tsendShortcut?:\n\t\t| { key: string; modifiers?: Array<'mod' | 'ctrl' | 'shift' | 'alt'> }\n\t\t| Array<{\n\t\t\t\tkey: string;\n\t\t\t\tmodifiers?: Array<'mod' | 'ctrl' | 'shift' | 'alt'>;\n\t\t }>;\n\tshortcuts?: KeyboardShortcut[];\n\tonMentionSelected?: (item: MentionItem, char: '@' | '#') => void;\n\tonSlashCommandSelected?: (command: SlashCommandItem) => void;\n\trenderMentionItem?: (item: MentionItem, isActive: boolean) => ReactNode;\n\trenderMentionDropdown?: (props: MentionDropdownRenderProps) => ReactNode;\n\tremoveMentionTriggerOnDismiss?: boolean;\n\trenderSlashCommandItem?: (\n\t\titem: SlashCommandItem,\n\t\tisActive: boolean,\n\t) => ReactNode;\n\trenderSlashCommandDropdown?: (\n\t\tprops: SlashCommandDropdownRenderProps,\n\t) => ReactNode;\n}\n\nexport function buildExtensions(config: ExtensionConfig) {\n\t// JSON body mode is a dedicated code editor — it deliberately skips the rich\n\t// text stack (mentions, typography, rich paste, sections) and turns the whole\n\t// document into one syntax-highlighted JSON code block, Postman-style.\n\tif (config.features?.jsonMode) {\n\t\treturn [\n\t\t\tStarterKit.configure({\n\t\t\t\tlink: false,\n\t\t\t\tunderline: false,\n\t\t\t\t// Keep codeBlock (the JSON container) and history; drop everything\n\t\t\t\t// that has no meaning inside raw JSON.\n\t\t\t\tbold: false,\n\t\t\t\titalic: false,\n\t\t\t\tstrike: false,\n\t\t\t\tcode: false,\n\t\t\t\theading: false,\n\t\t\t\tbulletList: false,\n\t\t\t\torderedList: false,\n\t\t\t\tblockquote: false,\n\t\t\t\thorizontalRule: false,\n\t\t\t}),\n\t\t\tPlaceholder.configure({\n\t\t\t\tplaceholder: config.placeholder ?? '{\\n \\n}',\n\t\t\t}),\n\t\t\tJsonHighlightExtension,\n\t\t\tSendShortcutExtension.configure({\n\t\t\t\tonSend: config.onSend,\n\t\t\t\tsendShortcut: config.sendShortcut,\n\t\t\t\textraShortcuts: config.shortcuts ?? [],\n\t\t\t}),\n\t\t\t...(config.maxCharacters\n\t\t\t\t? [CharacterCount.configure({ limit: config.maxCharacters })]\n\t\t\t\t: []),\n\t\t];\n\t}\n\n\tconst hasRichPaste = config.features?.richPaste ?? false;\n\tconst hasRichTypography = config.features?.richTypography ?? false;\n\n\t// When allowedMarks is specified only those marks are registered in the schema.\n\t// This disables rendering, keyboard shortcuts AND paste-preservation for the\n\t// excluded marks — all three come for free when the extension is absent.\n\tconst allowedMarks = config.features?.allowedMarks;\n\tconst isMark = (m: 'bold' | 'italic' | 'strike' | 'underline' | 'code') =>\n\t\t!allowedMarks || allowedMarks.includes(m);\n\n\t// Soft line break. Shift-Enter inserts a <br> inside the current block\n\t// (matches Gmail); plain Enter still splits the block into a new paragraph\n\t// via StarterKit's default. HardBreak is registered standalone — like Link\n\t// and Underline above — instead of through StarterKit, so we can bind ONLY\n\t// Shift-Enter here and leave Mod-Enter free for the send shortcut.\n\tconst softBreak = HardBreak.extend({\n\t\taddKeyboardShortcuts() {\n\t\t\treturn {\n\t\t\t\t'Shift-Enter': () => this.editor.commands.setHardBreak(),\n\t\t\t};\n\t\t},\n\t});\n\n\t// Paragraph that preserves a zero block-margin from the source (Gmail-style).\n\t// Most sources (ChatGPT, typed text) use plain <p> → no margin attr → the\n\t// editor/viewer default 1em margin provides the gap. Google-Docs-style\n\t// content uses margin:0 paragraphs and a separate blank-line paragraph for\n\t// spacing; we keep that margin:0 so the blank line — not stacked margins —\n\t// provides a single-line gap, exactly like Gmail. The paste pipeline also\n\t// sets margin:'0' on every paragraph when a paste contains blank-line\n\t// paragraphs (see applyParagraphSpacing), so spacing comes from EITHER the\n\t// margin OR the blank line, never both (no double gap).\n\tconst isZeroLen = (v: string) =>\n\t\tv === '0' || v === '0px' || v === '0pt' || v === '0em';\n\tconst ParagraphWithMargin = Paragraph.extend({\n\t\taddAttributes() {\n\t\t\treturn {\n\t\t\t\t...(this.parent?.() ?? {}),\n\t\t\t\tmargin: {\n\t\t\t\t\tdefault: null,\n\t\t\t\t\tparseHTML: (el: HTMLElement) =>\n\t\t\t\t\t\tisZeroLen(el.style.marginTop) && isZeroLen(el.style.marginBottom)\n\t\t\t\t\t\t\t? '0'\n\t\t\t\t\t\t\t: null,\n\t\t\t\t\trenderHTML: (attrs: { margin?: string | null }) =>\n\t\t\t\t\t\tattrs.margin === '0' ? { style: 'margin: 0px' } : {},\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t});\n\n\tconst base = [\n\t\t// Exclude Link and Underline from StarterKit — TipTap v3 bundles both.\n\t\t// Without this, two copies of each are registered and the StarterKit copy's\n\t\t// click handler calls window.open even when openOnClick: false is set.\n\t\tStarterKit.configure({\n\t\t\tlink: false,\n\t\t\tunderline: false,\n\t\t\thardBreak: false,\n\t\t\tparagraph: false,\n\t\t\tbold: isMark('bold') ? {} : false,\n\t\t\titalic: isMark('italic') ? {} : false,\n\t\t\tstrike: isMark('strike') ? {} : false,\n\t\t\tcode: isMark('code') ? {} : false,\n\t\t}),\n\t\tParagraphWithMargin,\n\t\tsoftBreak,\n\t\t...(isMark('underline') ? [Underline] : []),\n\t\t// Extend Link to strip addPasteRules() and addInputRules() so pasting a\n\t\t// URL never creates a hyperlink regardless of autolink/linkOnPaste values.\n\t\tLink.extend({\n\t\t\taddPasteRules: () => [],\n\t\t\taddInputRules: () => [],\n\t\t}).configure({\n\t\t\topenOnClick: false,\n\t\t\tautolink: false,\n\t\t\tlinkOnPaste: false,\n\t\t\t// Block javascript:/data:/vbscript: hrefs. TipTap keeps the href\n\t\t\t// attribute verbatim, so scheme validation must live here.\n\t\t\tisAllowedUri: (url) => isAllowedLinkUrl(url),\n\t\t\tHTMLAttributes: {\n\t\t\t\trel: 'noopener noreferrer',\n\t\t\t\tclass: 'bik-link',\n\t\t\t},\n\t\t}),\n\t\tTextStyle,\n\t\tPlaceholder.configure({\n\t\t\tplaceholder: config.placeholder ?? 'Type a message...',\n\t\t}),\n\t\t// Consumer paste callback runs first. If it returns true the event is\n\t\t// consumed and neither PlainClipboardExtension nor the editor's default\n\t\t// paste handling will run for that event.\n\t\t...(config.onPaste\n\t\t\t? [PasteExtension.configure({ onPaste: config.onPaste })]\n\t\t\t: []),\n\t\tPasteNormalizationExtension.configure({\n\t\t\tpreserveMarks: hasRichPaste,\n\t\t\t// Only email + signature (richPaste) keep the rich HTML paste pipeline.\n\t\t\t// Every other channel (WhatsApp, IG, FB, comments) forces plain-text\n\t\t\t// paste for BOTH Cmd/Ctrl+V and Cmd/Ctrl+Shift+V — no formatting.\n\t\t\tplainOnly: !hasRichPaste,\n\t\t}),\n\t\tSendShortcutExtension.configure({\n\t\t\tonSend: config.onSend,\n\t\t\tsendShortcut: config.sendShortcut,\n\t\t\textraShortcuts: config.shortcuts ?? [],\n\t\t}),\n\t\tVariableDecorationExtension,\n\t\t...(config.maxCharacters\n\t\t\t? [CharacterCount.configure({ limit: config.maxCharacters })]\n\t\t\t: []),\n\t\t...(config.characterLimit\n\t\t\t? [CharacterLimitExtension.configure({ limit: config.characterLimit })]\n\t\t\t: []),\n\t\t// Always register SectionDividerNode so the custom <div data-section-divider>\n\t\t// node is part of the schema in every editor instance. This means imperative\n\t\t// callers (setBodyAndSections / setSectionContent) work even when no\n\t\t// `sections` prop was provided at mount time.\n\t\tSectionDividerNode,\n\t];\n\n\tconst mentionExtensions = [\n\t\t...(config.mentions?.agents\n\t\t\t? [\n\t\t\t\t\tbuildAgentMentionExtension(\n\t\t\t\t\t\tconfig.mentions.agents,\n\t\t\t\t\t\tconfig.onMentionSelected,\n\t\t\t\t\t\tconfig.renderMentionItem,\n\t\t\t\t\t\tconfig.renderMentionDropdown,\n\t\t\t\t\t\tconfig.removeMentionTriggerOnDismiss,\n\t\t\t\t\t),\n\t\t\t ]\n\t\t\t: []),\n\t\t...(config.mentions?.teams\n\t\t\t? [\n\t\t\t\t\tbuildTeamMentionExtension(\n\t\t\t\t\t\tconfig.mentions.teams,\n\t\t\t\t\t\tconfig.onMentionSelected,\n\t\t\t\t\t\tconfig.renderMentionItem,\n\t\t\t\t\t\tconfig.renderMentionDropdown,\n\t\t\t\t\t\tconfig.removeMentionTriggerOnDismiss,\n\t\t\t\t\t),\n\t\t\t ]\n\t\t\t: []),\n\t\t...(config.slashCommands\n\t\t\t? [\n\t\t\t\t\tbuildSlashCommandExtension(\n\t\t\t\t\t\tconfig.slashCommands,\n\t\t\t\t\t\tconfig.onSlashCommandSelected,\n\t\t\t\t\t\tconfig.renderSlashCommandItem,\n\t\t\t\t\t\tconfig.renderSlashCommandDropdown,\n\t\t\t\t\t),\n\t\t\t ]\n\t\t\t: []),\n\t];\n\n\tconst richExtensions = hasRichTypography\n\t\t? [\n\t\t\t\tColor,\n\t\t\t\tHighlight.configure({ multicolor: true }),\n\t\t\t\tFontFamily,\n\t\t\t\tFontSizeExtension,\n\t\t\t\tTextAlign.configure({ types: ['heading', 'paragraph'] }),\n\t\t\t\tSubscript,\n\t\t\t\tSuperscript,\n\t\t\t\tImage,\n\t\t ]\n\t\t: [Color];\n\n\treturn [...base, ...mentionExtensions, ...richExtensions];\n}\n"],"names":["buildExtensions","config","StarterKit","Placeholder","JsonHighlightExtension","SendShortcutExtension","CharacterCount","hasRichPaste","hasRichTypography","allowedMarks","isMark","m","softBreak","HardBreak","isZeroLen","v","ParagraphWithMargin","Paragraph","el","attrs","base","Underline","Link","url","isAllowedLinkUrl","TextStyle","PasteExtension","PasteNormalizationExtension","VariableDecorationExtension","CharacterLimitExtension","SectionDividerNode","mentionExtensions","buildAgentMentionExtension","buildTeamMentionExtension","buildSlashCommandExtension","richExtensions","Color","Highlight","FontFamily","FontSizeExtension","TextAlign","Subscript","Superscript","Image"],"mappings":"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"}
1
+ {"version":3,"file":"buildExtensions.js","sources":["../../../../src/editor/extensions/buildExtensions.ts"],"sourcesContent":["import CharacterCount from '@tiptap/extension-character-count';\nimport Color from '@tiptap/extension-color';\nimport FontFamily from '@tiptap/extension-font-family';\nimport HardBreak from '@tiptap/extension-hard-break';\nimport Highlight from '@tiptap/extension-highlight';\nimport Image from '@tiptap/extension-image';\nimport Link from '@tiptap/extension-link';\nimport Paragraph from '@tiptap/extension-paragraph';\nimport Placeholder from '@tiptap/extension-placeholder';\nimport Subscript from '@tiptap/extension-subscript';\nimport Superscript from '@tiptap/extension-superscript';\nimport TextAlign from '@tiptap/extension-text-align';\nimport { TextStyle } from '@tiptap/extension-text-style';\nimport Underline from '@tiptap/extension-underline';\nimport StarterKit from '@tiptap/starter-kit';\nimport type { ReactNode } from 'react';\nimport { isAllowedLinkUrl } from '../../helpers/sanitize';\nimport type {\n\tEditorFeatures,\n\tEditorSnapshot,\n\tKeyboardShortcut,\n\tMentionDropdownRenderProps,\n\tMentionItem,\n\tPasteData,\n\tSlashCommandDropdownRenderProps,\n\tSlashCommandItem,\n} from '../BikEditor.types';\nimport { CharacterLimitExtension } from './characterLimit/CharacterLimitExtension';\nimport { FontSizeExtension } from './FontSizeExtension';\nimport { HtmlTagStripExtension } from './htmlTagStrip/HtmlTagStripExtension';\nimport { JsonHighlightExtension } from './jsonHighlight/JsonHighlightExtension';\nimport {\n\tbuildAgentMentionExtension,\n\tbuildTeamMentionExtension,\n} from './mention/MentionExtension';\nimport { PasteExtension } from './paste/PasteExtension';\nimport { PasteNormalizationExtension } from './plainClipboard/PasteNormalizationExtension';\nimport { SectionDividerNode } from './sectionDivider/SectionDividerNode';\nimport { SendShortcutExtension } from './sendShortcut/SendShortcutExtension';\nimport { buildSlashCommandExtension } from './slashCommand/SlashCommandExtension';\nimport { VariableDecorationExtension } from './variable/VariableDecorationExtension';\n\ninterface ExtensionConfig {\n\tfeatures?: EditorFeatures;\n\tplaceholder?: string;\n\tmaxCharacters?: number;\n\t/**\n\t * Soft character limit. Unlike `maxCharacters` (which hard-blocks all input),\n\t * this blocks typing past the limit but ALLOWS paste to overflow, rendering\n\t * the excess characters greyed (`.bik-editor-overflow`). Purely visual +\n\t * typing-guard; the consumer shows the count/error and gates send itself.\n\t */\n\tcharacterLimit?: number;\n\thasSections?: boolean;\n\tmentions?: {\n\t\tagents?: { current: MentionItem[] };\n\t\tteams?: { current: MentionItem[] };\n\t};\n\tslashCommands?: { current: SlashCommandItem[] };\n\tonPaste?: (data: PasteData) => boolean | void;\n\tonSend?: (content: EditorSnapshot) => void;\n\tsendShortcut?:\n\t\t| { key: string; modifiers?: Array<'mod' | 'ctrl' | 'shift' | 'alt'> }\n\t\t| Array<{\n\t\t\t\tkey: string;\n\t\t\t\tmodifiers?: Array<'mod' | 'ctrl' | 'shift' | 'alt'>;\n\t\t }>;\n\tshortcuts?: KeyboardShortcut[];\n\tonMentionSelected?: (item: MentionItem, char: '@' | '#') => void;\n\tonSlashCommandSelected?: (command: SlashCommandItem) => void;\n\trenderMentionItem?: (item: MentionItem, isActive: boolean) => ReactNode;\n\trenderMentionDropdown?: (props: MentionDropdownRenderProps) => ReactNode;\n\tremoveMentionTriggerOnDismiss?: boolean;\n\trenderSlashCommandItem?: (\n\t\titem: SlashCommandItem,\n\t\tisActive: boolean,\n\t) => ReactNode;\n\trenderSlashCommandDropdown?: (\n\t\tprops: SlashCommandDropdownRenderProps,\n\t) => ReactNode;\n}\n\nexport function buildExtensions(config: ExtensionConfig) {\n\t// JSON body mode is a dedicated code editor — it deliberately skips the rich\n\t// text stack (mentions, typography, rich paste, sections) and turns the whole\n\t// document into one syntax-highlighted JSON code block, Postman-style.\n\tif (config.features?.jsonMode) {\n\t\treturn [\n\t\t\tStarterKit.configure({\n\t\t\t\tlink: false,\n\t\t\t\tunderline: false,\n\t\t\t\t// Keep codeBlock (the JSON container) and history; drop everything\n\t\t\t\t// that has no meaning inside raw JSON.\n\t\t\t\tbold: false,\n\t\t\t\titalic: false,\n\t\t\t\tstrike: false,\n\t\t\t\tcode: false,\n\t\t\t\theading: false,\n\t\t\t\tbulletList: false,\n\t\t\t\torderedList: false,\n\t\t\t\tblockquote: false,\n\t\t\t\thorizontalRule: false,\n\t\t\t}),\n\t\t\tPlaceholder.configure({\n\t\t\t\tplaceholder: config.placeholder ?? '{\\n \\n}',\n\t\t\t}),\n\t\t\tJsonHighlightExtension,\n\t\t\tSendShortcutExtension.configure({\n\t\t\t\tonSend: config.onSend,\n\t\t\t\tsendShortcut: config.sendShortcut,\n\t\t\t\textraShortcuts: config.shortcuts ?? [],\n\t\t\t}),\n\t\t\t...(config.maxCharacters\n\t\t\t\t? [CharacterCount.configure({ limit: config.maxCharacters })]\n\t\t\t\t: []),\n\t\t];\n\t}\n\n\tconst hasRichPaste = config.features?.richPaste ?? false;\n\tconst hasRichTypography = config.features?.richTypography ?? false;\n\n\t// When allowedMarks is specified only those marks are registered in the schema.\n\t// This disables rendering, keyboard shortcuts AND paste-preservation for the\n\t// excluded marks — all three come for free when the extension is absent.\n\tconst allowedMarks = config.features?.allowedMarks;\n\tconst isMark = (m: 'bold' | 'italic' | 'strike' | 'underline' | 'code') =>\n\t\t!allowedMarks || allowedMarks.includes(m);\n\n\t// Soft line break. Shift-Enter inserts a <br> inside the current block\n\t// (matches Gmail); plain Enter still splits the block into a new paragraph\n\t// via StarterKit's default. HardBreak is registered standalone — like Link\n\t// and Underline above — instead of through StarterKit, so we can bind ONLY\n\t// Shift-Enter here and leave Mod-Enter free for the send shortcut.\n\tconst softBreak = HardBreak.extend({\n\t\taddKeyboardShortcuts() {\n\t\t\treturn {\n\t\t\t\t'Shift-Enter': () => this.editor.commands.setHardBreak(),\n\t\t\t};\n\t\t},\n\t});\n\n\t// Paragraph that preserves a zero block-margin from the source (Gmail-style).\n\t// Most sources (ChatGPT, typed text) use plain <p> → no margin attr → the\n\t// editor/viewer default 1em margin provides the gap. Google-Docs-style\n\t// content uses margin:0 paragraphs and a separate blank-line paragraph for\n\t// spacing; we keep that margin:0 so the blank line — not stacked margins —\n\t// provides a single-line gap, exactly like Gmail. The paste pipeline also\n\t// sets margin:'0' on every paragraph when a paste contains blank-line\n\t// paragraphs (see applyParagraphSpacing), so spacing comes from EITHER the\n\t// margin OR the blank line, never both (no double gap).\n\tconst isZeroLen = (v: string) =>\n\t\tv === '0' || v === '0px' || v === '0pt' || v === '0em';\n\tconst ParagraphWithMargin = Paragraph.extend({\n\t\taddAttributes() {\n\t\t\treturn {\n\t\t\t\t...(this.parent?.() ?? {}),\n\t\t\t\tmargin: {\n\t\t\t\t\tdefault: null,\n\t\t\t\t\tparseHTML: (el: HTMLElement) =>\n\t\t\t\t\t\tisZeroLen(el.style.marginTop) && isZeroLen(el.style.marginBottom)\n\t\t\t\t\t\t\t? '0'\n\t\t\t\t\t\t\t: null,\n\t\t\t\t\trenderHTML: (attrs: { margin?: string | null }) =>\n\t\t\t\t\t\tattrs.margin === '0' ? { style: 'margin: 0px' } : {},\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t});\n\n\tconst base = [\n\t\t// Exclude Link and Underline from StarterKit — TipTap v3 bundles both.\n\t\t// Without this, two copies of each are registered and the StarterKit copy's\n\t\t// click handler calls window.open even when openOnClick: false is set.\n\t\tStarterKit.configure({\n\t\t\tlink: false,\n\t\t\tunderline: false,\n\t\t\thardBreak: false,\n\t\t\tparagraph: false,\n\t\t\tbold: isMark('bold') ? {} : false,\n\t\t\titalic: isMark('italic') ? {} : false,\n\t\t\tstrike: isMark('strike') ? {} : false,\n\t\t\tcode: isMark('code') ? {} : false,\n\t\t}),\n\t\tParagraphWithMargin,\n\t\tsoftBreak,\n\t\t...(isMark('underline') ? [Underline] : []),\n\t\t// Extend Link to strip addPasteRules() and addInputRules() so pasting a\n\t\t// URL never creates a hyperlink regardless of autolink/linkOnPaste values.\n\t\tLink.extend({\n\t\t\taddPasteRules: () => [],\n\t\t\taddInputRules: () => [],\n\t\t}).configure({\n\t\t\topenOnClick: false,\n\t\t\tautolink: false,\n\t\t\tlinkOnPaste: false,\n\t\t\t// Block javascript:/data:/vbscript: hrefs. TipTap keeps the href\n\t\t\t// attribute verbatim, so scheme validation must live here.\n\t\t\tisAllowedUri: (url) => isAllowedLinkUrl(url),\n\t\t\tHTMLAttributes: {\n\t\t\t\trel: 'noopener noreferrer',\n\t\t\t\tclass: 'bik-link',\n\t\t\t},\n\t\t}),\n\t\tTextStyle,\n\t\tPlaceholder.configure({\n\t\t\tplaceholder: config.placeholder ?? 'Type a message...',\n\t\t}),\n\t\t// Consumer paste callback runs first. If it returns true the event is\n\t\t// consumed and neither PlainClipboardExtension nor the editor's default\n\t\t// paste handling will run for that event.\n\t\t...(config.onPaste\n\t\t\t? [PasteExtension.configure({ onPaste: config.onPaste })]\n\t\t\t: []),\n\t\tPasteNormalizationExtension.configure({\n\t\t\tpreserveMarks: hasRichPaste,\n\t\t\t// Only email + signature (richPaste) keep the rich HTML paste pipeline.\n\t\t\t// Every other channel (WhatsApp, IG, FB, comments) forces plain-text\n\t\t\t// paste for BOTH Cmd/Ctrl+V and Cmd/Ctrl+Shift+V — no formatting.\n\t\t\tplainOnly: !hasRichPaste,\n\t\t}),\n\t\tSendShortcutExtension.configure({\n\t\t\tonSend: config.onSend,\n\t\t\tsendShortcut: config.sendShortcut,\n\t\t\textraShortcuts: config.shortcuts ?? [],\n\t\t}),\n\t\tVariableDecorationExtension,\n\t\t// Strip complete HTML tags from typed text in non-email channels.\n\t\t// Email (richPaste) keeps tags because the editor legitimately renders HTML.\n\t\t...(!hasRichPaste ? [HtmlTagStripExtension] : []),\n\t\t...(config.maxCharacters\n\t\t\t? [CharacterCount.configure({ limit: config.maxCharacters })]\n\t\t\t: []),\n\t\t...(config.characterLimit\n\t\t\t? [CharacterLimitExtension.configure({ limit: config.characterLimit })]\n\t\t\t: []),\n\t\t// Always register SectionDividerNode so the custom <div data-section-divider>\n\t\t// node is part of the schema in every editor instance. This means imperative\n\t\t// callers (setBodyAndSections / setSectionContent) work even when no\n\t\t// `sections` prop was provided at mount time.\n\t\tSectionDividerNode,\n\t];\n\n\tconst mentionExtensions = [\n\t\t...(config.mentions?.agents\n\t\t\t? [\n\t\t\t\t\tbuildAgentMentionExtension(\n\t\t\t\t\t\tconfig.mentions.agents,\n\t\t\t\t\t\tconfig.onMentionSelected,\n\t\t\t\t\t\tconfig.renderMentionItem,\n\t\t\t\t\t\tconfig.renderMentionDropdown,\n\t\t\t\t\t\tconfig.removeMentionTriggerOnDismiss,\n\t\t\t\t\t),\n\t\t\t ]\n\t\t\t: []),\n\t\t...(config.mentions?.teams\n\t\t\t? [\n\t\t\t\t\tbuildTeamMentionExtension(\n\t\t\t\t\t\tconfig.mentions.teams,\n\t\t\t\t\t\tconfig.onMentionSelected,\n\t\t\t\t\t\tconfig.renderMentionItem,\n\t\t\t\t\t\tconfig.renderMentionDropdown,\n\t\t\t\t\t\tconfig.removeMentionTriggerOnDismiss,\n\t\t\t\t\t),\n\t\t\t ]\n\t\t\t: []),\n\t\t...(config.slashCommands\n\t\t\t? [\n\t\t\t\t\tbuildSlashCommandExtension(\n\t\t\t\t\t\tconfig.slashCommands,\n\t\t\t\t\t\tconfig.onSlashCommandSelected,\n\t\t\t\t\t\tconfig.renderSlashCommandItem,\n\t\t\t\t\t\tconfig.renderSlashCommandDropdown,\n\t\t\t\t\t),\n\t\t\t ]\n\t\t\t: []),\n\t];\n\n\tconst richExtensions = hasRichTypography\n\t\t? [\n\t\t\t\tColor,\n\t\t\t\tHighlight.configure({ multicolor: true }),\n\t\t\t\tFontFamily,\n\t\t\t\tFontSizeExtension,\n\t\t\t\tTextAlign.configure({ types: ['heading', 'paragraph'] }),\n\t\t\t\tSubscript,\n\t\t\t\tSuperscript,\n\t\t\t\tImage,\n\t\t ]\n\t\t: [Color];\n\n\treturn [...base, ...mentionExtensions, ...richExtensions];\n}\n"],"names":["buildExtensions","config","StarterKit","Placeholder","JsonHighlightExtension","SendShortcutExtension","CharacterCount","hasRichPaste","hasRichTypography","allowedMarks","isMark","m","softBreak","HardBreak","isZeroLen","v","ParagraphWithMargin","Paragraph","el","attrs","base","Underline","Link","url","isAllowedLinkUrl","TextStyle","PasteExtension","PasteNormalizationExtension","VariableDecorationExtension","HtmlTagStripExtension","CharacterLimitExtension","SectionDividerNode","mentionExtensions","buildAgentMentionExtension","buildTeamMentionExtension","buildSlashCommandExtension","richExtensions","Color","Highlight","FontFamily","FontSizeExtension","TextAlign","Subscript","Superscript","Image"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("../../../node_modules/@tiptap/core/dist/index.js"),u=require("@tiptap/pm/state"),r=/<\/?[a-zA-Z][a-zA-Z0-9]*(?:\s[^>]*)?\/?>/g,m=c.Extension.create({name:"htmlTagStrip",addProseMirrorPlugins(){return[new u.Plugin({appendTransaction(p,o,n){if(!p.some(t=>t.docChanged))return null;const{tr:e}=n;let s=!1;return n.doc.descendants((t,a)=>{if(!t.isText||!t.text||(r.lastIndex=0,!r.test(t.text)))return;r.lastIndex=0;const i=t.text.replace(r,"");if(i===t.text)return;const l=a+t.nodeSize;i?e.replaceWith(e.mapping.map(a),e.mapping.map(l),n.schema.text(i,t.marks)):e.delete(e.mapping.map(a),e.mapping.map(l)),s=!0}),s?(e.setMeta("addToHistory",!1),e):null}})]}});exports.HtmlTagStripExtension=m;
2
+ //# sourceMappingURL=HtmlTagStripExtension.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HtmlTagStripExtension.js","sources":["../../../../../src/editor/extensions/htmlTagStrip/HtmlTagStripExtension.ts"],"sourcesContent":["import { Extension } from '@tiptap/core';\nimport { Plugin } from '@tiptap/pm/state';\n\nconst COMPLETE_TAG = /<\\/?[a-zA-Z][a-zA-Z0-9]*(?:\\s[^>]*)?\\/?>/g;\n\n/**\n * Strips complete HTML tags from typed text in real time.\n *\n * Enabled for non-email channels (WhatsApp, IG, private note, etc.) where\n * HTML has no meaning. When the user types `<h1>Hello</h1>`, the tags are\n * removed as soon as the closing `>` completes each tag, leaving just `Hello`.\n *\n * Incomplete tags like `<h` are preserved (the regex requires a closing `>`).\n * Non-tag angle brackets like `2 < 3` are also preserved.\n *\n * Uses `appendTransaction` so the strip happens in the same undo step as\n * the keystroke — Cmd+Z undoes both the tag and the strip together.\n */\nexport const HtmlTagStripExtension = Extension.create({\n\tname: 'htmlTagStrip',\n\taddProseMirrorPlugins() {\n\t\treturn [\n\t\t\tnew Plugin({\n\t\t\t\tappendTransaction(transactions, _oldState, newState) {\n\t\t\t\t\tif (!transactions.some((t) => t.docChanged)) return null;\n\n\t\t\t\t\tconst { tr } = newState;\n\t\t\t\t\tlet changed = false;\n\n\t\t\t\t\tnewState.doc.descendants((node, pos) => {\n\t\t\t\t\t\tif (!node.isText || !node.text) return;\n\t\t\t\t\t\tCOMPLETE_TAG.lastIndex = 0;\n\t\t\t\t\t\tif (!COMPLETE_TAG.test(node.text)) return;\n\n\t\t\t\t\t\tCOMPLETE_TAG.lastIndex = 0;\n\t\t\t\t\t\tconst stripped = node.text.replace(COMPLETE_TAG, '');\n\t\t\t\t\t\tif (stripped === node.text) return;\n\n\t\t\t\t\t\tconst end = pos + node.nodeSize;\n\t\t\t\t\t\tif (stripped) {\n\t\t\t\t\t\t\ttr.replaceWith(\n\t\t\t\t\t\t\t\ttr.mapping.map(pos),\n\t\t\t\t\t\t\t\ttr.mapping.map(end),\n\t\t\t\t\t\t\t\tnewState.schema.text(stripped, node.marks),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttr.delete(tr.mapping.map(pos), tr.mapping.map(end));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tchanged = true;\n\t\t\t\t\t});\n\n\t\t\t\t\tif (!changed) return null;\n\t\t\t\t\ttr.setMeta('addToHistory', false);\n\t\t\t\t\treturn tr;\n\t\t\t\t},\n\t\t\t}),\n\t\t];\n\t},\n});\n"],"names":["COMPLETE_TAG","HtmlTagStripExtension","Extension","Plugin","transactions","_oldState","newState","tr","changed","node","pos","stripped","end"],"mappings":"kLAGMA,EAAe,4CAeRC,EAAwBC,EAAAA,UAAU,OAAO,CACrD,KAAM,eACN,uBAAwB,CACvB,MAAO,CACN,IAAIC,SAAO,CACV,kBAAkBC,EAAcC,EAAWC,EAAU,CACpD,GAAI,CAACF,EAAa,KAAM,GAAM,EAAE,UAAU,EAAG,OAAO,KAEpD,KAAM,CAAE,GAAAG,GAAOD,EACf,IAAIE,EAAU,GAwBd,OAtBAF,EAAS,IAAI,YAAY,CAACG,EAAMC,IAAQ,CAGvC,GAFI,CAACD,EAAK,QAAU,CAACA,EAAK,OAC1BT,EAAa,UAAY,EACrB,CAACA,EAAa,KAAKS,EAAK,IAAI,GAAG,OAEnCT,EAAa,UAAY,EACzB,MAAMW,EAAWF,EAAK,KAAK,QAAQT,EAAc,EAAE,EACnD,GAAIW,IAAaF,EAAK,KAAM,OAE5B,MAAMG,EAAMF,EAAMD,EAAK,SACnBE,EACHJ,EAAG,YACFA,EAAG,QAAQ,IAAIG,CAAG,EAClBH,EAAG,QAAQ,IAAIK,CAAG,EAClBN,EAAS,OAAO,KAAKK,EAAUF,EAAK,KAAK,CAAA,EAG1CF,EAAG,OAAOA,EAAG,QAAQ,IAAIG,CAAG,EAAGH,EAAG,QAAQ,IAAIK,CAAG,CAAC,EAEnDJ,EAAU,EACX,CAAC,EAEIA,GACLD,EAAG,QAAQ,eAAgB,EAAK,EACzBA,GAFc,IAGtB,CAAA,CACA,CAAA,CAEH,CACD,CAAC"}
@@ -11,20 +11,21 @@ import y from "@tiptap/extension-subscript";
11
11
  import T from "@tiptap/extension-superscript";
12
12
  import L from "@tiptap/extension-text-align";
13
13
  import { TextStyle as P } from "@tiptap/extension-text-style";
14
- import w from "@tiptap/extension-underline";
14
+ import H from "@tiptap/extension-underline";
15
15
  import m from "@tiptap/starter-kit";
16
- import { isAllowedLinkUrl as D } from "../../helpers/sanitize.js";
17
- import { CharacterLimitExtension as H } from "./characterLimit/CharacterLimitExtension.js";
18
- import { FontSizeExtension as A } from "./FontSizeExtension.js";
19
- import { JsonHighlightExtension as g } from "./jsonHighlight/JsonHighlightExtension.js";
20
- import { buildAgentMentionExtension as B, buildTeamMentionExtension as I } from "./mention/MentionExtension.js";
21
- import { PasteExtension as O } from "./paste/PasteExtension.js";
22
- import { PasteNormalizationExtension as R } from "./plainClipboard/PasteNormalizationExtension.js";
23
- import { SectionDividerNode as z } from "./sectionDivider/SectionDividerNode.js";
16
+ import { isAllowedLinkUrl as w } from "../../helpers/sanitize.js";
17
+ import { CharacterLimitExtension as D } from "./characterLimit/CharacterLimitExtension.js";
18
+ import { FontSizeExtension as g } from "./FontSizeExtension.js";
19
+ import { HtmlTagStripExtension as A } from "./htmlTagStrip/HtmlTagStripExtension.js";
20
+ import { JsonHighlightExtension as B } from "./jsonHighlight/JsonHighlightExtension.js";
21
+ import { buildAgentMentionExtension as I, buildTeamMentionExtension as O } from "./mention/MentionExtension.js";
22
+ import { PasteExtension as R } from "./paste/PasteExtension.js";
23
+ import { PasteNormalizationExtension as z } from "./plainClipboard/PasteNormalizationExtension.js";
24
+ import { SectionDividerNode as F } from "./sectionDivider/SectionDividerNode.js";
24
25
  import { SendShortcutExtension as d } from "./sendShortcut/SendShortcutExtension.js";
25
- import { buildSlashCommandExtension as F } from "./slashCommand/SlashCommandExtension.js";
26
- import { VariableDecorationExtension as U } from "./variable/VariableDecorationExtension.js";
27
- function pe(e) {
26
+ import { buildSlashCommandExtension as U } from "./slashCommand/SlashCommandExtension.js";
27
+ import { VariableDecorationExtension as K } from "./variable/VariableDecorationExtension.js";
28
+ function he(e) {
28
29
  if (e.features?.jsonMode)
29
30
  return [
30
31
  m.configure({
@@ -47,7 +48,7 @@ function pe(e) {
47
48
 
48
49
  }`
49
50
  }),
50
- g,
51
+ B,
51
52
  d.configure({
52
53
  onSend: e.onSend,
53
54
  sendShortcut: e.sendShortcut,
@@ -88,7 +89,7 @@ function pe(e) {
88
89
  }),
89
90
  h,
90
91
  u,
91
- ...t("underline") ? [w] : [],
92
+ ...t("underline") ? [H] : [],
92
93
  // Extend Link to strip addPasteRules() and addInputRules() so pasting a
93
94
  // URL never creates a hyperlink regardless of autolink/linkOnPaste values.
94
95
  C.extend({
@@ -100,7 +101,7 @@ function pe(e) {
100
101
  linkOnPaste: !1,
101
102
  // Block javascript:/data:/vbscript: hrefs. TipTap keeps the href
102
103
  // attribute verbatim, so scheme validation must live here.
103
- isAllowedUri: (r) => D(r),
104
+ isAllowedUri: (r) => w(r),
104
105
  HTMLAttributes: {
105
106
  rel: "noopener noreferrer",
106
107
  class: "bik-link"
@@ -113,8 +114,8 @@ function pe(e) {
113
114
  // Consumer paste callback runs first. If it returns true the event is
114
115
  // consumed and neither PlainClipboardExtension nor the editor's default
115
116
  // paste handling will run for that event.
116
- ...e.onPaste ? [O.configure({ onPaste: e.onPaste })] : [],
117
- R.configure({
117
+ ...e.onPaste ? [R.configure({ onPaste: e.onPaste })] : [],
118
+ z.configure({
118
119
  preserveMarks: o,
119
120
  // Only email + signature (richPaste) keep the rich HTML paste pipeline.
120
121
  // Every other channel (WhatsApp, IG, FB, comments) forces plain-text
@@ -126,17 +127,20 @@ function pe(e) {
126
127
  sendShortcut: e.sendShortcut,
127
128
  extraShortcuts: e.shortcuts ?? []
128
129
  }),
129
- U,
130
+ K,
131
+ // Strip complete HTML tags from typed text in non-email channels.
132
+ // Email (richPaste) keeps tags because the editor legitimately renders HTML.
133
+ ...o ? [] : [A],
130
134
  ...e.maxCharacters ? [s.configure({ limit: e.maxCharacters })] : [],
131
- ...e.characterLimit ? [H.configure({ limit: e.characterLimit })] : [],
135
+ ...e.characterLimit ? [D.configure({ limit: e.characterLimit })] : [],
132
136
  // Always register SectionDividerNode so the custom <div data-section-divider>
133
137
  // node is part of the schema in every editor instance. This means imperative
134
138
  // callers (setBodyAndSections / setSectionContent) work even when no
135
139
  // `sections` prop was provided at mount time.
136
- z
140
+ F
137
141
  ], c = [
138
142
  ...e.mentions?.agents ? [
139
- B(
143
+ I(
140
144
  e.mentions.agents,
141
145
  e.onMentionSelected,
142
146
  e.renderMentionItem,
@@ -145,7 +149,7 @@ function pe(e) {
145
149
  )
146
150
  ] : [],
147
151
  ...e.mentions?.teams ? [
148
- I(
152
+ O(
149
153
  e.mentions.teams,
150
154
  e.onMentionSelected,
151
155
  e.renderMentionItem,
@@ -154,7 +158,7 @@ function pe(e) {
154
158
  )
155
159
  ] : [],
156
160
  ...e.slashCommands ? [
157
- F(
161
+ U(
158
162
  e.slashCommands,
159
163
  e.onSlashCommandSelected,
160
164
  e.renderSlashCommandItem,
@@ -165,7 +169,7 @@ function pe(e) {
165
169
  i,
166
170
  M.configure({ multicolor: !0 }),
167
171
  S,
168
- A,
172
+ g,
169
173
  L.configure({ types: ["heading", "paragraph"] }),
170
174
  y,
171
175
  T,
@@ -174,6 +178,6 @@ function pe(e) {
174
178
  return [...f, ...c, ...x];
175
179
  }
176
180
  export {
177
- pe as buildExtensions
181
+ he as buildExtensions
178
182
  };
179
183
  //# sourceMappingURL=buildExtensions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"buildExtensions.js","sources":["../../../../src/editor/extensions/buildExtensions.ts"],"sourcesContent":["import CharacterCount from '@tiptap/extension-character-count';\nimport Color from '@tiptap/extension-color';\nimport FontFamily from '@tiptap/extension-font-family';\nimport HardBreak from '@tiptap/extension-hard-break';\nimport Highlight from '@tiptap/extension-highlight';\nimport Image from '@tiptap/extension-image';\nimport Link from '@tiptap/extension-link';\nimport Paragraph from '@tiptap/extension-paragraph';\nimport Placeholder from '@tiptap/extension-placeholder';\nimport Subscript from '@tiptap/extension-subscript';\nimport Superscript from '@tiptap/extension-superscript';\nimport TextAlign from '@tiptap/extension-text-align';\nimport { TextStyle } from '@tiptap/extension-text-style';\nimport Underline from '@tiptap/extension-underline';\nimport StarterKit from '@tiptap/starter-kit';\nimport type { ReactNode } from 'react';\nimport { isAllowedLinkUrl } from '../../helpers/sanitize';\nimport type {\n\tEditorFeatures,\n\tEditorSnapshot,\n\tKeyboardShortcut,\n\tMentionDropdownRenderProps,\n\tMentionItem,\n\tPasteData,\n\tSlashCommandDropdownRenderProps,\n\tSlashCommandItem,\n} from '../BikEditor.types';\nimport { CharacterLimitExtension } from './characterLimit/CharacterLimitExtension';\nimport { FontSizeExtension } from './FontSizeExtension';\nimport { JsonHighlightExtension } from './jsonHighlight/JsonHighlightExtension';\nimport {\n\tbuildAgentMentionExtension,\n\tbuildTeamMentionExtension,\n} from './mention/MentionExtension';\nimport { PasteExtension } from './paste/PasteExtension';\nimport { PasteNormalizationExtension } from './plainClipboard/PasteNormalizationExtension';\nimport { SectionDividerNode } from './sectionDivider/SectionDividerNode';\nimport { SendShortcutExtension } from './sendShortcut/SendShortcutExtension';\nimport { buildSlashCommandExtension } from './slashCommand/SlashCommandExtension';\nimport { VariableDecorationExtension } from './variable/VariableDecorationExtension';\n\ninterface ExtensionConfig {\n\tfeatures?: EditorFeatures;\n\tplaceholder?: string;\n\tmaxCharacters?: number;\n\t/**\n\t * Soft character limit. Unlike `maxCharacters` (which hard-blocks all input),\n\t * this blocks typing past the limit but ALLOWS paste to overflow, rendering\n\t * the excess characters greyed (`.bik-editor-overflow`). Purely visual +\n\t * typing-guard; the consumer shows the count/error and gates send itself.\n\t */\n\tcharacterLimit?: number;\n\thasSections?: boolean;\n\tmentions?: {\n\t\tagents?: { current: MentionItem[] };\n\t\tteams?: { current: MentionItem[] };\n\t};\n\tslashCommands?: { current: SlashCommandItem[] };\n\tonPaste?: (data: PasteData) => boolean | void;\n\tonSend?: (content: EditorSnapshot) => void;\n\tsendShortcut?:\n\t\t| { key: string; modifiers?: Array<'mod' | 'ctrl' | 'shift' | 'alt'> }\n\t\t| Array<{\n\t\t\t\tkey: string;\n\t\t\t\tmodifiers?: Array<'mod' | 'ctrl' | 'shift' | 'alt'>;\n\t\t }>;\n\tshortcuts?: KeyboardShortcut[];\n\tonMentionSelected?: (item: MentionItem, char: '@' | '#') => void;\n\tonSlashCommandSelected?: (command: SlashCommandItem) => void;\n\trenderMentionItem?: (item: MentionItem, isActive: boolean) => ReactNode;\n\trenderMentionDropdown?: (props: MentionDropdownRenderProps) => ReactNode;\n\tremoveMentionTriggerOnDismiss?: boolean;\n\trenderSlashCommandItem?: (\n\t\titem: SlashCommandItem,\n\t\tisActive: boolean,\n\t) => ReactNode;\n\trenderSlashCommandDropdown?: (\n\t\tprops: SlashCommandDropdownRenderProps,\n\t) => ReactNode;\n}\n\nexport function buildExtensions(config: ExtensionConfig) {\n\t// JSON body mode is a dedicated code editor — it deliberately skips the rich\n\t// text stack (mentions, typography, rich paste, sections) and turns the whole\n\t// document into one syntax-highlighted JSON code block, Postman-style.\n\tif (config.features?.jsonMode) {\n\t\treturn [\n\t\t\tStarterKit.configure({\n\t\t\t\tlink: false,\n\t\t\t\tunderline: false,\n\t\t\t\t// Keep codeBlock (the JSON container) and history; drop everything\n\t\t\t\t// that has no meaning inside raw JSON.\n\t\t\t\tbold: false,\n\t\t\t\titalic: false,\n\t\t\t\tstrike: false,\n\t\t\t\tcode: false,\n\t\t\t\theading: false,\n\t\t\t\tbulletList: false,\n\t\t\t\torderedList: false,\n\t\t\t\tblockquote: false,\n\t\t\t\thorizontalRule: false,\n\t\t\t}),\n\t\t\tPlaceholder.configure({\n\t\t\t\tplaceholder: config.placeholder ?? '{\\n \\n}',\n\t\t\t}),\n\t\t\tJsonHighlightExtension,\n\t\t\tSendShortcutExtension.configure({\n\t\t\t\tonSend: config.onSend,\n\t\t\t\tsendShortcut: config.sendShortcut,\n\t\t\t\textraShortcuts: config.shortcuts ?? [],\n\t\t\t}),\n\t\t\t...(config.maxCharacters\n\t\t\t\t? [CharacterCount.configure({ limit: config.maxCharacters })]\n\t\t\t\t: []),\n\t\t];\n\t}\n\n\tconst hasRichPaste = config.features?.richPaste ?? false;\n\tconst hasRichTypography = config.features?.richTypography ?? false;\n\n\t// When allowedMarks is specified only those marks are registered in the schema.\n\t// This disables rendering, keyboard shortcuts AND paste-preservation for the\n\t// excluded marks — all three come for free when the extension is absent.\n\tconst allowedMarks = config.features?.allowedMarks;\n\tconst isMark = (m: 'bold' | 'italic' | 'strike' | 'underline' | 'code') =>\n\t\t!allowedMarks || allowedMarks.includes(m);\n\n\t// Soft line break. Shift-Enter inserts a <br> inside the current block\n\t// (matches Gmail); plain Enter still splits the block into a new paragraph\n\t// via StarterKit's default. HardBreak is registered standalone — like Link\n\t// and Underline above — instead of through StarterKit, so we can bind ONLY\n\t// Shift-Enter here and leave Mod-Enter free for the send shortcut.\n\tconst softBreak = HardBreak.extend({\n\t\taddKeyboardShortcuts() {\n\t\t\treturn {\n\t\t\t\t'Shift-Enter': () => this.editor.commands.setHardBreak(),\n\t\t\t};\n\t\t},\n\t});\n\n\t// Paragraph that preserves a zero block-margin from the source (Gmail-style).\n\t// Most sources (ChatGPT, typed text) use plain <p> → no margin attr → the\n\t// editor/viewer default 1em margin provides the gap. Google-Docs-style\n\t// content uses margin:0 paragraphs and a separate blank-line paragraph for\n\t// spacing; we keep that margin:0 so the blank line — not stacked margins —\n\t// provides a single-line gap, exactly like Gmail. The paste pipeline also\n\t// sets margin:'0' on every paragraph when a paste contains blank-line\n\t// paragraphs (see applyParagraphSpacing), so spacing comes from EITHER the\n\t// margin OR the blank line, never both (no double gap).\n\tconst isZeroLen = (v: string) =>\n\t\tv === '0' || v === '0px' || v === '0pt' || v === '0em';\n\tconst ParagraphWithMargin = Paragraph.extend({\n\t\taddAttributes() {\n\t\t\treturn {\n\t\t\t\t...(this.parent?.() ?? {}),\n\t\t\t\tmargin: {\n\t\t\t\t\tdefault: null,\n\t\t\t\t\tparseHTML: (el: HTMLElement) =>\n\t\t\t\t\t\tisZeroLen(el.style.marginTop) && isZeroLen(el.style.marginBottom)\n\t\t\t\t\t\t\t? '0'\n\t\t\t\t\t\t\t: null,\n\t\t\t\t\trenderHTML: (attrs: { margin?: string | null }) =>\n\t\t\t\t\t\tattrs.margin === '0' ? { style: 'margin: 0px' } : {},\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t});\n\n\tconst base = [\n\t\t// Exclude Link and Underline from StarterKit — TipTap v3 bundles both.\n\t\t// Without this, two copies of each are registered and the StarterKit copy's\n\t\t// click handler calls window.open even when openOnClick: false is set.\n\t\tStarterKit.configure({\n\t\t\tlink: false,\n\t\t\tunderline: false,\n\t\t\thardBreak: false,\n\t\t\tparagraph: false,\n\t\t\tbold: isMark('bold') ? {} : false,\n\t\t\titalic: isMark('italic') ? {} : false,\n\t\t\tstrike: isMark('strike') ? {} : false,\n\t\t\tcode: isMark('code') ? {} : false,\n\t\t}),\n\t\tParagraphWithMargin,\n\t\tsoftBreak,\n\t\t...(isMark('underline') ? [Underline] : []),\n\t\t// Extend Link to strip addPasteRules() and addInputRules() so pasting a\n\t\t// URL never creates a hyperlink regardless of autolink/linkOnPaste values.\n\t\tLink.extend({\n\t\t\taddPasteRules: () => [],\n\t\t\taddInputRules: () => [],\n\t\t}).configure({\n\t\t\topenOnClick: false,\n\t\t\tautolink: false,\n\t\t\tlinkOnPaste: false,\n\t\t\t// Block javascript:/data:/vbscript: hrefs. TipTap keeps the href\n\t\t\t// attribute verbatim, so scheme validation must live here.\n\t\t\tisAllowedUri: (url) => isAllowedLinkUrl(url),\n\t\t\tHTMLAttributes: {\n\t\t\t\trel: 'noopener noreferrer',\n\t\t\t\tclass: 'bik-link',\n\t\t\t},\n\t\t}),\n\t\tTextStyle,\n\t\tPlaceholder.configure({\n\t\t\tplaceholder: config.placeholder ?? 'Type a message...',\n\t\t}),\n\t\t// Consumer paste callback runs first. If it returns true the event is\n\t\t// consumed and neither PlainClipboardExtension nor the editor's default\n\t\t// paste handling will run for that event.\n\t\t...(config.onPaste\n\t\t\t? [PasteExtension.configure({ onPaste: config.onPaste })]\n\t\t\t: []),\n\t\tPasteNormalizationExtension.configure({\n\t\t\tpreserveMarks: hasRichPaste,\n\t\t\t// Only email + signature (richPaste) keep the rich HTML paste pipeline.\n\t\t\t// Every other channel (WhatsApp, IG, FB, comments) forces plain-text\n\t\t\t// paste for BOTH Cmd/Ctrl+V and Cmd/Ctrl+Shift+V — no formatting.\n\t\t\tplainOnly: !hasRichPaste,\n\t\t}),\n\t\tSendShortcutExtension.configure({\n\t\t\tonSend: config.onSend,\n\t\t\tsendShortcut: config.sendShortcut,\n\t\t\textraShortcuts: config.shortcuts ?? [],\n\t\t}),\n\t\tVariableDecorationExtension,\n\t\t...(config.maxCharacters\n\t\t\t? [CharacterCount.configure({ limit: config.maxCharacters })]\n\t\t\t: []),\n\t\t...(config.characterLimit\n\t\t\t? [CharacterLimitExtension.configure({ limit: config.characterLimit })]\n\t\t\t: []),\n\t\t// Always register SectionDividerNode so the custom <div data-section-divider>\n\t\t// node is part of the schema in every editor instance. This means imperative\n\t\t// callers (setBodyAndSections / setSectionContent) work even when no\n\t\t// `sections` prop was provided at mount time.\n\t\tSectionDividerNode,\n\t];\n\n\tconst mentionExtensions = [\n\t\t...(config.mentions?.agents\n\t\t\t? [\n\t\t\t\t\tbuildAgentMentionExtension(\n\t\t\t\t\t\tconfig.mentions.agents,\n\t\t\t\t\t\tconfig.onMentionSelected,\n\t\t\t\t\t\tconfig.renderMentionItem,\n\t\t\t\t\t\tconfig.renderMentionDropdown,\n\t\t\t\t\t\tconfig.removeMentionTriggerOnDismiss,\n\t\t\t\t\t),\n\t\t\t ]\n\t\t\t: []),\n\t\t...(config.mentions?.teams\n\t\t\t? [\n\t\t\t\t\tbuildTeamMentionExtension(\n\t\t\t\t\t\tconfig.mentions.teams,\n\t\t\t\t\t\tconfig.onMentionSelected,\n\t\t\t\t\t\tconfig.renderMentionItem,\n\t\t\t\t\t\tconfig.renderMentionDropdown,\n\t\t\t\t\t\tconfig.removeMentionTriggerOnDismiss,\n\t\t\t\t\t),\n\t\t\t ]\n\t\t\t: []),\n\t\t...(config.slashCommands\n\t\t\t? [\n\t\t\t\t\tbuildSlashCommandExtension(\n\t\t\t\t\t\tconfig.slashCommands,\n\t\t\t\t\t\tconfig.onSlashCommandSelected,\n\t\t\t\t\t\tconfig.renderSlashCommandItem,\n\t\t\t\t\t\tconfig.renderSlashCommandDropdown,\n\t\t\t\t\t),\n\t\t\t ]\n\t\t\t: []),\n\t];\n\n\tconst richExtensions = hasRichTypography\n\t\t? [\n\t\t\t\tColor,\n\t\t\t\tHighlight.configure({ multicolor: true }),\n\t\t\t\tFontFamily,\n\t\t\t\tFontSizeExtension,\n\t\t\t\tTextAlign.configure({ types: ['heading', 'paragraph'] }),\n\t\t\t\tSubscript,\n\t\t\t\tSuperscript,\n\t\t\t\tImage,\n\t\t ]\n\t\t: [Color];\n\n\treturn [...base, ...mentionExtensions, ...richExtensions];\n}\n"],"names":["buildExtensions","config","StarterKit","Placeholder","JsonHighlightExtension","SendShortcutExtension","CharacterCount","hasRichPaste","hasRichTypography","allowedMarks","isMark","m","softBreak","HardBreak","isZeroLen","v","ParagraphWithMargin","Paragraph","el","attrs","base","Underline","Link","url","isAllowedLinkUrl","TextStyle","PasteExtension","PasteNormalizationExtension","VariableDecorationExtension","CharacterLimitExtension","SectionDividerNode","mentionExtensions","buildAgentMentionExtension","buildTeamMentionExtension","buildSlashCommandExtension","richExtensions","Color","Highlight","FontFamily","FontSizeExtension","TextAlign","Subscript","Superscript","Image"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAiFO,SAASA,GAAgBC,GAAyB;AAIxD,MAAIA,EAAO,UAAU;AACpB,WAAO;AAAA,MACNC,EAAW,UAAU;AAAA,QACpB,MAAM;AAAA,QACN,WAAW;AAAA;AAAA;AAAA,QAGX,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,aAAa;AAAA,QACb,YAAY;AAAA,QACZ,gBAAgB;AAAA,MAAA,CAChB;AAAA,MACDC,EAAY,UAAU;AAAA,QACrB,aAAaF,EAAO,eAAe;AAAA;AAAA;AAAA,MAAA,CACnC;AAAA,MACDG;AAAA,MACAC,EAAsB,UAAU;AAAA,QAC/B,QAAQJ,EAAO;AAAA,QACf,cAAcA,EAAO;AAAA,QACrB,gBAAgBA,EAAO,aAAa,CAAA;AAAA,MAAC,CACrC;AAAA,MACD,GAAIA,EAAO,gBACR,CAACK,EAAe,UAAU,EAAE,OAAOL,EAAO,cAAA,CAAe,CAAC,IAC1D,CAAA;AAAA,IAAC;AAIN,QAAMM,IAAeN,EAAO,UAAU,aAAa,IAC7CO,IAAoBP,EAAO,UAAU,kBAAkB,IAKvDQ,IAAeR,EAAO,UAAU,cAChCS,IAAS,CAACC,MACf,CAACF,KAAgBA,EAAa,SAASE,CAAC,GAOnCC,IAAYC,EAAU,OAAO;AAAA,IAClC,uBAAuB;AACtB,aAAO;AAAA,QACN,eAAe,MAAM,KAAK,OAAO,SAAS,aAAA;AAAA,MAAa;AAAA,IAEzD;AAAA,EAAA,CACA,GAWKC,IAAY,CAACC,MAClBA,MAAM,OAAOA,MAAM,SAASA,MAAM,SAASA,MAAM,OAC5CC,IAAsBC,EAAU,OAAO;AAAA,IAC5C,gBAAgB;AACf,aAAO;AAAA,QACN,GAAI,KAAK,SAAA,KAAc,CAAA;AAAA,QACvB,QAAQ;AAAA,UACP,SAAS;AAAA,UACT,WAAW,CAACC,MACXJ,EAAUI,EAAG,MAAM,SAAS,KAAKJ,EAAUI,EAAG,MAAM,YAAY,IAC7D,MACA;AAAA,UACJ,YAAY,CAACC,MACZA,EAAM,WAAW,MAAM,EAAE,OAAO,kBAAkB,CAAA;AAAA,QAAC;AAAA,MACrD;AAAA,IAEF;AAAA,EAAA,CACA,GAEKC,IAAO;AAAA;AAAA;AAAA;AAAA,IAIZlB,EAAW,UAAU;AAAA,MACpB,MAAM;AAAA,MACN,WAAW;AAAA,MACX,WAAW;AAAA,MACX,WAAW;AAAA,MACX,MAAMQ,EAAO,MAAM,IAAI,CAAA,IAAK;AAAA,MAC5B,QAAQA,EAAO,QAAQ,IAAI,CAAA,IAAK;AAAA,MAChC,QAAQA,EAAO,QAAQ,IAAI,CAAA,IAAK;AAAA,MAChC,MAAMA,EAAO,MAAM,IAAI,KAAK;AAAA,IAAA,CAC5B;AAAA,IACDM;AAAA,IACAJ;AAAA,IACA,GAAIF,EAAO,WAAW,IAAI,CAACW,CAAS,IAAI,CAAA;AAAA;AAAA;AAAA,IAGxCC,EAAK,OAAO;AAAA,MACX,eAAe,MAAM,CAAA;AAAA,MACrB,eAAe,MAAM,CAAA;AAAA,IAAC,CACtB,EAAE,UAAU;AAAA,MACZ,aAAa;AAAA,MACb,UAAU;AAAA,MACV,aAAa;AAAA;AAAA;AAAA,MAGb,cAAc,CAACC,MAAQC,EAAiBD,CAAG;AAAA,MAC3C,gBAAgB;AAAA,QACf,KAAK;AAAA,QACL,OAAO;AAAA,MAAA;AAAA,IACR,CACA;AAAA,IACDE;AAAA,IACAtB,EAAY,UAAU;AAAA,MACrB,aAAaF,EAAO,eAAe;AAAA,IAAA,CACnC;AAAA;AAAA;AAAA;AAAA,IAID,GAAIA,EAAO,UACR,CAACyB,EAAe,UAAU,EAAE,SAASzB,EAAO,SAAS,CAAC,IACtD,CAAA;AAAA,IACH0B,EAA4B,UAAU;AAAA,MACrC,eAAepB;AAAA;AAAA;AAAA;AAAA,MAIf,WAAW,CAACA;AAAA,IAAA,CACZ;AAAA,IACDF,EAAsB,UAAU;AAAA,MAC/B,QAAQJ,EAAO;AAAA,MACf,cAAcA,EAAO;AAAA,MACrB,gBAAgBA,EAAO,aAAa,CAAA;AAAA,IAAC,CACrC;AAAA,IACD2B;AAAA,IACA,GAAI3B,EAAO,gBACR,CAACK,EAAe,UAAU,EAAE,OAAOL,EAAO,eAAe,CAAC,IAC1D,CAAA;AAAA,IACH,GAAIA,EAAO,iBACR,CAAC4B,EAAwB,UAAU,EAAE,OAAO5B,EAAO,gBAAgB,CAAC,IACpE,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAKH6B;AAAA,EAAA,GAGKC,IAAoB;AAAA,IACzB,GAAI9B,EAAO,UAAU,SAClB;AAAA,MACA+B;AAAA,QACC/B,EAAO,SAAS;AAAA,QAChBA,EAAO;AAAA,QACPA,EAAO;AAAA,QACPA,EAAO;AAAA,QACPA,EAAO;AAAA,MAAA;AAAA,IACR,IAEA,CAAA;AAAA,IACH,GAAIA,EAAO,UAAU,QAClB;AAAA,MACAgC;AAAA,QACChC,EAAO,SAAS;AAAA,QAChBA,EAAO;AAAA,QACPA,EAAO;AAAA,QACPA,EAAO;AAAA,QACPA,EAAO;AAAA,MAAA;AAAA,IACR,IAEA,CAAA;AAAA,IACH,GAAIA,EAAO,gBACR;AAAA,MACAiC;AAAA,QACCjC,EAAO;AAAA,QACPA,EAAO;AAAA,QACPA,EAAO;AAAA,QACPA,EAAO;AAAA,MAAA;AAAA,IACR,IAEA,CAAA;AAAA,EAAC,GAGCkC,IAAiB3B,IACpB;AAAA,IACA4B;AAAA,IACAC,EAAU,UAAU,EAAE,YAAY,IAAM;AAAA,IACxCC;AAAA,IACAC;AAAA,IACAC,EAAU,UAAU,EAAE,OAAO,CAAC,WAAW,WAAW,GAAG;AAAA,IACvDC;AAAA,IACAC;AAAA,IACAC;AAAA,EAAA,IAEA,CAACP,CAAK;AAET,SAAO,CAAC,GAAGhB,GAAM,GAAGW,GAAmB,GAAGI,CAAc;AACzD;"}
1
+ {"version":3,"file":"buildExtensions.js","sources":["../../../../src/editor/extensions/buildExtensions.ts"],"sourcesContent":["import CharacterCount from '@tiptap/extension-character-count';\nimport Color from '@tiptap/extension-color';\nimport FontFamily from '@tiptap/extension-font-family';\nimport HardBreak from '@tiptap/extension-hard-break';\nimport Highlight from '@tiptap/extension-highlight';\nimport Image from '@tiptap/extension-image';\nimport Link from '@tiptap/extension-link';\nimport Paragraph from '@tiptap/extension-paragraph';\nimport Placeholder from '@tiptap/extension-placeholder';\nimport Subscript from '@tiptap/extension-subscript';\nimport Superscript from '@tiptap/extension-superscript';\nimport TextAlign from '@tiptap/extension-text-align';\nimport { TextStyle } from '@tiptap/extension-text-style';\nimport Underline from '@tiptap/extension-underline';\nimport StarterKit from '@tiptap/starter-kit';\nimport type { ReactNode } from 'react';\nimport { isAllowedLinkUrl } from '../../helpers/sanitize';\nimport type {\n\tEditorFeatures,\n\tEditorSnapshot,\n\tKeyboardShortcut,\n\tMentionDropdownRenderProps,\n\tMentionItem,\n\tPasteData,\n\tSlashCommandDropdownRenderProps,\n\tSlashCommandItem,\n} from '../BikEditor.types';\nimport { CharacterLimitExtension } from './characterLimit/CharacterLimitExtension';\nimport { FontSizeExtension } from './FontSizeExtension';\nimport { HtmlTagStripExtension } from './htmlTagStrip/HtmlTagStripExtension';\nimport { JsonHighlightExtension } from './jsonHighlight/JsonHighlightExtension';\nimport {\n\tbuildAgentMentionExtension,\n\tbuildTeamMentionExtension,\n} from './mention/MentionExtension';\nimport { PasteExtension } from './paste/PasteExtension';\nimport { PasteNormalizationExtension } from './plainClipboard/PasteNormalizationExtension';\nimport { SectionDividerNode } from './sectionDivider/SectionDividerNode';\nimport { SendShortcutExtension } from './sendShortcut/SendShortcutExtension';\nimport { buildSlashCommandExtension } from './slashCommand/SlashCommandExtension';\nimport { VariableDecorationExtension } from './variable/VariableDecorationExtension';\n\ninterface ExtensionConfig {\n\tfeatures?: EditorFeatures;\n\tplaceholder?: string;\n\tmaxCharacters?: number;\n\t/**\n\t * Soft character limit. Unlike `maxCharacters` (which hard-blocks all input),\n\t * this blocks typing past the limit but ALLOWS paste to overflow, rendering\n\t * the excess characters greyed (`.bik-editor-overflow`). Purely visual +\n\t * typing-guard; the consumer shows the count/error and gates send itself.\n\t */\n\tcharacterLimit?: number;\n\thasSections?: boolean;\n\tmentions?: {\n\t\tagents?: { current: MentionItem[] };\n\t\tteams?: { current: MentionItem[] };\n\t};\n\tslashCommands?: { current: SlashCommandItem[] };\n\tonPaste?: (data: PasteData) => boolean | void;\n\tonSend?: (content: EditorSnapshot) => void;\n\tsendShortcut?:\n\t\t| { key: string; modifiers?: Array<'mod' | 'ctrl' | 'shift' | 'alt'> }\n\t\t| Array<{\n\t\t\t\tkey: string;\n\t\t\t\tmodifiers?: Array<'mod' | 'ctrl' | 'shift' | 'alt'>;\n\t\t }>;\n\tshortcuts?: KeyboardShortcut[];\n\tonMentionSelected?: (item: MentionItem, char: '@' | '#') => void;\n\tonSlashCommandSelected?: (command: SlashCommandItem) => void;\n\trenderMentionItem?: (item: MentionItem, isActive: boolean) => ReactNode;\n\trenderMentionDropdown?: (props: MentionDropdownRenderProps) => ReactNode;\n\tremoveMentionTriggerOnDismiss?: boolean;\n\trenderSlashCommandItem?: (\n\t\titem: SlashCommandItem,\n\t\tisActive: boolean,\n\t) => ReactNode;\n\trenderSlashCommandDropdown?: (\n\t\tprops: SlashCommandDropdownRenderProps,\n\t) => ReactNode;\n}\n\nexport function buildExtensions(config: ExtensionConfig) {\n\t// JSON body mode is a dedicated code editor — it deliberately skips the rich\n\t// text stack (mentions, typography, rich paste, sections) and turns the whole\n\t// document into one syntax-highlighted JSON code block, Postman-style.\n\tif (config.features?.jsonMode) {\n\t\treturn [\n\t\t\tStarterKit.configure({\n\t\t\t\tlink: false,\n\t\t\t\tunderline: false,\n\t\t\t\t// Keep codeBlock (the JSON container) and history; drop everything\n\t\t\t\t// that has no meaning inside raw JSON.\n\t\t\t\tbold: false,\n\t\t\t\titalic: false,\n\t\t\t\tstrike: false,\n\t\t\t\tcode: false,\n\t\t\t\theading: false,\n\t\t\t\tbulletList: false,\n\t\t\t\torderedList: false,\n\t\t\t\tblockquote: false,\n\t\t\t\thorizontalRule: false,\n\t\t\t}),\n\t\t\tPlaceholder.configure({\n\t\t\t\tplaceholder: config.placeholder ?? '{\\n \\n}',\n\t\t\t}),\n\t\t\tJsonHighlightExtension,\n\t\t\tSendShortcutExtension.configure({\n\t\t\t\tonSend: config.onSend,\n\t\t\t\tsendShortcut: config.sendShortcut,\n\t\t\t\textraShortcuts: config.shortcuts ?? [],\n\t\t\t}),\n\t\t\t...(config.maxCharacters\n\t\t\t\t? [CharacterCount.configure({ limit: config.maxCharacters })]\n\t\t\t\t: []),\n\t\t];\n\t}\n\n\tconst hasRichPaste = config.features?.richPaste ?? false;\n\tconst hasRichTypography = config.features?.richTypography ?? false;\n\n\t// When allowedMarks is specified only those marks are registered in the schema.\n\t// This disables rendering, keyboard shortcuts AND paste-preservation for the\n\t// excluded marks — all three come for free when the extension is absent.\n\tconst allowedMarks = config.features?.allowedMarks;\n\tconst isMark = (m: 'bold' | 'italic' | 'strike' | 'underline' | 'code') =>\n\t\t!allowedMarks || allowedMarks.includes(m);\n\n\t// Soft line break. Shift-Enter inserts a <br> inside the current block\n\t// (matches Gmail); plain Enter still splits the block into a new paragraph\n\t// via StarterKit's default. HardBreak is registered standalone — like Link\n\t// and Underline above — instead of through StarterKit, so we can bind ONLY\n\t// Shift-Enter here and leave Mod-Enter free for the send shortcut.\n\tconst softBreak = HardBreak.extend({\n\t\taddKeyboardShortcuts() {\n\t\t\treturn {\n\t\t\t\t'Shift-Enter': () => this.editor.commands.setHardBreak(),\n\t\t\t};\n\t\t},\n\t});\n\n\t// Paragraph that preserves a zero block-margin from the source (Gmail-style).\n\t// Most sources (ChatGPT, typed text) use plain <p> → no margin attr → the\n\t// editor/viewer default 1em margin provides the gap. Google-Docs-style\n\t// content uses margin:0 paragraphs and a separate blank-line paragraph for\n\t// spacing; we keep that margin:0 so the blank line — not stacked margins —\n\t// provides a single-line gap, exactly like Gmail. The paste pipeline also\n\t// sets margin:'0' on every paragraph when a paste contains blank-line\n\t// paragraphs (see applyParagraphSpacing), so spacing comes from EITHER the\n\t// margin OR the blank line, never both (no double gap).\n\tconst isZeroLen = (v: string) =>\n\t\tv === '0' || v === '0px' || v === '0pt' || v === '0em';\n\tconst ParagraphWithMargin = Paragraph.extend({\n\t\taddAttributes() {\n\t\t\treturn {\n\t\t\t\t...(this.parent?.() ?? {}),\n\t\t\t\tmargin: {\n\t\t\t\t\tdefault: null,\n\t\t\t\t\tparseHTML: (el: HTMLElement) =>\n\t\t\t\t\t\tisZeroLen(el.style.marginTop) && isZeroLen(el.style.marginBottom)\n\t\t\t\t\t\t\t? '0'\n\t\t\t\t\t\t\t: null,\n\t\t\t\t\trenderHTML: (attrs: { margin?: string | null }) =>\n\t\t\t\t\t\tattrs.margin === '0' ? { style: 'margin: 0px' } : {},\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t});\n\n\tconst base = [\n\t\t// Exclude Link and Underline from StarterKit — TipTap v3 bundles both.\n\t\t// Without this, two copies of each are registered and the StarterKit copy's\n\t\t// click handler calls window.open even when openOnClick: false is set.\n\t\tStarterKit.configure({\n\t\t\tlink: false,\n\t\t\tunderline: false,\n\t\t\thardBreak: false,\n\t\t\tparagraph: false,\n\t\t\tbold: isMark('bold') ? {} : false,\n\t\t\titalic: isMark('italic') ? {} : false,\n\t\t\tstrike: isMark('strike') ? {} : false,\n\t\t\tcode: isMark('code') ? {} : false,\n\t\t}),\n\t\tParagraphWithMargin,\n\t\tsoftBreak,\n\t\t...(isMark('underline') ? [Underline] : []),\n\t\t// Extend Link to strip addPasteRules() and addInputRules() so pasting a\n\t\t// URL never creates a hyperlink regardless of autolink/linkOnPaste values.\n\t\tLink.extend({\n\t\t\taddPasteRules: () => [],\n\t\t\taddInputRules: () => [],\n\t\t}).configure({\n\t\t\topenOnClick: false,\n\t\t\tautolink: false,\n\t\t\tlinkOnPaste: false,\n\t\t\t// Block javascript:/data:/vbscript: hrefs. TipTap keeps the href\n\t\t\t// attribute verbatim, so scheme validation must live here.\n\t\t\tisAllowedUri: (url) => isAllowedLinkUrl(url),\n\t\t\tHTMLAttributes: {\n\t\t\t\trel: 'noopener noreferrer',\n\t\t\t\tclass: 'bik-link',\n\t\t\t},\n\t\t}),\n\t\tTextStyle,\n\t\tPlaceholder.configure({\n\t\t\tplaceholder: config.placeholder ?? 'Type a message...',\n\t\t}),\n\t\t// Consumer paste callback runs first. If it returns true the event is\n\t\t// consumed and neither PlainClipboardExtension nor the editor's default\n\t\t// paste handling will run for that event.\n\t\t...(config.onPaste\n\t\t\t? [PasteExtension.configure({ onPaste: config.onPaste })]\n\t\t\t: []),\n\t\tPasteNormalizationExtension.configure({\n\t\t\tpreserveMarks: hasRichPaste,\n\t\t\t// Only email + signature (richPaste) keep the rich HTML paste pipeline.\n\t\t\t// Every other channel (WhatsApp, IG, FB, comments) forces plain-text\n\t\t\t// paste for BOTH Cmd/Ctrl+V and Cmd/Ctrl+Shift+V — no formatting.\n\t\t\tplainOnly: !hasRichPaste,\n\t\t}),\n\t\tSendShortcutExtension.configure({\n\t\t\tonSend: config.onSend,\n\t\t\tsendShortcut: config.sendShortcut,\n\t\t\textraShortcuts: config.shortcuts ?? [],\n\t\t}),\n\t\tVariableDecorationExtension,\n\t\t// Strip complete HTML tags from typed text in non-email channels.\n\t\t// Email (richPaste) keeps tags because the editor legitimately renders HTML.\n\t\t...(!hasRichPaste ? [HtmlTagStripExtension] : []),\n\t\t...(config.maxCharacters\n\t\t\t? [CharacterCount.configure({ limit: config.maxCharacters })]\n\t\t\t: []),\n\t\t...(config.characterLimit\n\t\t\t? [CharacterLimitExtension.configure({ limit: config.characterLimit })]\n\t\t\t: []),\n\t\t// Always register SectionDividerNode so the custom <div data-section-divider>\n\t\t// node is part of the schema in every editor instance. This means imperative\n\t\t// callers (setBodyAndSections / setSectionContent) work even when no\n\t\t// `sections` prop was provided at mount time.\n\t\tSectionDividerNode,\n\t];\n\n\tconst mentionExtensions = [\n\t\t...(config.mentions?.agents\n\t\t\t? [\n\t\t\t\t\tbuildAgentMentionExtension(\n\t\t\t\t\t\tconfig.mentions.agents,\n\t\t\t\t\t\tconfig.onMentionSelected,\n\t\t\t\t\t\tconfig.renderMentionItem,\n\t\t\t\t\t\tconfig.renderMentionDropdown,\n\t\t\t\t\t\tconfig.removeMentionTriggerOnDismiss,\n\t\t\t\t\t),\n\t\t\t ]\n\t\t\t: []),\n\t\t...(config.mentions?.teams\n\t\t\t? [\n\t\t\t\t\tbuildTeamMentionExtension(\n\t\t\t\t\t\tconfig.mentions.teams,\n\t\t\t\t\t\tconfig.onMentionSelected,\n\t\t\t\t\t\tconfig.renderMentionItem,\n\t\t\t\t\t\tconfig.renderMentionDropdown,\n\t\t\t\t\t\tconfig.removeMentionTriggerOnDismiss,\n\t\t\t\t\t),\n\t\t\t ]\n\t\t\t: []),\n\t\t...(config.slashCommands\n\t\t\t? [\n\t\t\t\t\tbuildSlashCommandExtension(\n\t\t\t\t\t\tconfig.slashCommands,\n\t\t\t\t\t\tconfig.onSlashCommandSelected,\n\t\t\t\t\t\tconfig.renderSlashCommandItem,\n\t\t\t\t\t\tconfig.renderSlashCommandDropdown,\n\t\t\t\t\t),\n\t\t\t ]\n\t\t\t: []),\n\t];\n\n\tconst richExtensions = hasRichTypography\n\t\t? [\n\t\t\t\tColor,\n\t\t\t\tHighlight.configure({ multicolor: true }),\n\t\t\t\tFontFamily,\n\t\t\t\tFontSizeExtension,\n\t\t\t\tTextAlign.configure({ types: ['heading', 'paragraph'] }),\n\t\t\t\tSubscript,\n\t\t\t\tSuperscript,\n\t\t\t\tImage,\n\t\t ]\n\t\t: [Color];\n\n\treturn [...base, ...mentionExtensions, ...richExtensions];\n}\n"],"names":["buildExtensions","config","StarterKit","Placeholder","JsonHighlightExtension","SendShortcutExtension","CharacterCount","hasRichPaste","hasRichTypography","allowedMarks","isMark","m","softBreak","HardBreak","isZeroLen","v","ParagraphWithMargin","Paragraph","el","attrs","base","Underline","Link","url","isAllowedLinkUrl","TextStyle","PasteExtension","PasteNormalizationExtension","VariableDecorationExtension","HtmlTagStripExtension","CharacterLimitExtension","SectionDividerNode","mentionExtensions","buildAgentMentionExtension","buildTeamMentionExtension","buildSlashCommandExtension","richExtensions","Color","Highlight","FontFamily","FontSizeExtension","TextAlign","Subscript","Superscript","Image"],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { Extension } from '@tiptap/core';
2
+ /**
3
+ * Strips complete HTML tags from typed text in real time.
4
+ *
5
+ * Enabled for non-email channels (WhatsApp, IG, private note, etc.) where
6
+ * HTML has no meaning. When the user types `<h1>Hello</h1>`, the tags are
7
+ * removed as soon as the closing `>` completes each tag, leaving just `Hello`.
8
+ *
9
+ * Incomplete tags like `<h` are preserved (the regex requires a closing `>`).
10
+ * Non-tag angle brackets like `2 < 3` are also preserved.
11
+ *
12
+ * Uses `appendTransaction` so the strip happens in the same undo step as
13
+ * the keystroke — Cmd+Z undoes both the tag and the strip together.
14
+ */
15
+ export declare const HtmlTagStripExtension: Extension<any, any>;
@@ -0,0 +1,32 @@
1
+ import { Extension as m } from "../../../node_modules/@tiptap/core/dist/index.js";
2
+ import { Plugin as c } from "@tiptap/pm/state";
3
+ const r = /<\/?[a-zA-Z][a-zA-Z0-9]*(?:\s[^>]*)?\/?>/g, g = m.create({
4
+ name: "htmlTagStrip",
5
+ addProseMirrorPlugins() {
6
+ return [
7
+ new c({
8
+ appendTransaction(l, o, a) {
9
+ if (!l.some((t) => t.docChanged)) return null;
10
+ const { tr: e } = a;
11
+ let s = !1;
12
+ return a.doc.descendants((t, n) => {
13
+ if (!t.isText || !t.text || (r.lastIndex = 0, !r.test(t.text))) return;
14
+ r.lastIndex = 0;
15
+ const i = t.text.replace(r, "");
16
+ if (i === t.text) return;
17
+ const p = n + t.nodeSize;
18
+ i ? e.replaceWith(
19
+ e.mapping.map(n),
20
+ e.mapping.map(p),
21
+ a.schema.text(i, t.marks)
22
+ ) : e.delete(e.mapping.map(n), e.mapping.map(p)), s = !0;
23
+ }), s ? (e.setMeta("addToHistory", !1), e) : null;
24
+ }
25
+ })
26
+ ];
27
+ }
28
+ });
29
+ export {
30
+ g as HtmlTagStripExtension
31
+ };
32
+ //# sourceMappingURL=HtmlTagStripExtension.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HtmlTagStripExtension.js","sources":["../../../../../src/editor/extensions/htmlTagStrip/HtmlTagStripExtension.ts"],"sourcesContent":["import { Extension } from '@tiptap/core';\nimport { Plugin } from '@tiptap/pm/state';\n\nconst COMPLETE_TAG = /<\\/?[a-zA-Z][a-zA-Z0-9]*(?:\\s[^>]*)?\\/?>/g;\n\n/**\n * Strips complete HTML tags from typed text in real time.\n *\n * Enabled for non-email channels (WhatsApp, IG, private note, etc.) where\n * HTML has no meaning. When the user types `<h1>Hello</h1>`, the tags are\n * removed as soon as the closing `>` completes each tag, leaving just `Hello`.\n *\n * Incomplete tags like `<h` are preserved (the regex requires a closing `>`).\n * Non-tag angle brackets like `2 < 3` are also preserved.\n *\n * Uses `appendTransaction` so the strip happens in the same undo step as\n * the keystroke — Cmd+Z undoes both the tag and the strip together.\n */\nexport const HtmlTagStripExtension = Extension.create({\n\tname: 'htmlTagStrip',\n\taddProseMirrorPlugins() {\n\t\treturn [\n\t\t\tnew Plugin({\n\t\t\t\tappendTransaction(transactions, _oldState, newState) {\n\t\t\t\t\tif (!transactions.some((t) => t.docChanged)) return null;\n\n\t\t\t\t\tconst { tr } = newState;\n\t\t\t\t\tlet changed = false;\n\n\t\t\t\t\tnewState.doc.descendants((node, pos) => {\n\t\t\t\t\t\tif (!node.isText || !node.text) return;\n\t\t\t\t\t\tCOMPLETE_TAG.lastIndex = 0;\n\t\t\t\t\t\tif (!COMPLETE_TAG.test(node.text)) return;\n\n\t\t\t\t\t\tCOMPLETE_TAG.lastIndex = 0;\n\t\t\t\t\t\tconst stripped = node.text.replace(COMPLETE_TAG, '');\n\t\t\t\t\t\tif (stripped === node.text) return;\n\n\t\t\t\t\t\tconst end = pos + node.nodeSize;\n\t\t\t\t\t\tif (stripped) {\n\t\t\t\t\t\t\ttr.replaceWith(\n\t\t\t\t\t\t\t\ttr.mapping.map(pos),\n\t\t\t\t\t\t\t\ttr.mapping.map(end),\n\t\t\t\t\t\t\t\tnewState.schema.text(stripped, node.marks),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttr.delete(tr.mapping.map(pos), tr.mapping.map(end));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tchanged = true;\n\t\t\t\t\t});\n\n\t\t\t\t\tif (!changed) return null;\n\t\t\t\t\ttr.setMeta('addToHistory', false);\n\t\t\t\t\treturn tr;\n\t\t\t\t},\n\t\t\t}),\n\t\t];\n\t},\n});\n"],"names":["COMPLETE_TAG","HtmlTagStripExtension","Extension","Plugin","transactions","_oldState","newState","tr","changed","node","pos","stripped","end"],"mappings":";;AAGA,MAAMA,IAAe,6CAeRC,IAAwBC,EAAU,OAAO;AAAA,EACrD,MAAM;AAAA,EACN,wBAAwB;AACvB,WAAO;AAAA,MACN,IAAIC,EAAO;AAAA,QACV,kBAAkBC,GAAcC,GAAWC,GAAU;AACpD,cAAI,CAACF,EAAa,KAAK,CAAC,MAAM,EAAE,UAAU,EAAG,QAAO;AAEpD,gBAAM,EAAE,IAAAG,MAAOD;AACf,cAAIE,IAAU;AAwBd,iBAtBAF,EAAS,IAAI,YAAY,CAACG,GAAMC,MAAQ;AAGvC,gBAFI,CAACD,EAAK,UAAU,CAACA,EAAK,SAC1BT,EAAa,YAAY,GACrB,CAACA,EAAa,KAAKS,EAAK,IAAI,GAAG;AAEnC,YAAAT,EAAa,YAAY;AACzB,kBAAMW,IAAWF,EAAK,KAAK,QAAQT,GAAc,EAAE;AACnD,gBAAIW,MAAaF,EAAK,KAAM;AAE5B,kBAAMG,IAAMF,IAAMD,EAAK;AACvB,YAAIE,IACHJ,EAAG;AAAA,cACFA,EAAG,QAAQ,IAAIG,CAAG;AAAA,cAClBH,EAAG,QAAQ,IAAIK,CAAG;AAAA,cAClBN,EAAS,OAAO,KAAKK,GAAUF,EAAK,KAAK;AAAA,YAAA,IAG1CF,EAAG,OAAOA,EAAG,QAAQ,IAAIG,CAAG,GAAGH,EAAG,QAAQ,IAAIK,CAAG,CAAC,GAEnDJ,IAAU;AAAA,UACX,CAAC,GAEIA,KACLD,EAAG,QAAQ,gBAAgB,EAAK,GACzBA,KAFc;AAAA,QAGtB;AAAA,MAAA,CACA;AAAA,IAAA;AAAA,EAEH;AACD,CAAC;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bikdotai/bik-component-library",
3
- "version": "0.0.838-beta.3",
3
+ "version": "0.0.838-beta.4",
4
4
  "description": "Bik Component Library",
5
5
  "repository": {
6
6
  "type": "git",