@hachej/boring-ui-kit 0.4.23 → 0.4.25

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 (54) hide show
  1. package/README.md +4 -0
  2. package/bin/boring-ui-blocks.mjs +50 -13
  3. package/dist/chat-view.d.ts +278 -0
  4. package/dist/chat-view.js +393 -0
  5. package/dist/file-tree.js +5 -0
  6. package/package.json +6 -2
  7. package/r/agent-target.json +4 -4
  8. package/r/app-shell.json +9 -9
  9. package/r/board-viewer.json +5 -5
  10. package/r/button.json +1 -1
  11. package/r/diagram-viewer.json +4 -4
  12. package/r/dock.json +1 -1
  13. package/r/documents.json +15 -14
  14. package/r/feedback-preview.json +1 -1
  15. package/r/feedback.json +6 -6
  16. package/r/file-tree.json +5 -5
  17. package/r/highlights.json +3 -3
  18. package/r/html-viewer.json +3 -3
  19. package/r/looks-viewer.json +1 -1
  20. package/r/maquette-viewer.json +5 -5
  21. package/r/markdown-editor.json +3 -3
  22. package/r/mockup-viewer.json +1 -1
  23. package/r/pi-ambient.json +11 -11
  24. package/r/pi-app-kinds.json +27 -0
  25. package/r/pi-app.json +16 -25
  26. package/r/pi-artifacts.json +1 -1
  27. package/r/pi-chat-approval.json +3 -2
  28. package/r/pi-chat-attachments.json +1 -1
  29. package/r/pi-chat-markdown.json +1 -1
  30. package/r/pi-chat-mentions.json +1 -1
  31. package/r/pi-chat-pickers.json +1 -1
  32. package/r/pi-chat-plugins.json +11 -11
  33. package/r/pi-chat-question.json +1 -1
  34. package/r/pi-chat-slash.json +1 -1
  35. package/r/pi-chat-subagent.json +1 -1
  36. package/r/pi-chat-voice.json +2 -2
  37. package/r/pi-chat.json +11 -11
  38. package/r/pi-workspace.json +4 -4
  39. package/r/placed-agent.json +4 -4
  40. package/r/provider-setup.json +2 -2
  41. package/r/record-viewer.json +1 -1
  42. package/r/registry.json +187 -169
  43. package/r/requests-board.json +1 -1
  44. package/r/shared-chat.json +7 -7
  45. package/r/task-list-viewer.json +2 -2
  46. package/r/version-history.json +3 -3
  47. package/r/viewer-code.json +2 -2
  48. package/r/viewer-frame.json +5 -5
  49. package/r/viewer-html.json +4 -4
  50. package/r/viewer-image.json +2 -2
  51. package/r/viewer-markdown.json +5 -5
  52. package/r/viewer-pdf.json +3 -3
  53. package/r/viewers.json +6 -6
  54. package/r/windows.json +1 -1
@@ -10,7 +10,7 @@
10
10
  "@earendil-works/pi-durable@~1.2.0"
11
11
  ],
12
12
  "registryDependencies": [
13
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.23/r/pi-chat.json",
13
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.25/r/pi-chat.json",
14
14
  "button",
15
15
  "item"
16
16
  ],
@@ -4,12 +4,13 @@
4
4
  "title": "Pi chat approval card",
5
5
  "description": "The Approve / Deny card for host tools wrapped with `requireApproval`. Plug it in with `cards={{ approval: ApprovalCard }}`; without it pi-chat shows a \"waiting for your answer\" notice with no answer UI.",
6
6
  "dependencies": [
7
+ "@hachej/boring-ui-kit@^0.4.25",
7
8
  "react@^19.0.0",
8
9
  "lucide-react@^1.51.0",
9
10
  "@earendil-works/pi-ai@~1.2.0"
10
11
  ],
11
12
  "registryDependencies": [
12
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.23/r/pi-chat.json",
13
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.25/r/pi-chat.json",
13
14
  "badge",
14
15
  "button",
15
16
  "item"
@@ -17,7 +18,7 @@
17
18
  "files": [
18
19
  {
19
20
  "path": "registry/pi-chat-approval/approval-card.tsx",
20
- "content": "'use client';\n\nimport { useEffect, useRef, useState } from 'react';\nimport type { ToolCall, ToolResultMessage } from '@earendil-works/pi-ai';\nimport { CheckIcon, ShieldAlertIcon, XIcon } from 'lucide-react';\nimport { Badge } from '@/components/ui/badge';\nimport { Button } from '@/components/ui/button';\nimport { Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemMedia, ItemTitle } from '@/components/ui/item';\nimport type { ApprovalCardProps } from '../pi-chat/config';\nimport { useChatText } from '../pi-chat/labels';\n\n// The strings `requireApproval` (`@hachej/boring-agent/approval`) asks with and puts at the start of a denial result.\nexport const APPROVE = 'Approve';\nexport const DENY = 'Deny';\nexport const DENIED_PREFIX = 'Denied by the person.';\n\n\n/** The arguments the call would run with, one line each. They come from the model's call itself: the card shows what will actually execute. */\nexport function describeArguments(call: ToolCall): { readonly name: string; readonly value: string }[] {\n return Object.entries(call.arguments as Record<string, unknown>).map(([name, value]) => {\n const text = typeof value === 'string' ? value : JSON.stringify(value) ?? '';\n return { name, value: text.length > 600 ? `${text.slice(0, 600)}…` : text };\n });\n}\n\n/**\n * The settled state. The wrapper records its decision in the call's details; a call that settled without one was never\n * decided (the run was stopped while it waited), so it is not shown as approved.\n */\nfunction decisionOf(result: ToolResultMessage | undefined, decision: 'approved' | 'denied' | 'cancelled' | undefined): 'pending' | 'denied' | 'approved' | 'cancelled' {\n if (!result) return 'pending';\n if (decision) return decision;\n const text = result.content.map(part => part.type === 'text' ? part.text : '').join('');\n return result.isError && text.startsWith(DENIED_PREFIX) ? 'denied' : 'cancelled';\n}\n\nexport type { ApprovalCardProps };\n\n/** Human approval for a gated tool call: pending (Approve and Deny), then approved or denied. Answering uses the host's `answer`. */\nexport function ApprovalCard({ call, result, answer, questionId, summary, decision, live = false }: ApprovalCardProps) {\n const { labels } = useChatText();\n const [sent, setSent] = useState<string | undefined>();\n const [sending, setSending] = useState<string | undefined>();\n const [refusal, setRefusal] = useState<string | undefined>();\n const mounted = useRef(true);\n useEffect(() => { mounted.current = true; return () => { mounted.current = false; }; }, []);\n const settled = decisionOf(result, decision);\n // `pending` means the decision waits on the person; `writing` means the model is still generating the call.\n const state = settled !== 'pending' ? settled : sent === APPROVE ? 'approved-sent' : sent === DENY ? 'denied-sent' : live ? 'writing' : 'pending';\n const open = state === 'pending' || state === 'writing';\n const args = describeArguments(call);\n const busy = sending !== undefined || live;\n\n async function decide(value: string) {\n if (!answer || !questionId || busy) return;\n setRefusal(undefined); setSending(value);\n try {\n const outcome = await answer(questionId, value);\n if (outcome.kind === 'answered') setSent(value);\n else setRefusal(outcome.reason || labels.decisionRefused(outcome.kind));\n } catch (cause) { setRefusal(cause instanceof Error ? cause.message : labels.decisionNotSent); }\n finally { if (mounted.current) setSending(undefined); }\n }\n const approved = state === 'approved' || state === 'approved-sent', denied = state === 'denied' || state === 'denied-sent';\n\n const status = open ? labels.needsApproval : approved ? labels.approved : denied ? labels.denied : labels.notDecided;\n const unavailable = !answer || !questionId || busy;\n\n // shadcn's Confirmation recipe: an outline Item with the question and Approve/Deny actions; pending approvals are prompts, not activity.\n return <Item variant=\"outline\" size=\"sm\" render={<section />} data-testid=\"approval-card\" data-state={state} data-call-id={call.id} data-tool={call.name} aria-label={labels.approvalNeeded} className=\"my-2 text-start\">\n <ItemMedia variant=\"icon\">\n {approved ? <CheckIcon aria-hidden=\"true\" /> : denied ? <XIcon aria-hidden=\"true\" /> : <ShieldAlertIcon aria-hidden=\"true\" />}\n </ItemMedia>\n <ItemContent className=\"min-w-0 basis-0\">\n <ItemDescription className=\"line-clamp-none\"><Badge variant={open ? 'outline' : denied ? 'destructive' : 'secondary'}>{status}</Badge></ItemDescription>\n {summary && <ItemTitle data-testid=\"approval-summary\" className=\"line-clamp-none [overflow-wrap:anywhere]\">{summary}</ItemTitle>}\n <ItemDescription data-testid=\"approval-tool\" className=\"line-clamp-none [overflow-wrap:anywhere]\">{labels.wantsToRun} <code className=\"font-mono\">{call.name}</code></ItemDescription>\n {args.length > 0 && (() => {\n const list = <dl data-testid=\"approval-args\" className=\"m-0 grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 text-xs\">\n {args.map(item => [<dt key={`${item.name}:n`} className=\"text-muted-foreground\">{item.name}</dt>, <dd key={`${item.name}:v`} className=\"m-0 font-mono whitespace-pre-wrap [overflow-wrap:anywhere]\">{item.value}</dd>])}\n </dl>;\n // With a summary the raw arguments are the detail behind it; without one they are all the person has to decide on.\n return summary ? <details data-testid=\"approval-details\" className=\"text-xs\"><summary className=\"inline-flex min-h-8 cursor-pointer items-center text-muted-foreground select-none max-sm:min-h-11 pointer-coarse:min-h-11\">{labels.arguments}</summary>{list}</details> : list;\n })()}\n </ItemContent>\n {open && <ItemActions role=\"group\" aria-label={labels.decision}>\n <Button variant=\"default\" data-testid=\"approval-approve\" disabled={unavailable} onClick={() => { void decide(APPROVE); }}><CheckIcon data-icon=\"inline-start\" aria-hidden=\"true\" />{labels.approve}</Button>\n <Button variant=\"outline\" data-testid=\"approval-deny\" disabled={unavailable} onClick={() => { void decide(DENY); }}><XIcon data-icon=\"inline-start\" aria-hidden=\"true\" />{labels.deny}</Button>\n </ItemActions>}\n {(open && (live || !answer || !questionId)) && <ItemFooter><ItemDescription>{live ? labels.stillWritingCall : labels.decidingUnavailable}</ItemDescription></ItemFooter>}\n {(state === 'approved-sent' || state === 'denied-sent') && <ItemFooter><ItemDescription>{labels.decisionSent(approved)}</ItemDescription></ItemFooter>}\n {refusal && open && <ItemFooter><p role=\"alert\" data-testid=\"approval-refusal\" className=\"m-0 text-sm text-destructive\">{refusal}</p></ItemFooter>}\n </Item>;\n}\n",
21
+ "content": "'use client';\n\nimport { useEffect, useRef, useState } from 'react';\nimport type { ToolCall, ToolResultMessage } from '@earendil-works/pi-ai';\nimport { CheckIcon, ShieldAlertIcon, XIcon } from 'lucide-react';\nimport { waits, type ApprovalDecision } from '@hachej/boring-ui-kit/chat-view';\nimport { Badge } from '@/components/ui/badge';\nimport { Button } from '@/components/ui/button';\nimport { Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemMedia, ItemTitle } from '@/components/ui/item';\nimport type { ApprovalCardProps } from '../pi-chat/config';\nimport { useChatText } from '../pi-chat/labels';\n\n// The strings `requireApproval` (`@hachej/boring-agent/approval`) asks with and puts at the start of a denial result.\nexport const APPROVE = 'Approve';\nexport const DENY = 'Deny';\nexport const DENIED_PREFIX = 'Denied by the person.';\n\n\n/** The arguments the call would run with, one line each. They come from the model's call itself: the card shows what will actually execute. */\nexport function describeArguments(call: ToolCall): { readonly name: string; readonly value: string }[] {\n return Object.entries(call.arguments as Record<string, unknown>).map(([name, value]) => {\n const text = typeof value === 'string' ? value : JSON.stringify(value) ?? '';\n return { name, value: text.length > 600 ? `${text.slice(0, 600)}…` : text };\n });\n}\n\n/**\n * The decision, once there is one. The wrapper records it in the call's details as soon as it is made, before the call finishes; a call\n * that settled without one was never decided (the run was stopped while it waited), so it is not shown as approved.\n */\nfunction decisionOf(result: ToolResultMessage | undefined, decision: ApprovalDecision | undefined): ApprovalDecision | undefined {\n if (decision) return decision;\n if (!result) return undefined;\n const text = result.content.map(part => part.type === 'text' ? part.text : '').join('');\n return result.isError && text.startsWith(DENIED_PREFIX) ? 'denied' : 'cancelled';\n}\n\nexport type { ApprovalCardProps };\n\n/** Human approval for a gated tool call: pending (Approve and Deny), then approved or denied. Answering uses the host's `answer`. */\nexport function ApprovalCard({ call, result, answer, questionId, summary, decision, live = false }: ApprovalCardProps) {\n const { labels } = useChatText();\n const [sent, setSent] = useState<string | undefined>();\n const [sending, setSending] = useState<string | undefined>();\n const [refusal, setRefusal] = useState<string | undefined>();\n const mounted = useRef(true);\n useEffect(() => { mounted.current = true; return () => { mounted.current = false; }; }, []);\n // `pending` is the kit's waiting rule (`waits`, as the chat's status line uses); `writing` means the model is still generating the call.\n const waiting = waits({ kind: 'approval', key: call.id, call, result, live, questionId, summary: summary ?? '', ...(decision ? { decision } : {}) });\n const state = decisionOf(result, decision) ?? (sent === APPROVE ? 'approved-sent' : sent === DENY ? 'denied-sent' : waiting ? 'pending' : 'writing');\n const open = state === 'pending' || state === 'writing';\n const args = describeArguments(call);\n const busy = sending !== undefined || live;\n\n async function decide(value: string) {\n if (!answer || !questionId || busy) return;\n setRefusal(undefined); setSending(value);\n try {\n const outcome = await answer(questionId, value);\n if (outcome.kind === 'answered') setSent(value);\n else setRefusal(outcome.reason || labels.decisionRefused(outcome.kind));\n } catch (cause) { setRefusal(cause instanceof Error ? cause.message : labels.decisionNotSent); }\n finally { if (mounted.current) setSending(undefined); }\n }\n const approved = state === 'approved' || state === 'approved-sent', denied = state === 'denied' || state === 'denied-sent';\n\n const status = open ? labels.needsApproval : approved ? labels.approved : denied ? labels.denied : labels.notDecided;\n const unavailable = !answer || !questionId || busy;\n\n // shadcn's Confirmation recipe: an outline Item with the question and Approve/Deny actions; pending approvals are prompts, not activity.\n return <Item variant=\"outline\" size=\"sm\" render={<section />} data-testid=\"approval-card\" data-state={state} data-call-id={call.id} data-tool={call.name} aria-label={labels.approvalNeeded} className=\"my-2 text-start\">\n <ItemMedia variant=\"icon\">\n {approved ? <CheckIcon aria-hidden=\"true\" /> : denied ? <XIcon aria-hidden=\"true\" /> : <ShieldAlertIcon aria-hidden=\"true\" />}\n </ItemMedia>\n <ItemContent className=\"min-w-0 basis-0\">\n <ItemDescription className=\"line-clamp-none\"><Badge variant={open ? 'outline' : denied ? 'destructive' : 'secondary'}>{status}</Badge></ItemDescription>\n {summary && <ItemTitle data-testid=\"approval-summary\" className=\"line-clamp-none [overflow-wrap:anywhere]\">{summary}</ItemTitle>}\n <ItemDescription data-testid=\"approval-tool\" className=\"line-clamp-none [overflow-wrap:anywhere]\">{labels.wantsToRun} <code className=\"font-mono\">{call.name}</code></ItemDescription>\n {args.length > 0 && (() => {\n const list = <dl data-testid=\"approval-args\" className=\"m-0 grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 text-xs\">\n {args.map(item => [<dt key={`${item.name}:n`} className=\"text-muted-foreground\">{item.name}</dt>, <dd key={`${item.name}:v`} className=\"m-0 font-mono whitespace-pre-wrap [overflow-wrap:anywhere]\">{item.value}</dd>])}\n </dl>;\n // With a summary the raw arguments are the detail behind it; without one they are all the person has to decide on.\n return summary ? <details data-testid=\"approval-details\" className=\"text-xs\"><summary className=\"inline-flex min-h-8 cursor-pointer items-center text-muted-foreground select-none max-sm:min-h-11 pointer-coarse:min-h-11\">{labels.arguments}</summary>{list}</details> : list;\n })()}\n </ItemContent>\n {open && <ItemActions role=\"group\" aria-label={labels.decision}>\n <Button variant=\"default\" data-testid=\"approval-approve\" disabled={unavailable} onClick={() => { void decide(APPROVE); }}><CheckIcon data-icon=\"inline-start\" aria-hidden=\"true\" />{labels.approve}</Button>\n <Button variant=\"outline\" data-testid=\"approval-deny\" disabled={unavailable} onClick={() => { void decide(DENY); }}><XIcon data-icon=\"inline-start\" aria-hidden=\"true\" />{labels.deny}</Button>\n </ItemActions>}\n {(open && (live || !answer || !questionId)) && <ItemFooter><ItemDescription>{live ? labels.stillWritingCall : labels.decidingUnavailable}</ItemDescription></ItemFooter>}\n {/* Decided, the gated call not finished yet: whoever decided, the run carries on by itself. */}\n {(approved || denied) && !result && <ItemFooter><ItemDescription>{labels.decisionSent(approved)}</ItemDescription></ItemFooter>}\n {refusal && open && <ItemFooter><p role=\"alert\" data-testid=\"approval-refusal\" className=\"m-0 text-sm text-destructive\">{refusal}</p></ItemFooter>}\n </Item>;\n}\n",
21
22
  "type": "registry:component",
22
23
  "target": "components/pi-chat-approval/approval-card.tsx"
23
24
  }
@@ -9,7 +9,7 @@
9
9
  "lucide-react@^1.51.0"
10
10
  ],
11
11
  "registryDependencies": [
12
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.23/r/pi-chat.json",
12
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.25/r/pi-chat.json",
13
13
  "button",
14
14
  "input"
15
15
  ],
@@ -9,7 +9,7 @@
9
9
  "entities@^8.1.0"
10
10
  ],
11
11
  "registryDependencies": [
12
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.23/r/pi-chat.json",
12
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.25/r/pi-chat.json",
13
13
  "button",
14
14
  "checkbox",
15
15
  "table"
@@ -8,7 +8,7 @@
8
8
  "lucide-react@^1.51.0"
9
9
  ],
10
10
  "registryDependencies": [
11
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.23/r/pi-chat.json",
11
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.25/r/pi-chat.json",
12
12
  "item"
13
13
  ],
14
14
  "files": [
@@ -8,7 +8,7 @@
8
8
  "lucide-react@^1.51.0"
9
9
  ],
10
10
  "registryDependencies": [
11
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.23/r/pi-chat.json",
11
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.25/r/pi-chat.json",
12
12
  "button",
13
13
  "dropdown-menu"
14
14
  ],
@@ -7,17 +7,17 @@
7
7
  "react@^19.0.0"
8
8
  ],
9
9
  "registryDependencies": [
10
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.23/r/pi-chat.json",
11
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.23/r/pi-chat-markdown.json",
12
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.23/r/pi-chat-slash.json",
13
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.23/r/pi-chat-mentions.json",
14
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.23/r/pi-chat-pickers.json",
15
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.23/r/pi-chat-voice.json",
16
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.23/r/pi-chat-attachments.json",
17
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.23/r/pi-chat-question.json",
18
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.23/r/pi-chat-approval.json",
19
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.23/r/pi-chat-subagent.json",
20
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.23/r/pi-artifacts.json"
10
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.25/r/pi-chat.json",
11
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.25/r/pi-chat-markdown.json",
12
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.25/r/pi-chat-slash.json",
13
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.25/r/pi-chat-mentions.json",
14
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.25/r/pi-chat-pickers.json",
15
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.25/r/pi-chat-voice.json",
16
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.25/r/pi-chat-attachments.json",
17
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.25/r/pi-chat-question.json",
18
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.25/r/pi-chat-approval.json",
19
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.25/r/pi-chat-subagent.json",
20
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.25/r/pi-artifacts.json"
21
21
  ],
22
22
  "files": [
23
23
  {
@@ -9,7 +9,7 @@
9
9
  "@earendil-works/pi-ai@~1.2.0"
10
10
  ],
11
11
  "registryDependencies": [
12
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.23/r/pi-chat.json",
12
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.25/r/pi-chat.json",
13
13
  "alert",
14
14
  "button",
15
15
  "card",
@@ -8,7 +8,7 @@
8
8
  "lucide-react@^1.51.0"
9
9
  ],
10
10
  "registryDependencies": [
11
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.23/r/pi-chat.json",
11
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.25/r/pi-chat.json",
12
12
  "badge",
13
13
  "button",
14
14
  "input-group",
@@ -9,7 +9,7 @@
9
9
  "@earendil-works/pi-ai@~1.2.0"
10
10
  ],
11
11
  "registryDependencies": [
12
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.23/r/pi-chat.json",
12
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.25/r/pi-chat.json",
13
13
  "alert",
14
14
  "badge",
15
15
  "button",
@@ -4,12 +4,12 @@
4
4
  "title": "Pi chat voice messages",
5
5
  "description": "Hold-to-record voice messages: the recorder hook over the kit's voice capture, the composer's record button and the pending voice bubble, plus useHoldToRecord. Plug it in with `voiceMessage={{ ...config, ...voiceMessagePlugin }}`; without it pi-chat has no voice button.",
6
6
  "dependencies": [
7
- "@hachej/boring-ui-kit@^0.4.23",
7
+ "@hachej/boring-ui-kit@^0.4.25",
8
8
  "react@^19.0.0",
9
9
  "lucide-react@^1.51.0"
10
10
  ],
11
11
  "registryDependencies": [
12
- "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.23/r/pi-chat.json",
12
+ "https://cdn.jsdelivr.net/npm/@hachej/boring-ui-kit@0.4.25/r/pi-chat.json",
13
13
  "bubble",
14
14
  "button",
15
15
  "message",