@tetrascience-npm/tetrascience-react-ui 0.5.0-beta.40.1 → 0.5.0-beta.42.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.
Files changed (99) hide show
  1. package/dist/components/ai/attachments.cjs +2 -0
  2. package/dist/components/ai/attachments.cjs.map +1 -0
  3. package/dist/components/ai/attachments.js +224 -0
  4. package/dist/components/ai/attachments.js.map +1 -0
  5. package/dist/components/ai/chain-of-thought.cjs +2 -0
  6. package/dist/components/ai/chain-of-thought.cjs.map +1 -0
  7. package/dist/components/ai/chain-of-thought.js +145 -0
  8. package/dist/components/ai/chain-of-thought.js.map +1 -0
  9. package/dist/components/ai/confirmation.cjs +2 -0
  10. package/dist/components/ai/confirmation.cjs.map +1 -0
  11. package/dist/components/ai/confirmation.js +109 -0
  12. package/dist/components/ai/confirmation.js.map +1 -0
  13. package/dist/components/ai/context.cjs +2 -0
  14. package/dist/components/ai/context.cjs.map +1 -0
  15. package/dist/components/ai/context.js +266 -0
  16. package/dist/components/ai/context.js.map +1 -0
  17. package/dist/components/ai/conversation.cjs +4 -0
  18. package/dist/components/ai/conversation.cjs.map +1 -0
  19. package/dist/components/ai/conversation.js +108 -0
  20. package/dist/components/ai/conversation.js.map +1 -0
  21. package/dist/components/ai/inline-citation.cjs +2 -0
  22. package/dist/components/ai/inline-citation.cjs.map +1 -0
  23. package/dist/components/ai/inline-citation.js +182 -0
  24. package/dist/components/ai/inline-citation.js.map +1 -0
  25. package/dist/components/ai/message.cjs +2 -0
  26. package/dist/components/ai/message.cjs.map +1 -0
  27. package/dist/components/ai/message.js +237 -0
  28. package/dist/components/ai/message.js.map +1 -0
  29. package/dist/components/ai/model-selector.cjs +2 -0
  30. package/dist/components/ai/model-selector.cjs.map +1 -0
  31. package/dist/components/ai/model-selector.js +77 -0
  32. package/dist/components/ai/model-selector.js.map +1 -0
  33. package/dist/components/ai/prompt-input.cjs +2 -0
  34. package/dist/components/ai/prompt-input.cjs.map +1 -0
  35. package/dist/components/ai/prompt-input.js +774 -0
  36. package/dist/components/ai/prompt-input.js.map +1 -0
  37. package/dist/components/ai/queue.cjs +2 -0
  38. package/dist/components/ai/queue.cjs.map +1 -0
  39. package/dist/components/ai/queue.js +209 -0
  40. package/dist/components/ai/queue.js.map +1 -0
  41. package/dist/components/ai/reasoning.cjs +2 -0
  42. package/dist/components/ai/reasoning.cjs.map +1 -0
  43. package/dist/components/ai/reasoning.js +129 -0
  44. package/dist/components/ai/reasoning.js.map +1 -0
  45. package/dist/components/ai/shimmer.cjs +2 -0
  46. package/dist/components/ai/shimmer.cjs.map +1 -0
  47. package/dist/components/ai/shimmer.js +49 -0
  48. package/dist/components/ai/shimmer.js.map +1 -0
  49. package/dist/components/ai/sources.cjs +2 -0
  50. package/dist/components/ai/sources.cjs.map +1 -0
  51. package/dist/components/ai/sources.js +54 -0
  52. package/dist/components/ai/sources.js.map +1 -0
  53. package/dist/components/ai/speech-input.cjs +2 -0
  54. package/dist/components/ai/speech-input.cjs.map +1 -0
  55. package/dist/components/ai/speech-input.js +123 -0
  56. package/dist/components/ai/speech-input.js.map +1 -0
  57. package/dist/components/ai/stream-status.cjs +2 -0
  58. package/dist/components/ai/stream-status.cjs.map +1 -0
  59. package/dist/components/ai/stream-status.js +106 -0
  60. package/dist/components/ai/stream-status.js.map +1 -0
  61. package/dist/components/ai/suggestion.cjs +2 -0
  62. package/dist/components/ai/suggestion.cjs.map +1 -0
  63. package/dist/components/ai/suggestion.js +38 -0
  64. package/dist/components/ai/suggestion.js.map +1 -0
  65. package/dist/components/ai/task.cjs +2 -0
  66. package/dist/components/ai/task.cjs.map +1 -0
  67. package/dist/components/ai/task.js +94 -0
  68. package/dist/components/ai/task.js.map +1 -0
  69. package/dist/components/ai/tool.cjs +2 -0
  70. package/dist/components/ai/tool.cjs.map +1 -0
  71. package/dist/components/ai/tool.js +143 -0
  72. package/dist/components/ai/tool.js.map +1 -0
  73. package/dist/components/composed/Chat/Chat.cjs +2 -0
  74. package/dist/components/composed/Chat/Chat.cjs.map +1 -0
  75. package/dist/components/composed/Chat/Chat.js +167 -0
  76. package/dist/components/composed/Chat/Chat.js.map +1 -0
  77. package/dist/components/ui/code-block.cjs +4 -0
  78. package/dist/components/ui/code-block.cjs.map +1 -0
  79. package/dist/components/ui/code-block.js +306 -0
  80. package/dist/components/ui/code-block.js.map +1 -0
  81. package/dist/components/ui/data-table/data-table-filter.cjs +2 -0
  82. package/dist/components/ui/data-table/data-table-filter.cjs.map +1 -0
  83. package/dist/components/ui/data-table/data-table-filter.js +178 -0
  84. package/dist/components/ui/data-table/data-table-filter.js.map +1 -0
  85. package/dist/components/ui/data-table/data-table.cjs +1 -1
  86. package/dist/components/ui/data-table/data-table.cjs.map +1 -1
  87. package/dist/components/ui/data-table/data-table.js +244 -195
  88. package/dist/components/ui/data-table/data-table.js.map +1 -1
  89. package/dist/components/ui/progress.cjs +2 -0
  90. package/dist/components/ui/progress.cjs.map +1 -0
  91. package/dist/components/ui/progress.js +32 -0
  92. package/dist/components/ui/progress.js.map +1 -0
  93. package/dist/index.cjs +1 -1
  94. package/dist/index.css +1 -1
  95. package/dist/index.d.ts +1182 -1
  96. package/dist/index.js +572 -368
  97. package/dist/index.js.map +1 -1
  98. package/dist/index.tailwind.css +1 -1
  99. package/package.json +12 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Chat.js","sources":["../../../../src/components/composed/Chat/Chat.tsx"],"sourcesContent":["import { CopyIcon, GlobeIcon, RefreshCcwIcon } from \"lucide-react\"\nimport { useCallback, useId, useState } from \"react\"\n\nimport {\n Conversation,\n ConversationContent,\n ConversationEmptyState,\n ConversationScrollButton,\n} from \"@/components/ai/conversation\"\nimport {\n Message,\n MessageAction,\n MessageActions,\n MessageBranch,\n MessageBranchContent,\n MessageBranchNext,\n MessageBranchPage,\n MessageBranchPrevious,\n MessageBranchSelector,\n MessageContent,\n MessageResponse,\n MessageToolbar,\n} from \"@/components/ai/message\"\nimport {\n PromptInput,\n PromptInputBody,\n PromptInputButton,\n PromptInputFooter,\n PromptInputSelect,\n PromptInputSelectContent,\n PromptInputSelectItem,\n PromptInputSelectTrigger,\n PromptInputSelectValue,\n PromptInputSubmit,\n PromptInputTextarea,\n PromptInputTools,\n type PromptInputMessage,\n} from \"@/components/ai/prompt-input\"\nimport { Reasoning, ReasoningContent, ReasoningTrigger } from \"@/components/ai/reasoning\"\nimport { Source, Sources, SourcesContent, SourcesTrigger } from \"@/components/ai/sources\"\nimport { StreamStatus } from \"@/components/ai/stream-status\"\nimport { Suggestion, Suggestions } from \"@/components/ai/suggestion\"\nimport { cn } from \"@/lib/utils\"\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ChatSource {\n href: string\n title: string\n}\n\nexport interface ChatMessage {\n id: string\n role: \"user\" | \"assistant\"\n content: string\n /** Displayed in a collapsible Reasoning panel above the message content. */\n reasoning?: string\n /** Web sources used to produce this message. */\n sources?: ChatSource[]\n /** Multiple alternative responses (for branching). When provided, the\n * array should include `content` as one of its items. */\n branches?: string[]\n}\n\nexport interface ChatModel {\n id: string\n name: string\n}\n\nexport interface ChatProps {\n /** Initial messages to pre-populate the conversation. */\n initialMessages?: ChatMessage[]\n /** Available models shown in the model-selector dropdown. */\n models?: ChatModel[]\n /** Initial model id. Defaults to the first model in `models`. */\n defaultModel?: string\n /** Suggestion chips shown when the conversation is empty. */\n suggestions?: string[]\n /** Called when the user submits a message. The returned string (if any)\n * will be appended as the assistant's reply. Defaults to a no-op. */\n onSend?: (message: string, model: string) => Promise<string | undefined> | string | undefined\n className?: string\n}\n\n// ---------------------------------------------------------------------------\n// Default values\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_MODELS: ChatModel[] = [\n { id: \"claude-sonnet-4-6\", name: \"Claude Sonnet\" },\n { id: \"claude-opus-4-7\", name: \"Claude Opus\" },\n]\n\nconst DEFAULT_SUGGESTIONS = [\n \"Explain quantum entanglement simply\",\n \"Write a Python script to rename files\",\n \"Summarise the latest AI research trends\",\n \"What is the difference between TCP and UDP?\",\n]\n\nlet _idCounter = 0\nconst nextId = () => {\n _idCounter += 1\n return `chat-msg-${_idCounter}`\n}\n\nconst STREAM_STATUS_LINGER_MS = 3000\n\n// ---------------------------------------------------------------------------\n// Chat component\n// ---------------------------------------------------------------------------\n\nexport const Chat = ({\n initialMessages = [],\n models = DEFAULT_MODELS,\n defaultModel,\n suggestions = DEFAULT_SUGGESTIONS,\n onSend,\n className,\n}: ChatProps) => {\n const labelId = useId()\n const [messages, setMessages] = useState<ChatMessage[]>(initialMessages)\n const [text, setText] = useState(\"\")\n const [model, setModel] = useState(defaultModel ?? models[0]?.id ?? \"\")\n const [webSearch, setWebSearch] = useState(false)\n const [status, setStatus] = useState<\"ready\" | \"streaming\">(\"ready\")\n const [streamStart, setStreamStart] = useState<Date | undefined>()\n\n const handleSubmit = useCallback(\n async (_msg: PromptInputMessage) => {\n const trimmed = text.trim()\n if (!trimmed || status === \"streaming\") return\n\n const userMsg: ChatMessage = {\n id: nextId(),\n role: \"user\",\n content: trimmed,\n }\n setMessages((prev) => [...prev, userMsg])\n setText(\"\")\n setStatus(\"streaming\")\n setStreamStart(new Date())\n\n try {\n const reply = await onSend?.(trimmed, model)\n const assistantMsg: ChatMessage = {\n id: nextId(),\n role: \"assistant\",\n content: reply ?? \"I received your message.\",\n }\n setMessages((prev) => [...prev, assistantMsg])\n } finally {\n setStatus(\"ready\")\n // Keep streamStart briefly so the bubble-confirm ripple can play out\n setTimeout(() => setStreamStart(undefined), STREAM_STATUS_LINGER_MS)\n }\n },\n [text, status, model, onSend],\n )\n\n const handleSuggestion = useCallback((suggestion: string) => {\n setText(suggestion)\n }, [])\n\n const handleCopy = useCallback((content: string) => {\n navigator.clipboard.writeText(content).catch(() => {})\n }, [])\n\n const handleRetry = useCallback((id: string) => {\n setMessages((prev) => {\n const idx = prev.findIndex((m) => m.id === id)\n if (idx === -1) return prev\n return prev.slice(0, idx)\n })\n }, [])\n\n return (\n <div className={cn(\"mx-auto flex h-full w-full max-w-[980px] flex-col\", className)}>\n {/* Conversation */}\n <Conversation className=\"flex-1\">\n <ConversationContent>\n {messages.length === 0 ? (\n <ConversationEmptyState\n description=\"Send a message or pick a suggestion below to start.\"\n title=\"Start a conversation\"\n />\n ) : null}\n\n {messages.map((msg, index) => (\n <div className=\"space-y-1\" key={msg.id}>\n {/* Branch wrapper — renders alt responses side-by-side when branches exist */}\n {msg.role === \"assistant\" && msg.branches && msg.branches.length > 1 ? (\n <MessageBranch>\n <MessageBranchContent>\n {msg.branches.map((branch, i) => (\n <AssistantMessage\n key={i}\n content={branch}\n id={msg.id}\n onCopy={handleCopy}\n onRetry={handleRetry}\n reasoning={i === 0 ? msg.reasoning : undefined}\n sources={i === 0 ? msg.sources : undefined}\n />\n ))}\n </MessageBranchContent>\n <MessageToolbar>\n <MessageBranchSelector>\n <MessageBranchPrevious />\n <MessageBranchPage />\n <MessageBranchNext />\n </MessageBranchSelector>\n </MessageToolbar>\n </MessageBranch>\n ) : msg.role === \"assistant\" ? (\n <>\n <AssistantMessage\n content={msg.content}\n id={msg.id}\n onCopy={handleCopy}\n onRetry={handleRetry}\n reasoning={msg.reasoning}\n sources={msg.sources}\n />\n </>\n ) : (\n <Message from=\"user\">\n <MessageContent>\n <MessageResponse>{msg.content}</MessageResponse>\n </MessageContent>\n </Message>\n )}\n\n {index === messages.length - 1 && streamStart && (\n <div className=\"mt-3\">\n <StreamStatus\n iconVariant=\"loader-circle\"\n isStreaming={status === \"streaming\"}\n startTime={streamStart}\n />\n </div>\n )}\n </div>\n ))}\n </ConversationContent>\n <ConversationScrollButton />\n </Conversation>\n\n {/* Suggestions — only shown when conversation is empty */}\n {messages.length === 0 && suggestions.length > 0 && (\n <div className=\"px-4 pb-2\">\n <Suggestions>\n {suggestions.map((s) => (\n <Suggestion key={s} onClick={handleSuggestion} suggestion={s} />\n ))}\n </Suggestions>\n </div>\n )}\n\n {/* Prompt input */}\n <div className=\"px-4 pt-3 pb-4\">\n <PromptInput onSubmit={handleSubmit}>\n <PromptInputBody>\n <PromptInputTextarea\n aria-labelledby={labelId}\n onChange={(e) => setText(e.target.value)}\n placeholder=\"Ask anything...\"\n value={text}\n />\n </PromptInputBody>\n <PromptInputFooter>\n <PromptInputTools>\n {/* Model selector */}\n {models.length > 1 && (\n <PromptInputSelect onValueChange={setModel} value={model}>\n <PromptInputSelectTrigger>\n <PromptInputSelectValue />\n </PromptInputSelectTrigger>\n <PromptInputSelectContent>\n {models.map((m) => (\n <PromptInputSelectItem key={m.id} value={m.id}>\n {m.name}\n </PromptInputSelectItem>\n ))}\n </PromptInputSelectContent>\n </PromptInputSelect>\n )}\n\n {/* Web search toggle */}\n <PromptInputButton\n onClick={() => setWebSearch((v) => !v)}\n tooltip={{ content: \"Search the web\" }}\n variant={webSearch ? \"default\" : \"ghost\"}\n >\n <GlobeIcon size={16} />\n <span>Search</span>\n </PromptInputButton>\n </PromptInputTools>\n\n <PromptInputSubmit disabled={!text.trim()} status={status} />\n </PromptInputFooter>\n </PromptInput>\n </div>\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// Internal sub-component for assistant messages\n// ---------------------------------------------------------------------------\n\ninterface AssistantMessageProps {\n id: string\n content: string\n reasoning?: string\n sources?: ChatSource[]\n onCopy: (content: string) => void\n onRetry: (id: string) => void\n}\n\nconst AssistantMessage = ({ id, content, reasoning, sources, onCopy, onRetry }: AssistantMessageProps) => (\n <div className=\"space-y-2\">\n {reasoning && (\n <Reasoning>\n <ReasoningTrigger />\n <ReasoningContent>{reasoning}</ReasoningContent>\n </Reasoning>\n )}\n\n <Message from=\"assistant\">\n <MessageContent>\n <MessageResponse>{content}</MessageResponse>\n </MessageContent>\n </Message>\n\n {sources && sources.length > 0 && (\n <Sources>\n <SourcesTrigger count={sources.length} />\n <SourcesContent>\n {sources.map((s) => (\n <Source href={s.href} key={s.href} title={s.title} />\n ))}\n </SourcesContent>\n </Sources>\n )}\n\n <MessageActions>\n <MessageAction label=\"Copy\" onClick={() => onCopy(content)}>\n <CopyIcon className=\"size-3\" />\n </MessageAction>\n <MessageAction label=\"Retry\" onClick={() => onRetry(id)}>\n <RefreshCcwIcon className=\"size-3\" />\n </MessageAction>\n </MessageActions>\n </div>\n)\n"],"names":["DEFAULT_MODELS","DEFAULT_SUGGESTIONS","_idCounter","nextId","STREAM_STATUS_LINGER_MS","Chat","initialMessages","models","defaultModel","suggestions","onSend","className","labelId","useId","messages","setMessages","useState","text","setText","model","setModel","webSearch","setWebSearch","status","setStatus","streamStart","setStreamStart","handleSubmit","useCallback","_msg","trimmed","userMsg","prev","reply","assistantMsg","handleSuggestion","suggestion","handleCopy","content","handleRetry","id","idx","m","cn","jsxs","Conversation","ConversationContent","jsx","ConversationEmptyState","msg","index","MessageBranch","MessageBranchContent","branch","i","AssistantMessage","MessageToolbar","MessageBranchSelector","MessageBranchPrevious","MessageBranchPage","MessageBranchNext","Fragment","Message","MessageContent","MessageResponse","StreamStatus","ConversationScrollButton","Suggestions","s","Suggestion","PromptInput","PromptInputBody","PromptInputTextarea","e","PromptInputFooter","PromptInputTools","PromptInputSelect","PromptInputSelectTrigger","PromptInputSelectValue","PromptInputSelectContent","PromptInputSelectItem","PromptInputButton","v","GlobeIcon","PromptInputSubmit","reasoning","sources","onCopy","onRetry","Reasoning","ReasoningTrigger","ReasoningContent","Sources","SourcesTrigger","SourcesContent","Source","MessageActions","MessageAction","CopyIcon","RefreshCcwIcon"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("react/jsx-runtime"),x=require("lucide-react"),c=require("react"),w=require("shiki"),L=require("./button.cjs"),C=require("./select.cjs"),a=require("../../lib/utils.cjs"),N=e=>e&&e&1,I=e=>e&&e&2,M=e=>e&&e&4,R=e=>e.map((t,o)=>({key:`line-${o}`,tokens:t.map((n,l)=>({key:`line-${o}-${l}`,token:n}))})),E=({token:e})=>s.jsx("span",{className:"dark:!bg-[var(--shiki-dark-bg)] dark:!text-[var(--shiki-dark)]",style:{backgroundColor:e.bgColor,color:e.color,fontStyle:N(e.fontStyle)?"italic":void 0,fontWeight:I(e.fontStyle)?"bold":void 0,textDecoration:M(e.fontStyle)?"underline":void 0,...e.htmlStyle},children:e.content}),q=a.cn("block","before:content-[counter(line)]","before:inline-block","before:[counter-increment:line]","before:w-8","before:mr-4","before:text-right","before:text-muted-foreground/50","before:font-mono","before:select-none"),$=({keyedLine:e,showLineNumbers:t})=>s.jsx("span",{className:t?q:"block",children:e.tokens.length===0?`
2
+ `:e.tokens.map(({token:o,key:n})=>s.jsx(E,{token:o},n))}),y=c.createContext({code:""}),B=new Map,p=new Map,h=new Map,f=100,A=(e,t)=>{const o=e.slice(0,f),n=e.length>f?e.slice(-f):"";return`${t}:${e.length}:${o}:${n}`},H=e=>{const t=B.get(e);if(t)return t;const o=w.createHighlighter({langs:[e],themes:["github-light","github-dark"]});return B.set(e,o),o},_=e=>({bg:"transparent",fg:"inherit",tokens:e.split(`
3
+ `).map(t=>t===""?[]:[{color:"inherit",content:t}])}),b=(e,t,o)=>{const n=A(e,t),l=p.get(n);return l||(o&&(h.has(n)||h.set(n,new Set),h.get(n)?.add(o)),H(t).then(r=>{const d=r.getLoadedLanguages().includes(t)?t:"text",u=r.codeToTokens(e,{lang:d,themes:{dark:"github-dark",light:"github-light"}}),g={bg:u.bg??"transparent",fg:u.fg??"inherit",tokens:u.tokens};p.set(n,g);const k=h.get(n);if(k){for(const m of k)m(g);h.delete(n)}}).catch(r=>{console.error("Failed to highlight code:",r),h.delete(n)}),null)},S=c.memo(({tokenized:e,showLineNumbers:t,className:o})=>{const n=c.useMemo(()=>({backgroundColor:e.bg,color:e.fg}),[e.bg,e.fg]),l=c.useMemo(()=>R(e.tokens),[e.tokens]);return s.jsx("pre",{className:a.cn("dark:!bg-[var(--shiki-dark-bg)] dark:!text-[var(--shiki-dark)] m-0 p-4 text-sm",o),style:n,children:s.jsx("code",{className:a.cn("font-mono text-sm",t&&"[counter-increment:line_0] [counter-reset:line]"),children:l.map(r=>s.jsx($,{keyedLine:r,showLineNumbers:t},r.key))})})},(e,t)=>e.tokenized===t.tokenized&&e.showLineNumbers===t.showLineNumbers&&e.className===t.className);S.displayName="CodeBlockBody";const j=({className:e,language:t,style:o,...n})=>s.jsx("div",{className:a.cn("group relative w-full overflow-hidden rounded-md border bg-background text-foreground",e),"data-language":t,style:{containIntrinsicSize:"auto 200px",contentVisibility:"auto",...o},...n}),K=({children:e,className:t,...o})=>s.jsx("div",{className:a.cn("flex items-center justify-between border-b bg-muted/80 px-3 py-2 text-muted-foreground text-xs",t),...o,children:e}),V=({children:e,className:t,...o})=>s.jsx("div",{className:a.cn("flex items-center gap-2",t),...o,children:e}),z=({children:e,className:t,...o})=>s.jsx("span",{className:a.cn("font-mono",t),...o,children:e}),F=({children:e,className:t,...o})=>s.jsx("div",{className:a.cn("-my-1 -mr-1 flex items-center gap-2",t),...o,children:e}),T=({code:e,language:t,showLineNumbers:o=!1})=>{const n=c.useMemo(()=>_(e),[e]),l=c.useMemo(()=>b(e,t)??n,[e,t,n]),[r,i]=c.useState(null),d=c.useRef({code:e,language:t});(d.current.code!==e||d.current.language!==t)&&(d.current={code:e,language:t},i(null)),c.useEffect(()=>{let g=!1;return b(e,t,k=>{g||i(k)}),()=>{g=!0}},[e,t]);const u=r??l;return s.jsx("div",{className:"relative overflow-auto",children:s.jsx(S,{showLineNumbers:o,tokenized:u})})},U=({code:e,language:t,showLineNumbers:o=!1,className:n,children:l,...r})=>{const i=c.useMemo(()=>({code:e}),[e]);return s.jsx(y.Provider,{value:i,children:s.jsxs(j,{className:n,language:t,...r,children:[l,s.jsx(T,{code:e,language:t,showLineNumbers:o})]})})},D=({onCopy:e,onError:t,timeout:o=2e3,children:n,className:l,...r})=>{const[i,d]=c.useState(!1),u=c.useRef(0),{code:g}=c.useContext(y),k=c.useCallback(async()=>{if(typeof window>"u"||!navigator?.clipboard?.writeText){t?.(new Error("Clipboard API not available"));return}try{i||(await navigator.clipboard.writeText(g),d(!0),e?.(),u.current=window.setTimeout(()=>d(!1),o))}catch(v){t?.(v)}},[g,e,t,o,i]);c.useEffect(()=>()=>{window.clearTimeout(u.current)},[]);const m=i?x.CheckIcon:x.CopyIcon;return s.jsx(L.Button,{className:a.cn("shrink-0",l),onClick:k,size:"icon",variant:"ghost",...r,children:n??s.jsx(m,{size:14})})},G=e=>s.jsx(C.Select,{...e}),O=({className:e,...t})=>s.jsx(C.SelectTrigger,{className:a.cn("h-7 border-none bg-transparent px-2 text-xs shadow-none",e),size:"sm",...t}),W=e=>s.jsx(C.SelectValue,{...e}),Y=({align:e="end",...t})=>s.jsx(C.SelectContent,{align:e,...t}),J=e=>s.jsx(C.SelectItem,{...e});exports.CodeBlock=U;exports.CodeBlockActions=F;exports.CodeBlockContainer=j;exports.CodeBlockContent=T;exports.CodeBlockCopyButton=D;exports.CodeBlockFilename=z;exports.CodeBlockHeader=K;exports.CodeBlockLanguageSelector=G;exports.CodeBlockLanguageSelectorContent=Y;exports.CodeBlockLanguageSelectorItem=J;exports.CodeBlockLanguageSelectorTrigger=O;exports.CodeBlockLanguageSelectorValue=W;exports.CodeBlockTitle=V;exports.highlightCode=b;
4
+ //# sourceMappingURL=code-block.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-block.cjs","sources":["../../../src/components/ui/code-block.tsx"],"sourcesContent":["import { CheckIcon, CopyIcon } from \"lucide-react\";\nimport {\n createContext,\n memo,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { createHighlighter } from \"shiki\";\n\nimport type { ComponentProps, CSSProperties, HTMLAttributes } from \"react\";\nimport type {\n BundledLanguage,\n BundledTheme,\n HighlighterGeneric,\n ThemedToken,\n} from \"shiki\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@/components/ui/select\";\nimport { cn } from \"@/lib/utils\";\n\n\n\n\n// Shiki uses bitflags for font styles: 1=italic, 2=bold, 4=underline\n// oxlint-disable-next-line eslint(no-bitwise)\nconst isItalic = (fontStyle: number | undefined) => fontStyle && fontStyle & 1;\n// oxlint-disable-next-line eslint(no-bitwise)\nconst isBold = (fontStyle: number | undefined) => fontStyle && fontStyle & 2;\nconst isUnderline = (fontStyle: number | undefined) =>\n // oxlint-disable-next-line eslint(no-bitwise)\n fontStyle && fontStyle & 4;\n\n// Transform tokens to include pre-computed keys to avoid noArrayIndexKey lint\ninterface KeyedToken {\n token: ThemedToken;\n key: string;\n}\ninterface KeyedLine {\n tokens: KeyedToken[];\n key: string;\n}\n\nconst addKeysToTokens = (lines: ThemedToken[][]): KeyedLine[] =>\n lines.map((line, lineIdx) => ({\n key: `line-${lineIdx}`,\n tokens: line.map((token, tokenIdx) => ({\n key: `line-${lineIdx}-${tokenIdx}`,\n token,\n })),\n }));\n\n// Token rendering component\nconst TokenSpan = ({ token }: { token: ThemedToken }) => (\n <span\n className=\"dark:!bg-[var(--shiki-dark-bg)] dark:!text-[var(--shiki-dark)]\"\n style={\n {\n backgroundColor: token.bgColor,\n color: token.color,\n fontStyle: isItalic(token.fontStyle) ? \"italic\" : undefined,\n fontWeight: isBold(token.fontStyle) ? \"bold\" : undefined,\n textDecoration: isUnderline(token.fontStyle) ? \"underline\" : undefined,\n ...token.htmlStyle,\n } as CSSProperties\n }\n >\n {token.content}\n </span>\n);\n\n// Line number styles using CSS counters\nconst LINE_NUMBER_CLASSES = cn(\n \"block\",\n \"before:content-[counter(line)]\",\n \"before:inline-block\",\n \"before:[counter-increment:line]\",\n \"before:w-8\",\n \"before:mr-4\",\n \"before:text-right\",\n \"before:text-muted-foreground/50\",\n \"before:font-mono\",\n \"before:select-none\"\n);\n\n// Line rendering component\nconst LineSpan = ({\n keyedLine,\n showLineNumbers,\n}: {\n keyedLine: KeyedLine;\n showLineNumbers: boolean;\n}) => (\n <span className={showLineNumbers ? LINE_NUMBER_CLASSES : \"block\"}>\n {keyedLine.tokens.length === 0\n ? \"\\n\"\n : keyedLine.tokens.map(({ token, key }) => (\n <TokenSpan key={key} token={token} />\n ))}\n </span>\n);\n\n// Types\ntype CodeBlockProps = HTMLAttributes<HTMLDivElement> & {\n code: string;\n language: BundledLanguage;\n showLineNumbers?: boolean;\n};\n\ninterface TokenizedCode {\n tokens: ThemedToken[][];\n fg: string;\n bg: string;\n}\n\ninterface CodeBlockContextType {\n code: string;\n}\n\n// Context\nconst CodeBlockContext = createContext<CodeBlockContextType>({\n code: \"\",\n});\n\n// Highlighter cache (singleton per language)\nconst highlighterCache = new Map<\n string,\n Promise<HighlighterGeneric<BundledLanguage, BundledTheme>>\n>();\n\n// Token cache\nconst tokensCache = new Map<string, TokenizedCode>();\n\n// Subscribers for async token updates\nconst subscribers = new Map<string, Set<(result: TokenizedCode) => void>>();\n\nconst CACHE_KEY_SLICE_LENGTH = 100;\n\nconst getTokensCacheKey = (code: string, language: BundledLanguage) => {\n const start = code.slice(0, CACHE_KEY_SLICE_LENGTH);\n const end = code.length > CACHE_KEY_SLICE_LENGTH ? code.slice(-CACHE_KEY_SLICE_LENGTH) : \"\";\n return `${language}:${code.length}:${start}:${end}`;\n};\n\nconst getHighlighter = (\n language: BundledLanguage\n): Promise<HighlighterGeneric<BundledLanguage, BundledTheme>> => {\n const cached = highlighterCache.get(language);\n if (cached) {\n return cached;\n }\n\n const highlighterPromise = createHighlighter({\n langs: [language],\n themes: [\"github-light\", \"github-dark\"],\n });\n\n highlighterCache.set(language, highlighterPromise);\n return highlighterPromise;\n};\n\n// Create raw tokens for immediate display while highlighting loads\nconst createRawTokens = (code: string): TokenizedCode => ({\n bg: \"transparent\",\n fg: \"inherit\",\n tokens: code.split(\"\\n\").map((line) =>\n line === \"\"\n ? []\n : [\n {\n color: \"inherit\",\n content: line,\n } as ThemedToken,\n ]\n ),\n});\n\n// Synchronous highlight with callback for async results\nexport const highlightCode = (\n code: string,\n language: BundledLanguage,\n // oxlint-disable-next-line eslint-plugin-promise(prefer-await-to-callbacks)\n callback?: (result: TokenizedCode) => void\n): TokenizedCode | null => {\n const tokensCacheKey = getTokensCacheKey(code, language);\n\n // Return cached result if available\n const cached = tokensCache.get(tokensCacheKey);\n if (cached) {\n return cached;\n }\n\n // Subscribe callback if provided\n if (callback) {\n if (!subscribers.has(tokensCacheKey)) {\n subscribers.set(tokensCacheKey, new Set());\n }\n subscribers.get(tokensCacheKey)?.add(callback);\n }\n\n // Start highlighting in background - fire-and-forget async pattern\n getHighlighter(language)\n // oxlint-disable-next-line eslint-plugin-promise(prefer-await-to-then)\n .then((highlighter) => {\n const availableLangs = highlighter.getLoadedLanguages();\n const langToUse = availableLangs.includes(language) ? language : \"text\";\n\n const result = highlighter.codeToTokens(code, {\n lang: langToUse,\n themes: {\n dark: \"github-dark\",\n light: \"github-light\",\n },\n });\n\n const tokenized: TokenizedCode = {\n bg: result.bg ?? \"transparent\",\n fg: result.fg ?? \"inherit\",\n tokens: result.tokens,\n };\n\n // Cache the result\n tokensCache.set(tokensCacheKey, tokenized);\n\n // Notify all subscribers\n const subs = subscribers.get(tokensCacheKey);\n if (subs) {\n for (const sub of subs) {\n sub(tokenized);\n }\n subscribers.delete(tokensCacheKey);\n }\n })\n // oxlint-disable-next-line eslint-plugin-promise(prefer-await-to-then), eslint-plugin-promise(prefer-await-to-callbacks)\n .catch((error) => {\n console.error(\"Failed to highlight code:\", error);\n subscribers.delete(tokensCacheKey);\n });\n\n return null;\n};\n\nconst CodeBlockBody = memo(\n ({\n tokenized,\n showLineNumbers,\n className,\n }: {\n tokenized: TokenizedCode;\n showLineNumbers: boolean;\n className?: string;\n }) => {\n const preStyle = useMemo(\n () => ({\n backgroundColor: tokenized.bg,\n color: tokenized.fg,\n }),\n [tokenized.bg, tokenized.fg]\n );\n\n const keyedLines = useMemo(\n () => addKeysToTokens(tokenized.tokens),\n [tokenized.tokens]\n );\n\n return (\n <pre\n className={cn(\n \"dark:!bg-[var(--shiki-dark-bg)] dark:!text-[var(--shiki-dark)] m-0 p-4 text-sm\",\n className\n )}\n style={preStyle}\n >\n <code\n className={cn(\n \"font-mono text-sm\",\n showLineNumbers && \"[counter-increment:line_0] [counter-reset:line]\"\n )}\n >\n {keyedLines.map((keyedLine) => (\n <LineSpan\n key={keyedLine.key}\n keyedLine={keyedLine}\n showLineNumbers={showLineNumbers}\n />\n ))}\n </code>\n </pre>\n );\n },\n (prevProps, nextProps) =>\n prevProps.tokenized === nextProps.tokenized &&\n prevProps.showLineNumbers === nextProps.showLineNumbers &&\n prevProps.className === nextProps.className\n);\n\nCodeBlockBody.displayName = \"CodeBlockBody\";\n\nexport const CodeBlockContainer = ({\n className,\n language,\n style,\n ...props\n}: HTMLAttributes<HTMLDivElement> & { language: string }) => (\n <div\n className={cn(\n \"group relative w-full overflow-hidden rounded-md border bg-background text-foreground\",\n className\n )}\n data-language={language}\n style={{\n containIntrinsicSize: \"auto 200px\",\n contentVisibility: \"auto\",\n ...style,\n }}\n {...props}\n />\n);\n\nexport const CodeBlockHeader = ({\n children,\n className,\n ...props\n}: HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex items-center justify-between border-b bg-muted/80 px-3 py-2 text-muted-foreground text-xs\",\n className\n )}\n {...props}\n >\n {children}\n </div>\n);\n\nexport const CodeBlockTitle = ({\n children,\n className,\n ...props\n}: HTMLAttributes<HTMLDivElement>) => (\n <div className={cn(\"flex items-center gap-2\", className)} {...props}>\n {children}\n </div>\n);\n\nexport const CodeBlockFilename = ({\n children,\n className,\n ...props\n}: HTMLAttributes<HTMLSpanElement>) => (\n <span className={cn(\"font-mono\", className)} {...props}>\n {children}\n </span>\n);\n\nexport const CodeBlockActions = ({\n children,\n className,\n ...props\n}: HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\"-my-1 -mr-1 flex items-center gap-2\", className)}\n {...props}\n >\n {children}\n </div>\n);\n\nexport const CodeBlockContent = ({\n code,\n language,\n showLineNumbers = false,\n}: {\n code: string;\n language: BundledLanguage;\n showLineNumbers?: boolean;\n}) => {\n // Memoized raw tokens for immediate display\n const rawTokens = useMemo(() => createRawTokens(code), [code]);\n\n // Synchronous cache lookup — avoids setState in effect for cached results\n const syncTokens = useMemo(\n () => highlightCode(code, language) ?? rawTokens,\n [code, language, rawTokens]\n );\n\n // Async highlighting result (populated after shiki loads)\n const [asyncTokens, setAsyncTokens] = useState<TokenizedCode | null>(null);\n const asyncKeyRef = useRef({ code, language });\n\n // Invalidate stale async tokens synchronously during render\n if (\n asyncKeyRef.current.code !== code ||\n asyncKeyRef.current.language !== language\n ) {\n asyncKeyRef.current = { code, language };\n setAsyncTokens(null);\n }\n\n useEffect(() => {\n let cancelled = false;\n\n highlightCode(code, language, (result) => {\n if (!cancelled) {\n setAsyncTokens(result);\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [code, language]);\n\n const tokenized = asyncTokens ?? syncTokens;\n\n return (\n <div className=\"relative overflow-auto\">\n <CodeBlockBody showLineNumbers={showLineNumbers} tokenized={tokenized} />\n </div>\n );\n};\n\nexport const CodeBlock = ({\n code,\n language,\n showLineNumbers = false,\n className,\n children,\n ...props\n}: CodeBlockProps) => {\n const contextValue = useMemo(() => ({ code }), [code]);\n\n return (\n <CodeBlockContext.Provider value={contextValue}>\n <CodeBlockContainer className={className} language={language} {...props}>\n {children}\n <CodeBlockContent\n code={code}\n language={language}\n showLineNumbers={showLineNumbers}\n />\n </CodeBlockContainer>\n </CodeBlockContext.Provider>\n );\n};\n\nexport type CodeBlockCopyButtonProps = ComponentProps<typeof Button> & {\n onCopy?: () => void;\n onError?: (error: Error) => void;\n timeout?: number;\n};\n\nexport const CodeBlockCopyButton = ({\n onCopy,\n onError,\n timeout = 2000,\n children,\n className,\n ...props\n}: CodeBlockCopyButtonProps) => {\n const [isCopied, setIsCopied] = useState(false);\n const timeoutRef = useRef<number>(0);\n const { code } = useContext(CodeBlockContext);\n\n const copyToClipboard = useCallback(async () => {\n if (typeof window === \"undefined\" || !navigator?.clipboard?.writeText) {\n onError?.(new Error(\"Clipboard API not available\"));\n return;\n }\n\n try {\n if (!isCopied) {\n await navigator.clipboard.writeText(code);\n setIsCopied(true);\n onCopy?.();\n timeoutRef.current = window.setTimeout(\n () => setIsCopied(false),\n timeout\n );\n }\n } catch (error) {\n onError?.(error as Error);\n }\n }, [code, onCopy, onError, timeout, isCopied]);\n\n useEffect(\n () => () => {\n window.clearTimeout(timeoutRef.current);\n },\n []\n );\n\n const Icon = isCopied ? CheckIcon : CopyIcon;\n\n return (\n <Button\n className={cn(\"shrink-0\", className)}\n onClick={copyToClipboard}\n size=\"icon\"\n variant=\"ghost\"\n {...props}\n >\n {children ?? <Icon size={14} />}\n </Button>\n );\n};\n\nexport type CodeBlockLanguageSelectorProps = ComponentProps<typeof Select>;\n\nexport const CodeBlockLanguageSelector = (\n props: CodeBlockLanguageSelectorProps\n) => <Select {...props} />;\n\nexport type CodeBlockLanguageSelectorTriggerProps = ComponentProps<\n typeof SelectTrigger\n>;\n\nexport const CodeBlockLanguageSelectorTrigger = ({\n className,\n ...props\n}: CodeBlockLanguageSelectorTriggerProps) => (\n <SelectTrigger\n className={cn(\n \"h-7 border-none bg-transparent px-2 text-xs shadow-none\",\n className\n )}\n size=\"sm\"\n {...props}\n />\n);\n\nexport type CodeBlockLanguageSelectorValueProps = ComponentProps<\n typeof SelectValue\n>;\n\nexport const CodeBlockLanguageSelectorValue = (\n props: CodeBlockLanguageSelectorValueProps\n) => <SelectValue {...props} />;\n\nexport type CodeBlockLanguageSelectorContentProps = ComponentProps<\n typeof SelectContent\n>;\n\nexport const CodeBlockLanguageSelectorContent = ({\n align = \"end\",\n ...props\n}: CodeBlockLanguageSelectorContentProps) => (\n <SelectContent align={align} {...props} />\n);\n\nexport type CodeBlockLanguageSelectorItemProps = ComponentProps<\n typeof SelectItem\n>;\n\nexport const CodeBlockLanguageSelectorItem = (\n props: CodeBlockLanguageSelectorItemProps\n) => <SelectItem {...props} />;\n"],"names":["isItalic","fontStyle","isBold","isUnderline","addKeysToTokens","lines","line","lineIdx","token","tokenIdx","TokenSpan","jsx","LINE_NUMBER_CLASSES","cn","LineSpan","keyedLine","showLineNumbers","key","CodeBlockContext","createContext","highlighterCache","tokensCache","subscribers","CACHE_KEY_SLICE_LENGTH","getTokensCacheKey","code","language","start","end","getHighlighter","cached","highlighterPromise","createHighlighter","createRawTokens","highlightCode","callback","tokensCacheKey","highlighter","langToUse","result","tokenized","subs","sub","error","CodeBlockBody","memo","className","preStyle","useMemo","keyedLines","prevProps","nextProps","CodeBlockContainer","style","props","CodeBlockHeader","children","CodeBlockTitle","CodeBlockFilename","CodeBlockActions","CodeBlockContent","rawTokens","syncTokens","asyncTokens","setAsyncTokens","useState","asyncKeyRef","useRef","useEffect","cancelled","CodeBlock","contextValue","jsxs","CodeBlockCopyButton","onCopy","onError","timeout","isCopied","setIsCopied","timeoutRef","useContext","copyToClipboard","useCallback","Icon","CheckIcon","CopyIcon","Button","CodeBlockLanguageSelector","Select","CodeBlockLanguageSelectorTrigger","SelectTrigger","CodeBlockLanguageSelectorValue","SelectValue","CodeBlockLanguageSelectorContent","align","SelectContent","CodeBlockLanguageSelectorItem","SelectItem"],"mappings":"0QAoCMA,EAAYC,GAAkCA,GAAaA,EAAY,EAEvEC,EAAUD,GAAkCA,GAAaA,EAAY,EACrEE,EAAeF,GAEnBA,GAAaA,EAAY,EAYrBG,EAAmBC,GACvBA,EAAM,IAAI,CAACC,EAAMC,KAAa,CAC5B,IAAK,QAAQA,CAAO,GACpB,OAAQD,EAAK,IAAI,CAACE,EAAOC,KAAc,CACrC,IAAK,QAAQF,CAAO,IAAIE,CAAQ,GAChC,MAAAD,CAAA,EACA,CACJ,EAAE,EAGEE,EAAY,CAAC,CAAE,MAAAF,CAAA,IACnBG,EAAAA,IAAC,OAAA,CACC,UAAU,iEACV,MACE,CACE,gBAAiBH,EAAM,QACvB,MAAOA,EAAM,MACb,UAAWR,EAASQ,EAAM,SAAS,EAAI,SAAW,OAClD,WAAYN,EAAOM,EAAM,SAAS,EAAI,OAAS,OAC/C,eAAgBL,EAAYK,EAAM,SAAS,EAAI,YAAc,OAC7D,GAAGA,EAAM,SAAA,EAIZ,SAAAA,EAAM,OAAA,CACT,EAIII,EAAsBC,EAAAA,GAC1B,QACA,iCACA,sBACA,kCACA,aACA,cACA,oBACA,kCACA,mBACA,oBACF,EAGMC,EAAW,CAAC,CAChB,UAAAC,EACA,gBAAAC,CACF,IAIEL,MAAC,OAAA,CAAK,UAAWK,EAAkBJ,EAAsB,QACtD,SAAAG,EAAU,OAAO,SAAW,EACzB;AAAA,EACAA,EAAU,OAAO,IAAI,CAAC,CAAE,MAAAP,EAAO,IAAAS,CAAA,UAC5BP,EAAA,CAAoB,MAAAF,CAAA,EAALS,CAAmB,CACpC,CAAA,CACP,EAqBIC,EAAmBC,EAAAA,cAAoC,CAC3D,KAAM,EACR,CAAC,EAGKC,MAAuB,IAMvBC,MAAkB,IAGlBC,MAAkB,IAElBC,EAAyB,IAEzBC,EAAoB,CAACC,EAAcC,IAA8B,CACrE,MAAMC,EAAQF,EAAK,MAAM,EAAGF,CAAsB,EAC5CK,EAAMH,EAAK,OAASF,EAAyBE,EAAK,MAAM,CAACF,CAAsB,EAAI,GACzF,MAAO,GAAGG,CAAQ,IAAID,EAAK,MAAM,IAAIE,CAAK,IAAIC,CAAG,EACnD,EAEMC,EACJH,GAC+D,CAC/D,MAAMI,EAASV,EAAiB,IAAIM,CAAQ,EAC5C,GAAII,EACF,OAAOA,EAGT,MAAMC,EAAqBC,EAAAA,kBAAkB,CAC3C,MAAO,CAACN,CAAQ,EAChB,OAAQ,CAAC,eAAgB,aAAa,CAAA,CACvC,EAED,OAAAN,EAAiB,IAAIM,EAAUK,CAAkB,EAC1CA,CACT,EAGME,EAAmBR,IAAiC,CACxD,GAAI,cACJ,GAAI,UACJ,OAAQA,EAAK,MAAM;AAAA,CAAI,EAAE,IAAKnB,GAC5BA,IAAS,GACL,GACA,CACE,CACE,MAAO,UACP,QAASA,CAAA,CACX,CACF,CAER,GAGa4B,EAAgB,CAC3BT,EACAC,EAEAS,IACyB,CACzB,MAAMC,EAAiBZ,EAAkBC,EAAMC,CAAQ,EAGjDI,EAAST,EAAY,IAAIe,CAAc,EAC7C,OAAIN,IAKAK,IACGb,EAAY,IAAIc,CAAc,GACjCd,EAAY,IAAIc,EAAgB,IAAI,GAAK,EAE3Cd,EAAY,IAAIc,CAAc,GAAG,IAAID,CAAQ,GAI/CN,EAAeH,CAAQ,EAEpB,KAAMW,GAAgB,CAErB,MAAMC,EADiBD,EAAY,mBAAA,EACF,SAASX,CAAQ,EAAIA,EAAW,OAE3Da,EAASF,EAAY,aAAaZ,EAAM,CAC5C,KAAMa,EACN,OAAQ,CACN,KAAM,cACN,MAAO,cAAA,CACT,CACD,EAEKE,EAA2B,CAC/B,GAAID,EAAO,IAAM,cACjB,GAAIA,EAAO,IAAM,UACjB,OAAQA,EAAO,MAAA,EAIjBlB,EAAY,IAAIe,EAAgBI,CAAS,EAGzC,MAAMC,EAAOnB,EAAY,IAAIc,CAAc,EAC3C,GAAIK,EAAM,CACR,UAAWC,KAAOD,EAChBC,EAAIF,CAAS,EAEflB,EAAY,OAAOc,CAAc,CACnC,CACF,CAAC,EAEA,MAAOO,GAAU,CAChB,QAAQ,MAAM,4BAA6BA,CAAK,EAChDrB,EAAY,OAAOc,CAAc,CACnC,CAAC,EAEI,KACT,EAEMQ,EAAgBC,EAAAA,KACpB,CAAC,CACC,UAAAL,EACA,gBAAAxB,EACA,UAAA8B,CAAA,IAKI,CACJ,MAAMC,EAAWC,EAAAA,QACf,KAAO,CACL,gBAAiBR,EAAU,GAC3B,MAAOA,EAAU,EAAA,GAEnB,CAACA,EAAU,GAAIA,EAAU,EAAE,CAAA,EAGvBS,EAAaD,EAAAA,QACjB,IAAM5C,EAAgBoC,EAAU,MAAM,EACtC,CAACA,EAAU,MAAM,CAAA,EAGnB,OACE7B,EAAAA,IAAC,MAAA,CACC,UAAWE,EAAAA,GACT,iFACAiC,CAAA,EAEF,MAAOC,EAEP,SAAApC,EAAAA,IAAC,OAAA,CACC,UAAWE,EAAAA,GACT,oBACAG,GAAmB,iDAAA,EAGpB,SAAAiC,EAAW,IAAKlC,GACfJ,EAAAA,IAACG,EAAA,CAEC,UAAAC,EACA,gBAAAC,CAAA,EAFKD,EAAU,GAAA,CAIlB,CAAA,CAAA,CACH,CAAA,CAGN,EACA,CAACmC,EAAWC,IACVD,EAAU,YAAcC,EAAU,WAClCD,EAAU,kBAAoBC,EAAU,iBACxCD,EAAU,YAAcC,EAAU,SACtC,EAEAP,EAAc,YAAc,gBAErB,MAAMQ,EAAqB,CAAC,CACjC,UAAAN,EACA,SAAApB,EACA,MAAA2B,EACA,GAAGC,CACL,IACE3C,EAAAA,IAAC,MAAA,CACC,UAAWE,EAAAA,GACT,wFACAiC,CAAA,EAEF,gBAAepB,EACf,MAAO,CACL,qBAAsB,aACtB,kBAAmB,OACnB,GAAG2B,CAAA,EAEJ,GAAGC,CAAA,CACN,EAGWC,EAAkB,CAAC,CAC9B,SAAAC,EACA,UAAAV,EACA,GAAGQ,CACL,IACE3C,EAAAA,IAAC,MAAA,CACC,UAAWE,EAAAA,GACT,iGACAiC,CAAA,EAED,GAAGQ,EAEH,SAAAE,CAAA,CACH,EAGWC,EAAiB,CAAC,CAC7B,SAAAD,EACA,UAAAV,EACA,GAAGQ,CACL,IACE3C,EAAAA,IAAC,OAAI,UAAWE,EAAAA,GAAG,0BAA2BiC,CAAS,EAAI,GAAGQ,EAC3D,SAAAE,CAAA,CACH,EAGWE,EAAoB,CAAC,CAChC,SAAAF,EACA,UAAAV,EACA,GAAGQ,CACL,IACE3C,EAAAA,IAAC,QAAK,UAAWE,EAAAA,GAAG,YAAaiC,CAAS,EAAI,GAAGQ,EAC9C,SAAAE,CAAA,CACH,EAGWG,EAAmB,CAAC,CAC/B,SAAAH,EACA,UAAAV,EACA,GAAGQ,CACL,IACE3C,EAAAA,IAAC,MAAA,CACC,UAAWE,EAAAA,GAAG,sCAAuCiC,CAAS,EAC7D,GAAGQ,EAEH,SAAAE,CAAA,CACH,EAGWI,EAAmB,CAAC,CAC/B,KAAAnC,EACA,SAAAC,EACA,gBAAAV,EAAkB,EACpB,IAIM,CAEJ,MAAM6C,EAAYb,EAAAA,QAAQ,IAAMf,EAAgBR,CAAI,EAAG,CAACA,CAAI,CAAC,EAGvDqC,EAAad,EAAAA,QACjB,IAAMd,EAAcT,EAAMC,CAAQ,GAAKmC,EACvC,CAACpC,EAAMC,EAAUmC,CAAS,CAAA,EAItB,CAACE,EAAaC,CAAc,EAAIC,EAAAA,SAA+B,IAAI,EACnEC,EAAcC,EAAAA,OAAO,CAAE,KAAA1C,EAAM,SAAAC,EAAU,GAI3CwC,EAAY,QAAQ,OAASzC,GAC7ByC,EAAY,QAAQ,WAAaxC,KAEjCwC,EAAY,QAAU,CAAE,KAAAzC,EAAM,SAAAC,CAAA,EAC9BsC,EAAe,IAAI,GAGrBI,EAAAA,UAAU,IAAM,CACd,IAAIC,EAAY,GAEhB,OAAAnC,EAAcT,EAAMC,EAAWa,GAAW,CACnC8B,GACHL,EAAezB,CAAM,CAEzB,CAAC,EAEM,IAAM,CACX8B,EAAY,EACd,CACF,EAAG,CAAC5C,EAAMC,CAAQ,CAAC,EAEnB,MAAMc,EAAYuB,GAAeD,EAEjC,OACEnD,MAAC,OAAI,UAAU,yBACb,eAACiC,EAAA,CAAc,gBAAA5B,EAAkC,UAAAwB,EAAsB,CAAA,CACzE,CAEJ,EAEa8B,EAAY,CAAC,CACxB,KAAA7C,EACA,SAAAC,EACA,gBAAAV,EAAkB,GAClB,UAAA8B,EACA,SAAAU,EACA,GAAGF,CACL,IAAsB,CACpB,MAAMiB,EAAevB,EAAAA,QAAQ,KAAO,CAAE,KAAAvB,IAAS,CAACA,CAAI,CAAC,EAErD,OACEd,EAAAA,IAACO,EAAiB,SAAjB,CAA0B,MAAOqD,EAChC,SAAAC,EAAAA,KAACpB,EAAA,CAAmB,UAAAN,EAAsB,SAAApB,EAAqB,GAAG4B,EAC/D,SAAA,CAAAE,EACD7C,EAAAA,IAACiD,EAAA,CACC,KAAAnC,EACA,SAAAC,EACA,gBAAAV,CAAA,CAAA,CACF,CAAA,CACF,CAAA,CACF,CAEJ,EAQayD,EAAsB,CAAC,CAClC,OAAAC,EACA,QAAAC,EACA,QAAAC,EAAU,IACV,SAAApB,EACA,UAAAV,EACA,GAAGQ,CACL,IAAgC,CAC9B,KAAM,CAACuB,EAAUC,CAAW,EAAIb,EAAAA,SAAS,EAAK,EACxCc,EAAaZ,EAAAA,OAAe,CAAC,EAC7B,CAAE,KAAA1C,CAAA,EAASuD,EAAAA,WAAW9D,CAAgB,EAEtC+D,EAAkBC,EAAAA,YAAY,SAAY,CAC9C,GAAI,OAAO,OAAW,KAAe,CAAC,WAAW,WAAW,UAAW,CACrEP,IAAU,IAAI,MAAM,6BAA6B,CAAC,EAClD,MACF,CAEA,GAAI,CACGE,IACH,MAAM,UAAU,UAAU,UAAUpD,CAAI,EACxCqD,EAAY,EAAI,EAChBJ,IAAA,EACAK,EAAW,QAAU,OAAO,WAC1B,IAAMD,EAAY,EAAK,EACvBF,CAAA,EAGN,OAASjC,EAAO,CACdgC,IAAUhC,CAAc,CAC1B,CACF,EAAG,CAAClB,EAAMiD,EAAQC,EAASC,EAASC,CAAQ,CAAC,EAE7CT,EAAAA,UACE,IAAM,IAAM,CACV,OAAO,aAAaW,EAAW,OAAO,CACxC,EACA,CAAA,CAAC,EAGH,MAAMI,EAAON,EAAWO,EAAAA,UAAYC,EAAAA,SAEpC,OACE1E,EAAAA,IAAC2E,EAAAA,OAAA,CACC,UAAWzE,EAAAA,GAAG,WAAYiC,CAAS,EACnC,QAASmC,EACT,KAAK,OACL,QAAQ,QACP,GAAG3B,EAEH,SAAAE,GAAY7C,EAAAA,IAACwE,EAAA,CAAK,KAAM,EAAA,CAAI,CAAA,CAAA,CAGnC,EAIaI,EACXjC,GACG3C,EAAAA,IAAC6E,EAAAA,OAAA,CAAQ,GAAGlC,CAAA,CAAO,EAMXmC,EAAmC,CAAC,CAC/C,UAAA3C,EACA,GAAGQ,CACL,IACE3C,EAAAA,IAAC+E,EAAAA,cAAA,CACC,UAAW7E,EAAAA,GACT,0DACAiC,CAAA,EAEF,KAAK,KACJ,GAAGQ,CAAA,CACN,EAOWqC,EACXrC,GACG3C,EAAAA,IAACiF,EAAAA,YAAA,CAAa,GAAGtC,CAAA,CAAO,EAMhBuC,EAAmC,CAAC,CAC/C,MAAAC,EAAQ,MACR,GAAGxC,CACL,IACE3C,EAAAA,IAACoF,EAAAA,cAAA,CAAc,MAAAD,EAAe,GAAGxC,CAAA,CAAO,EAO7B0C,EACX1C,GACG3C,EAAAA,IAACsF,EAAAA,WAAA,CAAY,GAAG3C,CAAA,CAAO"}
@@ -0,0 +1,306 @@
1
+ import { jsx as s, jsxs as T } from "react/jsx-runtime";
2
+ import { CheckIcon as N, CopyIcon as L } from "lucide-react";
3
+ import { createContext as I, memo as E, useMemo as h, useState as y, useRef as x, useEffect as w, useContext as $, useCallback as _ } from "react";
4
+ import { createHighlighter as A } from "shiki";
5
+ import { Button as H } from "./button.js";
6
+ import { Select as K, SelectContent as M, SelectItem as R, SelectTrigger as z, SelectValue as V } from "./select.js";
7
+ import { cn as i } from "../../lib/utils.js";
8
+ const j = (e) => e && e & 1, U = (e) => e && e & 2, F = (e) => (
9
+ // oxlint-disable-next-line eslint(no-bitwise)
10
+ e && e & 4
11
+ ), D = (e) => e.map((t, o) => ({
12
+ key: `line-${o}`,
13
+ tokens: t.map((n, c) => ({
14
+ key: `line-${o}-${c}`,
15
+ token: n
16
+ }))
17
+ })), G = ({ token: e }) => /* @__PURE__ */ s(
18
+ "span",
19
+ {
20
+ className: "dark:!bg-[var(--shiki-dark-bg)] dark:!text-[var(--shiki-dark)]",
21
+ style: {
22
+ backgroundColor: e.bgColor,
23
+ color: e.color,
24
+ fontStyle: j(e.fontStyle) ? "italic" : void 0,
25
+ fontWeight: U(e.fontStyle) ? "bold" : void 0,
26
+ textDecoration: F(e.fontStyle) ? "underline" : void 0,
27
+ ...e.htmlStyle
28
+ },
29
+ children: e.content
30
+ }
31
+ ), W = i(
32
+ "block",
33
+ "before:content-[counter(line)]",
34
+ "before:inline-block",
35
+ "before:[counter-increment:line]",
36
+ "before:w-8",
37
+ "before:mr-4",
38
+ "before:text-right",
39
+ "before:text-muted-foreground/50",
40
+ "before:font-mono",
41
+ "before:select-none"
42
+ ), Y = ({
43
+ keyedLine: e,
44
+ showLineNumbers: t
45
+ }) => /* @__PURE__ */ s("span", { className: t ? W : "block", children: e.tokens.length === 0 ? `
46
+ ` : e.tokens.map(({ token: o, key: n }) => /* @__PURE__ */ s(G, { token: o }, n)) }), v = I({
47
+ code: ""
48
+ }), b = /* @__PURE__ */ new Map(), C = /* @__PURE__ */ new Map(), g = /* @__PURE__ */ new Map(), k = 100, q = (e, t) => {
49
+ const o = e.slice(0, k), n = e.length > k ? e.slice(-k) : "";
50
+ return `${t}:${e.length}:${o}:${n}`;
51
+ }, J = (e) => {
52
+ const t = b.get(e);
53
+ if (t)
54
+ return t;
55
+ const o = A({
56
+ langs: [e],
57
+ themes: ["github-light", "github-dark"]
58
+ });
59
+ return b.set(e, o), o;
60
+ }, O = (e) => ({
61
+ bg: "transparent",
62
+ fg: "inherit",
63
+ tokens: e.split(`
64
+ `).map(
65
+ (t) => t === "" ? [] : [
66
+ {
67
+ color: "inherit",
68
+ content: t
69
+ }
70
+ ]
71
+ )
72
+ }), p = (e, t, o) => {
73
+ const n = q(e, t), c = C.get(n);
74
+ return c || (o && (g.has(n) || g.set(n, /* @__PURE__ */ new Set()), g.get(n)?.add(o)), J(t).then((r) => {
75
+ const l = r.getLoadedLanguages().includes(t) ? t : "text", m = r.codeToTokens(e, {
76
+ lang: l,
77
+ themes: {
78
+ dark: "github-dark",
79
+ light: "github-light"
80
+ }
81
+ }), d = {
82
+ bg: m.bg ?? "transparent",
83
+ fg: m.fg ?? "inherit",
84
+ tokens: m.tokens
85
+ };
86
+ C.set(n, d);
87
+ const u = g.get(n);
88
+ if (u) {
89
+ for (const f of u)
90
+ f(d);
91
+ g.delete(n);
92
+ }
93
+ }).catch((r) => {
94
+ console.error("Failed to highlight code:", r), g.delete(n);
95
+ }), null);
96
+ }, B = E(
97
+ ({
98
+ tokenized: e,
99
+ showLineNumbers: t,
100
+ className: o
101
+ }) => {
102
+ const n = h(
103
+ () => ({
104
+ backgroundColor: e.bg,
105
+ color: e.fg
106
+ }),
107
+ [e.bg, e.fg]
108
+ ), c = h(
109
+ () => D(e.tokens),
110
+ [e.tokens]
111
+ );
112
+ return /* @__PURE__ */ s(
113
+ "pre",
114
+ {
115
+ className: i(
116
+ "dark:!bg-[var(--shiki-dark-bg)] dark:!text-[var(--shiki-dark)] m-0 p-4 text-sm",
117
+ o
118
+ ),
119
+ style: n,
120
+ children: /* @__PURE__ */ s(
121
+ "code",
122
+ {
123
+ className: i(
124
+ "font-mono text-sm",
125
+ t && "[counter-increment:line_0] [counter-reset:line]"
126
+ ),
127
+ children: c.map((r) => /* @__PURE__ */ s(
128
+ Y,
129
+ {
130
+ keyedLine: r,
131
+ showLineNumbers: t
132
+ },
133
+ r.key
134
+ ))
135
+ }
136
+ )
137
+ }
138
+ );
139
+ },
140
+ (e, t) => e.tokenized === t.tokenized && e.showLineNumbers === t.showLineNumbers && e.className === t.className
141
+ );
142
+ B.displayName = "CodeBlockBody";
143
+ const Q = ({
144
+ className: e,
145
+ language: t,
146
+ style: o,
147
+ ...n
148
+ }) => /* @__PURE__ */ s(
149
+ "div",
150
+ {
151
+ className: i(
152
+ "group relative w-full overflow-hidden rounded-md border bg-background text-foreground",
153
+ e
154
+ ),
155
+ "data-language": t,
156
+ style: {
157
+ containIntrinsicSize: "auto 200px",
158
+ contentVisibility: "auto",
159
+ ...o
160
+ },
161
+ ...n
162
+ }
163
+ ), re = ({
164
+ children: e,
165
+ className: t,
166
+ ...o
167
+ }) => /* @__PURE__ */ s(
168
+ "div",
169
+ {
170
+ className: i(
171
+ "flex items-center justify-between border-b bg-muted/80 px-3 py-2 text-muted-foreground text-xs",
172
+ t
173
+ ),
174
+ ...o,
175
+ children: e
176
+ }
177
+ ), ce = ({
178
+ children: e,
179
+ className: t,
180
+ ...o
181
+ }) => /* @__PURE__ */ s("div", { className: i("flex items-center gap-2", t), ...o, children: e }), ie = ({
182
+ children: e,
183
+ className: t,
184
+ ...o
185
+ }) => /* @__PURE__ */ s("span", { className: i("font-mono", t), ...o, children: e }), ae = ({
186
+ children: e,
187
+ className: t,
188
+ ...o
189
+ }) => /* @__PURE__ */ s(
190
+ "div",
191
+ {
192
+ className: i("-my-1 -mr-1 flex items-center gap-2", t),
193
+ ...o,
194
+ children: e
195
+ }
196
+ ), X = ({
197
+ code: e,
198
+ language: t,
199
+ showLineNumbers: o = !1
200
+ }) => {
201
+ const n = h(() => O(e), [e]), c = h(
202
+ () => p(e, t) ?? n,
203
+ [e, t, n]
204
+ ), [r, a] = y(null), l = x({ code: e, language: t });
205
+ return (l.current.code !== e || l.current.language !== t) && (l.current = { code: e, language: t }, a(null)), w(() => {
206
+ let d = !1;
207
+ return p(e, t, (u) => {
208
+ d || a(u);
209
+ }), () => {
210
+ d = !0;
211
+ };
212
+ }, [e, t]), /* @__PURE__ */ s("div", { className: "relative overflow-auto", children: /* @__PURE__ */ s(B, { showLineNumbers: o, tokenized: r ?? c }) });
213
+ }, le = ({
214
+ code: e,
215
+ language: t,
216
+ showLineNumbers: o = !1,
217
+ className: n,
218
+ children: c,
219
+ ...r
220
+ }) => {
221
+ const a = h(() => ({ code: e }), [e]);
222
+ return /* @__PURE__ */ s(v.Provider, { value: a, children: /* @__PURE__ */ T(Q, { className: n, language: t, ...r, children: [
223
+ c,
224
+ /* @__PURE__ */ s(
225
+ X,
226
+ {
227
+ code: e,
228
+ language: t,
229
+ showLineNumbers: o
230
+ }
231
+ )
232
+ ] }) });
233
+ }, de = ({
234
+ onCopy: e,
235
+ onError: t,
236
+ timeout: o = 2e3,
237
+ children: n,
238
+ className: c,
239
+ ...r
240
+ }) => {
241
+ const [a, l] = y(!1), m = x(0), { code: d } = $(v), u = _(async () => {
242
+ if (typeof window > "u" || !navigator?.clipboard?.writeText) {
243
+ t?.(new Error("Clipboard API not available"));
244
+ return;
245
+ }
246
+ try {
247
+ a || (await navigator.clipboard.writeText(d), l(!0), e?.(), m.current = window.setTimeout(
248
+ () => l(!1),
249
+ o
250
+ ));
251
+ } catch (S) {
252
+ t?.(S);
253
+ }
254
+ }, [d, e, t, o, a]);
255
+ w(
256
+ () => () => {
257
+ window.clearTimeout(m.current);
258
+ },
259
+ []
260
+ );
261
+ const f = a ? N : L;
262
+ return /* @__PURE__ */ s(
263
+ H,
264
+ {
265
+ className: i("shrink-0", c),
266
+ onClick: u,
267
+ size: "icon",
268
+ variant: "ghost",
269
+ ...r,
270
+ children: n ?? /* @__PURE__ */ s(f, { size: 14 })
271
+ }
272
+ );
273
+ }, me = (e) => /* @__PURE__ */ s(K, { ...e }), ue = ({
274
+ className: e,
275
+ ...t
276
+ }) => /* @__PURE__ */ s(
277
+ z,
278
+ {
279
+ className: i(
280
+ "h-7 border-none bg-transparent px-2 text-xs shadow-none",
281
+ e
282
+ ),
283
+ size: "sm",
284
+ ...t
285
+ }
286
+ ), ge = (e) => /* @__PURE__ */ s(V, { ...e }), he = ({
287
+ align: e = "end",
288
+ ...t
289
+ }) => /* @__PURE__ */ s(M, { align: e, ...t }), fe = (e) => /* @__PURE__ */ s(R, { ...e });
290
+ export {
291
+ le as CodeBlock,
292
+ ae as CodeBlockActions,
293
+ Q as CodeBlockContainer,
294
+ X as CodeBlockContent,
295
+ de as CodeBlockCopyButton,
296
+ ie as CodeBlockFilename,
297
+ re as CodeBlockHeader,
298
+ me as CodeBlockLanguageSelector,
299
+ he as CodeBlockLanguageSelectorContent,
300
+ fe as CodeBlockLanguageSelectorItem,
301
+ ue as CodeBlockLanguageSelectorTrigger,
302
+ ge as CodeBlockLanguageSelectorValue,
303
+ ce as CodeBlockTitle,
304
+ p as highlightCode
305
+ };
306
+ //# sourceMappingURL=code-block.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-block.js","sources":["../../../src/components/ui/code-block.tsx"],"sourcesContent":["import { CheckIcon, CopyIcon } from \"lucide-react\";\nimport {\n createContext,\n memo,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { createHighlighter } from \"shiki\";\n\nimport type { ComponentProps, CSSProperties, HTMLAttributes } from \"react\";\nimport type {\n BundledLanguage,\n BundledTheme,\n HighlighterGeneric,\n ThemedToken,\n} from \"shiki\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@/components/ui/select\";\nimport { cn } from \"@/lib/utils\";\n\n\n\n\n// Shiki uses bitflags for font styles: 1=italic, 2=bold, 4=underline\n// oxlint-disable-next-line eslint(no-bitwise)\nconst isItalic = (fontStyle: number | undefined) => fontStyle && fontStyle & 1;\n// oxlint-disable-next-line eslint(no-bitwise)\nconst isBold = (fontStyle: number | undefined) => fontStyle && fontStyle & 2;\nconst isUnderline = (fontStyle: number | undefined) =>\n // oxlint-disable-next-line eslint(no-bitwise)\n fontStyle && fontStyle & 4;\n\n// Transform tokens to include pre-computed keys to avoid noArrayIndexKey lint\ninterface KeyedToken {\n token: ThemedToken;\n key: string;\n}\ninterface KeyedLine {\n tokens: KeyedToken[];\n key: string;\n}\n\nconst addKeysToTokens = (lines: ThemedToken[][]): KeyedLine[] =>\n lines.map((line, lineIdx) => ({\n key: `line-${lineIdx}`,\n tokens: line.map((token, tokenIdx) => ({\n key: `line-${lineIdx}-${tokenIdx}`,\n token,\n })),\n }));\n\n// Token rendering component\nconst TokenSpan = ({ token }: { token: ThemedToken }) => (\n <span\n className=\"dark:!bg-[var(--shiki-dark-bg)] dark:!text-[var(--shiki-dark)]\"\n style={\n {\n backgroundColor: token.bgColor,\n color: token.color,\n fontStyle: isItalic(token.fontStyle) ? \"italic\" : undefined,\n fontWeight: isBold(token.fontStyle) ? \"bold\" : undefined,\n textDecoration: isUnderline(token.fontStyle) ? \"underline\" : undefined,\n ...token.htmlStyle,\n } as CSSProperties\n }\n >\n {token.content}\n </span>\n);\n\n// Line number styles using CSS counters\nconst LINE_NUMBER_CLASSES = cn(\n \"block\",\n \"before:content-[counter(line)]\",\n \"before:inline-block\",\n \"before:[counter-increment:line]\",\n \"before:w-8\",\n \"before:mr-4\",\n \"before:text-right\",\n \"before:text-muted-foreground/50\",\n \"before:font-mono\",\n \"before:select-none\"\n);\n\n// Line rendering component\nconst LineSpan = ({\n keyedLine,\n showLineNumbers,\n}: {\n keyedLine: KeyedLine;\n showLineNumbers: boolean;\n}) => (\n <span className={showLineNumbers ? LINE_NUMBER_CLASSES : \"block\"}>\n {keyedLine.tokens.length === 0\n ? \"\\n\"\n : keyedLine.tokens.map(({ token, key }) => (\n <TokenSpan key={key} token={token} />\n ))}\n </span>\n);\n\n// Types\ntype CodeBlockProps = HTMLAttributes<HTMLDivElement> & {\n code: string;\n language: BundledLanguage;\n showLineNumbers?: boolean;\n};\n\ninterface TokenizedCode {\n tokens: ThemedToken[][];\n fg: string;\n bg: string;\n}\n\ninterface CodeBlockContextType {\n code: string;\n}\n\n// Context\nconst CodeBlockContext = createContext<CodeBlockContextType>({\n code: \"\",\n});\n\n// Highlighter cache (singleton per language)\nconst highlighterCache = new Map<\n string,\n Promise<HighlighterGeneric<BundledLanguage, BundledTheme>>\n>();\n\n// Token cache\nconst tokensCache = new Map<string, TokenizedCode>();\n\n// Subscribers for async token updates\nconst subscribers = new Map<string, Set<(result: TokenizedCode) => void>>();\n\nconst CACHE_KEY_SLICE_LENGTH = 100;\n\nconst getTokensCacheKey = (code: string, language: BundledLanguage) => {\n const start = code.slice(0, CACHE_KEY_SLICE_LENGTH);\n const end = code.length > CACHE_KEY_SLICE_LENGTH ? code.slice(-CACHE_KEY_SLICE_LENGTH) : \"\";\n return `${language}:${code.length}:${start}:${end}`;\n};\n\nconst getHighlighter = (\n language: BundledLanguage\n): Promise<HighlighterGeneric<BundledLanguage, BundledTheme>> => {\n const cached = highlighterCache.get(language);\n if (cached) {\n return cached;\n }\n\n const highlighterPromise = createHighlighter({\n langs: [language],\n themes: [\"github-light\", \"github-dark\"],\n });\n\n highlighterCache.set(language, highlighterPromise);\n return highlighterPromise;\n};\n\n// Create raw tokens for immediate display while highlighting loads\nconst createRawTokens = (code: string): TokenizedCode => ({\n bg: \"transparent\",\n fg: \"inherit\",\n tokens: code.split(\"\\n\").map((line) =>\n line === \"\"\n ? []\n : [\n {\n color: \"inherit\",\n content: line,\n } as ThemedToken,\n ]\n ),\n});\n\n// Synchronous highlight with callback for async results\nexport const highlightCode = (\n code: string,\n language: BundledLanguage,\n // oxlint-disable-next-line eslint-plugin-promise(prefer-await-to-callbacks)\n callback?: (result: TokenizedCode) => void\n): TokenizedCode | null => {\n const tokensCacheKey = getTokensCacheKey(code, language);\n\n // Return cached result if available\n const cached = tokensCache.get(tokensCacheKey);\n if (cached) {\n return cached;\n }\n\n // Subscribe callback if provided\n if (callback) {\n if (!subscribers.has(tokensCacheKey)) {\n subscribers.set(tokensCacheKey, new Set());\n }\n subscribers.get(tokensCacheKey)?.add(callback);\n }\n\n // Start highlighting in background - fire-and-forget async pattern\n getHighlighter(language)\n // oxlint-disable-next-line eslint-plugin-promise(prefer-await-to-then)\n .then((highlighter) => {\n const availableLangs = highlighter.getLoadedLanguages();\n const langToUse = availableLangs.includes(language) ? language : \"text\";\n\n const result = highlighter.codeToTokens(code, {\n lang: langToUse,\n themes: {\n dark: \"github-dark\",\n light: \"github-light\",\n },\n });\n\n const tokenized: TokenizedCode = {\n bg: result.bg ?? \"transparent\",\n fg: result.fg ?? \"inherit\",\n tokens: result.tokens,\n };\n\n // Cache the result\n tokensCache.set(tokensCacheKey, tokenized);\n\n // Notify all subscribers\n const subs = subscribers.get(tokensCacheKey);\n if (subs) {\n for (const sub of subs) {\n sub(tokenized);\n }\n subscribers.delete(tokensCacheKey);\n }\n })\n // oxlint-disable-next-line eslint-plugin-promise(prefer-await-to-then), eslint-plugin-promise(prefer-await-to-callbacks)\n .catch((error) => {\n console.error(\"Failed to highlight code:\", error);\n subscribers.delete(tokensCacheKey);\n });\n\n return null;\n};\n\nconst CodeBlockBody = memo(\n ({\n tokenized,\n showLineNumbers,\n className,\n }: {\n tokenized: TokenizedCode;\n showLineNumbers: boolean;\n className?: string;\n }) => {\n const preStyle = useMemo(\n () => ({\n backgroundColor: tokenized.bg,\n color: tokenized.fg,\n }),\n [tokenized.bg, tokenized.fg]\n );\n\n const keyedLines = useMemo(\n () => addKeysToTokens(tokenized.tokens),\n [tokenized.tokens]\n );\n\n return (\n <pre\n className={cn(\n \"dark:!bg-[var(--shiki-dark-bg)] dark:!text-[var(--shiki-dark)] m-0 p-4 text-sm\",\n className\n )}\n style={preStyle}\n >\n <code\n className={cn(\n \"font-mono text-sm\",\n showLineNumbers && \"[counter-increment:line_0] [counter-reset:line]\"\n )}\n >\n {keyedLines.map((keyedLine) => (\n <LineSpan\n key={keyedLine.key}\n keyedLine={keyedLine}\n showLineNumbers={showLineNumbers}\n />\n ))}\n </code>\n </pre>\n );\n },\n (prevProps, nextProps) =>\n prevProps.tokenized === nextProps.tokenized &&\n prevProps.showLineNumbers === nextProps.showLineNumbers &&\n prevProps.className === nextProps.className\n);\n\nCodeBlockBody.displayName = \"CodeBlockBody\";\n\nexport const CodeBlockContainer = ({\n className,\n language,\n style,\n ...props\n}: HTMLAttributes<HTMLDivElement> & { language: string }) => (\n <div\n className={cn(\n \"group relative w-full overflow-hidden rounded-md border bg-background text-foreground\",\n className\n )}\n data-language={language}\n style={{\n containIntrinsicSize: \"auto 200px\",\n contentVisibility: \"auto\",\n ...style,\n }}\n {...props}\n />\n);\n\nexport const CodeBlockHeader = ({\n children,\n className,\n ...props\n}: HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex items-center justify-between border-b bg-muted/80 px-3 py-2 text-muted-foreground text-xs\",\n className\n )}\n {...props}\n >\n {children}\n </div>\n);\n\nexport const CodeBlockTitle = ({\n children,\n className,\n ...props\n}: HTMLAttributes<HTMLDivElement>) => (\n <div className={cn(\"flex items-center gap-2\", className)} {...props}>\n {children}\n </div>\n);\n\nexport const CodeBlockFilename = ({\n children,\n className,\n ...props\n}: HTMLAttributes<HTMLSpanElement>) => (\n <span className={cn(\"font-mono\", className)} {...props}>\n {children}\n </span>\n);\n\nexport const CodeBlockActions = ({\n children,\n className,\n ...props\n}: HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\"-my-1 -mr-1 flex items-center gap-2\", className)}\n {...props}\n >\n {children}\n </div>\n);\n\nexport const CodeBlockContent = ({\n code,\n language,\n showLineNumbers = false,\n}: {\n code: string;\n language: BundledLanguage;\n showLineNumbers?: boolean;\n}) => {\n // Memoized raw tokens for immediate display\n const rawTokens = useMemo(() => createRawTokens(code), [code]);\n\n // Synchronous cache lookup — avoids setState in effect for cached results\n const syncTokens = useMemo(\n () => highlightCode(code, language) ?? rawTokens,\n [code, language, rawTokens]\n );\n\n // Async highlighting result (populated after shiki loads)\n const [asyncTokens, setAsyncTokens] = useState<TokenizedCode | null>(null);\n const asyncKeyRef = useRef({ code, language });\n\n // Invalidate stale async tokens synchronously during render\n if (\n asyncKeyRef.current.code !== code ||\n asyncKeyRef.current.language !== language\n ) {\n asyncKeyRef.current = { code, language };\n setAsyncTokens(null);\n }\n\n useEffect(() => {\n let cancelled = false;\n\n highlightCode(code, language, (result) => {\n if (!cancelled) {\n setAsyncTokens(result);\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [code, language]);\n\n const tokenized = asyncTokens ?? syncTokens;\n\n return (\n <div className=\"relative overflow-auto\">\n <CodeBlockBody showLineNumbers={showLineNumbers} tokenized={tokenized} />\n </div>\n );\n};\n\nexport const CodeBlock = ({\n code,\n language,\n showLineNumbers = false,\n className,\n children,\n ...props\n}: CodeBlockProps) => {\n const contextValue = useMemo(() => ({ code }), [code]);\n\n return (\n <CodeBlockContext.Provider value={contextValue}>\n <CodeBlockContainer className={className} language={language} {...props}>\n {children}\n <CodeBlockContent\n code={code}\n language={language}\n showLineNumbers={showLineNumbers}\n />\n </CodeBlockContainer>\n </CodeBlockContext.Provider>\n );\n};\n\nexport type CodeBlockCopyButtonProps = ComponentProps<typeof Button> & {\n onCopy?: () => void;\n onError?: (error: Error) => void;\n timeout?: number;\n};\n\nexport const CodeBlockCopyButton = ({\n onCopy,\n onError,\n timeout = 2000,\n children,\n className,\n ...props\n}: CodeBlockCopyButtonProps) => {\n const [isCopied, setIsCopied] = useState(false);\n const timeoutRef = useRef<number>(0);\n const { code } = useContext(CodeBlockContext);\n\n const copyToClipboard = useCallback(async () => {\n if (typeof window === \"undefined\" || !navigator?.clipboard?.writeText) {\n onError?.(new Error(\"Clipboard API not available\"));\n return;\n }\n\n try {\n if (!isCopied) {\n await navigator.clipboard.writeText(code);\n setIsCopied(true);\n onCopy?.();\n timeoutRef.current = window.setTimeout(\n () => setIsCopied(false),\n timeout\n );\n }\n } catch (error) {\n onError?.(error as Error);\n }\n }, [code, onCopy, onError, timeout, isCopied]);\n\n useEffect(\n () => () => {\n window.clearTimeout(timeoutRef.current);\n },\n []\n );\n\n const Icon = isCopied ? CheckIcon : CopyIcon;\n\n return (\n <Button\n className={cn(\"shrink-0\", className)}\n onClick={copyToClipboard}\n size=\"icon\"\n variant=\"ghost\"\n {...props}\n >\n {children ?? <Icon size={14} />}\n </Button>\n );\n};\n\nexport type CodeBlockLanguageSelectorProps = ComponentProps<typeof Select>;\n\nexport const CodeBlockLanguageSelector = (\n props: CodeBlockLanguageSelectorProps\n) => <Select {...props} />;\n\nexport type CodeBlockLanguageSelectorTriggerProps = ComponentProps<\n typeof SelectTrigger\n>;\n\nexport const CodeBlockLanguageSelectorTrigger = ({\n className,\n ...props\n}: CodeBlockLanguageSelectorTriggerProps) => (\n <SelectTrigger\n className={cn(\n \"h-7 border-none bg-transparent px-2 text-xs shadow-none\",\n className\n )}\n size=\"sm\"\n {...props}\n />\n);\n\nexport type CodeBlockLanguageSelectorValueProps = ComponentProps<\n typeof SelectValue\n>;\n\nexport const CodeBlockLanguageSelectorValue = (\n props: CodeBlockLanguageSelectorValueProps\n) => <SelectValue {...props} />;\n\nexport type CodeBlockLanguageSelectorContentProps = ComponentProps<\n typeof SelectContent\n>;\n\nexport const CodeBlockLanguageSelectorContent = ({\n align = \"end\",\n ...props\n}: CodeBlockLanguageSelectorContentProps) => (\n <SelectContent align={align} {...props} />\n);\n\nexport type CodeBlockLanguageSelectorItemProps = ComponentProps<\n typeof SelectItem\n>;\n\nexport const CodeBlockLanguageSelectorItem = (\n props: CodeBlockLanguageSelectorItemProps\n) => <SelectItem {...props} />;\n"],"names":["isItalic","fontStyle","isBold","isUnderline","addKeysToTokens","lines","line","lineIdx","token","tokenIdx","TokenSpan","jsx","LINE_NUMBER_CLASSES","cn","LineSpan","keyedLine","showLineNumbers","key","CodeBlockContext","createContext","highlighterCache","tokensCache","subscribers","CACHE_KEY_SLICE_LENGTH","getTokensCacheKey","code","language","start","end","getHighlighter","cached","highlighterPromise","createHighlighter","createRawTokens","highlightCode","callback","tokensCacheKey","highlighter","langToUse","result","tokenized","subs","sub","error","CodeBlockBody","memo","className","preStyle","useMemo","keyedLines","prevProps","nextProps","CodeBlockContainer","style","props","CodeBlockHeader","children","CodeBlockTitle","CodeBlockFilename","CodeBlockActions","CodeBlockContent","rawTokens","syncTokens","asyncTokens","setAsyncTokens","useState","asyncKeyRef","useRef","useEffect","cancelled","CodeBlock","contextValue","jsxs","CodeBlockCopyButton","onCopy","onError","timeout","isCopied","setIsCopied","timeoutRef","useContext","copyToClipboard","useCallback","Icon","CheckIcon","CopyIcon","Button","CodeBlockLanguageSelector","Select","CodeBlockLanguageSelectorTrigger","SelectTrigger","CodeBlockLanguageSelectorValue","SelectValue","CodeBlockLanguageSelectorContent","align","SelectContent","CodeBlockLanguageSelectorItem","SelectItem"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),g=require("lucide-react"),f=require("radix-ui"),R=require("./data-table.cjs"),x=require("../button.cjs"),O=require("../input.cjs"),s=require("../select.cjs"),y=require("../../../lib/utils.cjs"),P={contains:"contains",equals:"equals",not_equals:"not equals",starts_with:"starts with",ends_with:"ends with",is_empty:"is empty",is_not_empty:"is not empty"},A=["is_empty","is_not_empty"],_=["contains","equals","not_equals","starts_with","ends_with","is_empty","is_not_empty"];function L(l,r,n){return r[l]??n?.columnDef?.meta?.label??(typeof n?.columnDef?.header=="string"?n.columnDef.header:void 0)??l}function E(l,r){const n=r?.[0]??"contains";return{id:crypto.randomUUID(),columnId:l,operator:n,value:""}}function I({className:l}){const{table:r,columnLabels:n,filters:o,setFilters:u,filterConfig:v,enableFiltering:F}=R.useDataTable();if(!F)return null;const j=r.getAllLeafColumns(),i=v.length>0?v:j.filter(t=>"accessorKey"in t.columnDef||"accessorFn"in t.columnDef).map(t=>({columnId:t.id})),b=i[0],N=b?.columnId??"";function S(){u([...o,E(N,b?.operators)])}function q(t){u(o.filter(m=>m.id!==t))}function h(t,m){u(o.map(c=>c.id===t?{...c,...m}:c))}function z(){u([])}const d=o.length;return e.jsxs(f.Popover.Root,{children:[e.jsx(f.Popover.Trigger,{asChild:!0,children:e.jsxs(x.Button,{type:"button",variant:"outline",size:"sm","data-slot":"data-table-filter",className:y.cn(l),"aria-label":d>0?`Filter (${d} active)`:"Filter",children:[e.jsx(g.ListFilterIcon,{className:"size-3.5"}),"Filter",d>0&&e.jsx("span",{className:"flex size-4 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground",children:d})]})}),e.jsx(f.Popover.Portal,{children:e.jsx(f.Popover.Content,{"data-slot":"data-table-filter-panel",align:"end",sideOffset:4,className:y.cn("z-50 min-w-80 rounded-lg border bg-popover p-3 text-popover-foreground shadow-md outline-none","data-[state=open]:animate-in data-[state=closed]:animate-out","data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0","data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95","data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2"),children:e.jsxs("div",{className:"flex flex-col gap-2",children:[o.map(t=>{const c=i.find(a=>a.columnId===t.columnId)?.operators??_,T=A.includes(t.operator);return e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs(s.Select,{value:t.columnId,onValueChange:a=>{const p=i.find(D=>D.columnId===a)?.operators??_,w=p.includes(t.operator)?t.operator:p[0]??"contains";h(t.id,{columnId:a,operator:w,value:""})},children:[e.jsx(s.SelectTrigger,{size:"sm",className:"w-36",children:e.jsx(s.SelectValue,{})}),e.jsx(s.SelectContent,{children:i.map(a=>{const C=j.find(p=>p.id===a.columnId);return e.jsx(s.SelectItem,{value:a.columnId,children:a.label??L(a.columnId,n,C)},a.columnId)})})]}),e.jsxs(s.Select,{value:t.operator,onValueChange:a=>h(t.id,{operator:a,value:""}),children:[e.jsx(s.SelectTrigger,{size:"sm",className:"w-32",children:e.jsx(s.SelectValue,{})}),e.jsx(s.SelectContent,{children:c.map(a=>e.jsx(s.SelectItem,{value:a,children:P[a]},a))})]}),T?e.jsx("div",{className:"h-8 w-40","aria-hidden":!0}):e.jsx(O.Input,{className:"w-40",placeholder:"Value…",value:t.value,onChange:a=>h(t.id,{value:a.target.value})}),e.jsx(x.Button,{type:"button",variant:"ghost",size:"icon",className:"size-8 shrink-0 text-muted-foreground hover:text-foreground",onClick:()=>q(t.id),"aria-label":"Remove filter",children:e.jsx(g.XIcon,{className:"size-3.5"})})]},t.id)}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs(x.Button,{type:"button",variant:"outline",size:"sm",onClick:S,disabled:i.length===0,children:[e.jsx(g.PlusIcon,{className:"size-3.5"}),"Add filter"]}),o.length>0&&e.jsx(x.Button,{type:"button",variant:"ghost",size:"sm",className:"text-muted-foreground",onClick:z,children:"Clear all"})]})]})})})]})}I.displayName="DataTableFilter";exports.DataTableFilter=I;
2
+ //# sourceMappingURL=data-table-filter.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-table-filter.cjs","sources":["../../../../src/components/ui/data-table/data-table-filter.tsx"],"sourcesContent":["\"use client\"\n\nimport { ListFilterIcon, PlusIcon, XIcon } from \"lucide-react\"\nimport { Popover } from \"radix-ui\"\n\nimport { useDataTable } from \"./data-table\"\n\nimport type { FilterColumnConfig, FilterCondition, FilterOperator } from \"./data-table\"\n\nimport { Button } from \"@/components/ui/button\"\nimport { Input } from \"@/components/ui/input\"\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@/components/ui/select\"\nimport { cn } from \"@/lib/utils\"\n\n// ---------------------------------------------------------------------------\n// Operator metadata\n// ---------------------------------------------------------------------------\n\nconst OPERATOR_LABELS: Record<FilterOperator, string> = {\n contains: \"contains\",\n equals: \"equals\",\n not_equals: \"not equals\",\n starts_with: \"starts with\",\n ends_with: \"ends with\",\n is_empty: \"is empty\",\n is_not_empty: \"is not empty\",\n}\n\nconst VALUE_FREE_OPERATORS: FilterOperator[] = [\"is_empty\", \"is_not_empty\"]\n\nconst DEFAULT_OPERATORS: FilterOperator[] = [\n \"contains\",\n \"equals\",\n \"not_equals\",\n \"starts_with\",\n \"ends_with\",\n \"is_empty\",\n \"is_not_empty\",\n]\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction getColumnLabel(\n colId: string,\n columnLabels: Record<string, string>,\n\n col: any,\n): string {\n return (\n columnLabels[colId] ??\n (col?.columnDef?.meta as { label?: string } | undefined)?.label ??\n (typeof col?.columnDef?.header === \"string\" ? col.columnDef.header : undefined) ??\n colId\n )\n}\n\nfunction makeCondition(\n columnId: string,\n allowedOperators?: FilterOperator[],\n): FilterCondition {\n const operator = allowedOperators?.[0] ?? \"contains\"\n return { id: crypto.randomUUID(), columnId, operator, value: \"\" }\n}\n\n// ---------------------------------------------------------------------------\n// DataTableFilter\n// ---------------------------------------------------------------------------\n\ninterface DataTableFilterProps {\n className?: string\n}\n\nfunction DataTableFilter({ className }: DataTableFilterProps) {\n const { table, columnLabels, filters, setFilters, filterConfig, enableFiltering } =\n useDataTable()\n\n if (!enableFiltering) return null\n\n const allLeafColumns = table.getAllLeafColumns()\n\n const resolvedColumns: FilterColumnConfig[] =\n filterConfig.length > 0\n ? filterConfig\n : allLeafColumns\n .filter((col) => \"accessorKey\" in col.columnDef || \"accessorFn\" in col.columnDef)\n .map((col) => ({ columnId: col.id }))\n\n const firstColumn = resolvedColumns[0]\n const firstColumnId = firstColumn?.columnId ?? \"\"\n\n function addFilter() {\n setFilters([...filters, makeCondition(firstColumnId, firstColumn?.operators)])\n }\n\n function removeFilter(id: string) {\n setFilters(filters.filter((f) => f.id !== id))\n }\n\n function updateFilter(id: string, patch: Partial<FilterCondition>) {\n setFilters(filters.map((f) => (f.id === id ? { ...f, ...patch } : f)))\n }\n\n function clearAll() {\n setFilters([])\n }\n\n const activeCount = filters.length\n\n return (\n <Popover.Root>\n <Popover.Trigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n data-slot=\"data-table-filter\"\n className={cn(className)}\n aria-label={activeCount > 0 ? `Filter (${activeCount} active)` : \"Filter\"}\n >\n <ListFilterIcon className=\"size-3.5\" />\n Filter\n {activeCount > 0 && (\n <span className=\"flex size-4 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground\">\n {activeCount}\n </span>\n )}\n </Button>\n </Popover.Trigger>\n\n <Popover.Portal>\n <Popover.Content\n data-slot=\"data-table-filter-panel\"\n align=\"end\"\n sideOffset={4}\n className={cn(\n \"z-50 min-w-80 rounded-lg border bg-popover p-3 text-popover-foreground shadow-md outline-none\",\n \"data-[state=open]:animate-in data-[state=closed]:animate-out\",\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n \"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n \"data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2\",\n )}\n >\n <div className=\"flex flex-col gap-2\">\n {filters.map((condition) => {\n const colConfig = resolvedColumns.find((c) => c.columnId === condition.columnId)\n const operators = colConfig?.operators ?? DEFAULT_OPERATORS\n const isValueFree = VALUE_FREE_OPERATORS.includes(condition.operator)\n\n return (\n <div key={condition.id} className=\"flex items-center gap-2\">\n {/* Column selector */}\n <Select\n value={condition.columnId}\n onValueChange={(v) => {\n const nextConfig = resolvedColumns.find((c) => c.columnId === v)\n const nextOperators = nextConfig?.operators ?? DEFAULT_OPERATORS\n const nextOperator = nextOperators.includes(condition.operator)\n ? condition.operator\n : (nextOperators[0] ?? \"contains\")\n updateFilter(condition.id, {\n columnId: v,\n operator: nextOperator,\n value: \"\",\n })\n }}\n >\n <SelectTrigger size=\"sm\" className=\"w-36\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {resolvedColumns.map((c) => {\n const col = allLeafColumns.find((lc) => lc.id === c.columnId)\n return (\n <SelectItem key={c.columnId} value={c.columnId}>\n {c.label ?? getColumnLabel(c.columnId, columnLabels, col)}\n </SelectItem>\n )\n })}\n </SelectContent>\n </Select>\n\n {/* Operator selector */}\n <Select\n value={condition.operator}\n onValueChange={(v) =>\n updateFilter(condition.id, {\n operator: v as FilterOperator,\n value: \"\",\n })\n }\n >\n <SelectTrigger size=\"sm\" className=\"w-32\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {operators.map((op) => (\n <SelectItem key={op} value={op}>\n {OPERATOR_LABELS[op]}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n {/* Value input — hidden for value-free operators */}\n {isValueFree ? (\n <div className=\"h-8 w-40\" aria-hidden />\n ) : (\n <Input\n className=\"w-40\"\n placeholder=\"Value…\"\n value={condition.value}\n onChange={(e) => updateFilter(condition.id, { value: e.target.value })}\n />\n )}\n\n {/* Remove button */}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-8 shrink-0 text-muted-foreground hover:text-foreground\"\n onClick={() => removeFilter(condition.id)}\n aria-label=\"Remove filter\"\n >\n <XIcon className=\"size-3.5\" />\n </Button>\n </div>\n )\n })}\n\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={addFilter}\n disabled={resolvedColumns.length === 0}\n >\n <PlusIcon className=\"size-3.5\" />\n Add filter\n </Button>\n {filters.length > 0 && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-muted-foreground\"\n onClick={clearAll}\n >\n Clear all\n </Button>\n )}\n </div>\n </div>\n </Popover.Content>\n </Popover.Portal>\n </Popover.Root>\n )\n}\n\nDataTableFilter.displayName = \"DataTableFilter\"\n\nexport { DataTableFilter }\nexport type { DataTableFilterProps }\n"],"names":["OPERATOR_LABELS","VALUE_FREE_OPERATORS","DEFAULT_OPERATORS","getColumnLabel","colId","columnLabels","col","makeCondition","columnId","allowedOperators","operator","DataTableFilter","className","table","filters","setFilters","filterConfig","enableFiltering","useDataTable","allLeafColumns","resolvedColumns","firstColumn","firstColumnId","addFilter","removeFilter","id","f","updateFilter","patch","clearAll","activeCount","jsxs","Popover","jsx","Button","cn","ListFilterIcon","condition","operators","c","isValueFree","Select","v","nextOperators","nextOperator","SelectTrigger","SelectValue","SelectContent","lc","SelectItem","op","Input","e","XIcon","PlusIcon"],"mappings":"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"}