@kanzo-tech/ai 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/conversation.d.ts +2 -2
- package/dist/conversation.js.map +1 -1
- package/dist/markdown.d.ts +2 -2
- package/dist/markdown.js.map +1 -1
- package/package.json +4 -4
package/dist/conversation.d.ts
CHANGED
|
@@ -22,8 +22,8 @@ export declare const Conversation: (props: React.ComponentProps<typeof ark.div>)
|
|
|
22
22
|
export declare const ConversationContent: (props: React.ComponentProps<typeof ark.div>) => React.JSX.Element;
|
|
23
23
|
/**
|
|
24
24
|
* The stage for the zero state, not the zero state itself: a centred column with a reading measure,
|
|
25
|
-
* which
|
|
26
|
-
*
|
|
25
|
+
* which the caller's markup is composed into. There is deliberately no `title` / `description` /
|
|
26
|
+
* `icon` prop — `EmptyState` was exactly that and it is a tombstone in `@kanzo-tech/ui`.
|
|
27
27
|
*/
|
|
28
28
|
export declare const ConversationEmpty: (props: React.ComponentProps<typeof ark.div>) => React.JSX.Element;
|
|
29
29
|
/**
|
package/dist/conversation.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation.js","sources":["../src/conversation.tsx"],"sourcesContent":["\"use client\";\n\nimport { ark } from \"@ark-ui/react/factory\";\nimport { ArrowDownIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport { Button, cn } from \"@kanzo-tech/ui\";\n\ninterface ConversationCtx {\n viewportRef: (el: HTMLDivElement | null) => void;\n contentRef: (el: HTMLDivElement | null) => void;\n pinned: boolean;\n scrollToEnd: () => void;\n}\n\nconst Ctx = React.createContext<ConversationCtx | null>(null);\nconst useCtx = (part: string) => {\n const c = React.useContext(Ctx);\n if (!c) throw new Error(`${part} must render inside <Conversation>`);\n return c;\n};\n\n/** A scroll position is fractional and rarely lands on the end exactly; this is the allowance. */\nconst AT_END = 24;\n\n/**\n * The transcript's scroll container, and the pin is the whole component.\n *\n * **The naive version, which both real consumers wrote:** an effect on the message array calling\n * `scrollTo({ top: scrollHeight, behavior: \"smooth\" })`. It follows the tail unconditionally, so a\n * reader who scrolled up to re-read something is dragged back down by the next token, every token,\n * for as long as the answer is streaming. Following is only correct while the reader is *already*\n * at the tail: scrolling up releases the pin, and `ConversationScrollButton` is how they take it\n * back. Roughly forty lines of listener and observer, and no dependency.\n */\nexport const Conversation = (props: React.ComponentProps<typeof ark.div>) => {\n const { className, children, slot, ...rest } = props;\n const [viewport, setViewport] = React.useState<HTMLDivElement | null>(null);\n const [content, setContent] = React.useState<HTMLDivElement | null>(null);\n const [pinned, setPinned] = React.useState(true);\n const pinnedRef = React.useRef(true);\n\n const scrollToEnd = React.useCallback(() => {\n if (!viewport) return;\n pinnedRef.current = true;\n setPinned(true);\n viewport.scrollTop = viewport.scrollHeight;\n }, [viewport]);\n\n React.useEffect(() => {\n if (!viewport) return;\n\n const sync = () => {\n const atEnd = viewport.scrollHeight - viewport.scrollTop - viewport.clientHeight <= AT_END;\n pinnedRef.current = atEnd;\n setPinned(atEnd);\n };\n sync();\n viewport.addEventListener(\"scroll\", sync, { passive: true });\n\n // A stream produces *growth*, not scrolling: the content gets taller under a stationary\n // `scrollTop`, and no scroll event is fired for that. Observing the viewport alone never sees\n // it either — its own box did not change — which is why the inner element is observed too.\n const follow = () => {\n if (pinnedRef.current) viewport.scrollTop = viewport.scrollHeight;\n };\n const observer = typeof ResizeObserver === \"undefined\" ? null : new ResizeObserver(follow);\n observer?.observe(viewport);\n if (content) observer?.observe(content);\n\n return () => {\n viewport.removeEventListener(\"scroll\", sync);\n observer?.disconnect();\n };\n }, [viewport, content]);\n\n return (\n <Ctx.Provider\n value={{ viewportRef: setViewport, contentRef: setContent, pinned, scrollToEnd }}\n >\n <ark.div\n className={cn(\"relative flex min-h-0 flex-1 flex-col\", className)}\n {...rest}\n data-slot={slot ?? \"conversation\"}\n >\n {children}\n </ark.div>\n </Ctx.Provider>\n );\n};\n\n/**\n * The scrolling region.\n *\n * ARIA: `role=\"log\"` carries an implicit `aria-live=\"polite\"`, so a message appended while the\n * reader is somewhere else is announced after what they are on rather than over it. Nothing inside\n * declares a role — a message is content, not a widget — so there is no keyboard contract to owe.\n */\nexport const ConversationContent = (props: React.ComponentProps<typeof ark.div>) => {\n const { className, children, slot, ...rest } = props;\n const ctx = useCtx(\"ConversationContent\");\n\n return (\n <ark.div\n className={cn(\n \"flex min-h-0 flex-1 flex-col overflow-y-auto overscroll-contain\",\n \"px-4 py-6\",\n className\n )}\n ref={ctx.viewportRef}\n role=\"log\"\n {...rest}\n data-slot={slot ?? \"conversation-content\"}\n >\n {/* `flex-1` gives the inner element the viewport's height when the transcript is short, which\n is what lets `ConversationEmpty` centre itself in the panel rather than sit at the top. */}\n <ark.div\n className=\"mx-auto flex w-full max-w-3xl flex-1 flex-col gap-6\"\n ref={ctx.contentRef}\n >\n {children}\n </ark.div>\n </ark.div>\n );\n};\n\n/**\n * The stage for the zero state, not the zero state itself: a centred column with a reading measure,\n * which
|
|
1
|
+
{"version":3,"file":"conversation.js","sources":["../src/conversation.tsx"],"sourcesContent":["\"use client\";\n\nimport { ark } from \"@ark-ui/react/factory\";\nimport { ArrowDownIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport { Button, cn } from \"@kanzo-tech/ui\";\n\ninterface ConversationCtx {\n viewportRef: (el: HTMLDivElement | null) => void;\n contentRef: (el: HTMLDivElement | null) => void;\n pinned: boolean;\n scrollToEnd: () => void;\n}\n\nconst Ctx = React.createContext<ConversationCtx | null>(null);\nconst useCtx = (part: string) => {\n const c = React.useContext(Ctx);\n if (!c) throw new Error(`${part} must render inside <Conversation>`);\n return c;\n};\n\n/** A scroll position is fractional and rarely lands on the end exactly; this is the allowance. */\nconst AT_END = 24;\n\n/**\n * The transcript's scroll container, and the pin is the whole component.\n *\n * **The naive version, which both real consumers wrote:** an effect on the message array calling\n * `scrollTo({ top: scrollHeight, behavior: \"smooth\" })`. It follows the tail unconditionally, so a\n * reader who scrolled up to re-read something is dragged back down by the next token, every token,\n * for as long as the answer is streaming. Following is only correct while the reader is *already*\n * at the tail: scrolling up releases the pin, and `ConversationScrollButton` is how they take it\n * back. Roughly forty lines of listener and observer, and no dependency.\n */\nexport const Conversation = (props: React.ComponentProps<typeof ark.div>) => {\n const { className, children, slot, ...rest } = props;\n const [viewport, setViewport] = React.useState<HTMLDivElement | null>(null);\n const [content, setContent] = React.useState<HTMLDivElement | null>(null);\n const [pinned, setPinned] = React.useState(true);\n const pinnedRef = React.useRef(true);\n\n const scrollToEnd = React.useCallback(() => {\n if (!viewport) return;\n pinnedRef.current = true;\n setPinned(true);\n viewport.scrollTop = viewport.scrollHeight;\n }, [viewport]);\n\n React.useEffect(() => {\n if (!viewport) return;\n\n const sync = () => {\n const atEnd = viewport.scrollHeight - viewport.scrollTop - viewport.clientHeight <= AT_END;\n pinnedRef.current = atEnd;\n setPinned(atEnd);\n };\n sync();\n viewport.addEventListener(\"scroll\", sync, { passive: true });\n\n // A stream produces *growth*, not scrolling: the content gets taller under a stationary\n // `scrollTop`, and no scroll event is fired for that. Observing the viewport alone never sees\n // it either — its own box did not change — which is why the inner element is observed too.\n const follow = () => {\n if (pinnedRef.current) viewport.scrollTop = viewport.scrollHeight;\n };\n const observer = typeof ResizeObserver === \"undefined\" ? null : new ResizeObserver(follow);\n observer?.observe(viewport);\n if (content) observer?.observe(content);\n\n return () => {\n viewport.removeEventListener(\"scroll\", sync);\n observer?.disconnect();\n };\n }, [viewport, content]);\n\n return (\n <Ctx.Provider\n value={{ viewportRef: setViewport, contentRef: setContent, pinned, scrollToEnd }}\n >\n <ark.div\n className={cn(\"relative flex min-h-0 flex-1 flex-col\", className)}\n {...rest}\n data-slot={slot ?? \"conversation\"}\n >\n {children}\n </ark.div>\n </Ctx.Provider>\n );\n};\n\n/**\n * The scrolling region.\n *\n * ARIA: `role=\"log\"` carries an implicit `aria-live=\"polite\"`, so a message appended while the\n * reader is somewhere else is announced after what they are on rather than over it. Nothing inside\n * declares a role — a message is content, not a widget — so there is no keyboard contract to owe.\n */\nexport const ConversationContent = (props: React.ComponentProps<typeof ark.div>) => {\n const { className, children, slot, ...rest } = props;\n const ctx = useCtx(\"ConversationContent\");\n\n return (\n <ark.div\n className={cn(\n \"flex min-h-0 flex-1 flex-col overflow-y-auto overscroll-contain\",\n \"px-4 py-6\",\n className\n )}\n ref={ctx.viewportRef}\n role=\"log\"\n {...rest}\n data-slot={slot ?? \"conversation-content\"}\n >\n {/* `flex-1` gives the inner element the viewport's height when the transcript is short, which\n is what lets `ConversationEmpty` centre itself in the panel rather than sit at the top. */}\n <ark.div\n className=\"mx-auto flex w-full max-w-3xl flex-1 flex-col gap-6\"\n ref={ctx.contentRef}\n >\n {children}\n </ark.div>\n </ark.div>\n );\n};\n\n/**\n * The stage for the zero state, not the zero state itself: a centred column with a reading measure,\n * which the caller's markup is composed into. There is deliberately no `title` / `description` /\n * `icon` prop — `EmptyState` was exactly that and it is a tombstone in `@kanzo-tech/ui`.\n */\nexport const ConversationEmpty = (props: React.ComponentProps<typeof ark.div>) => {\n const { className, slot, ...rest } = props;\n\n return (\n <ark.div\n className={cn(\n \"flex flex-1 flex-col items-center justify-center gap-3\",\n \"mx-auto max-w-md px-4 py-12\",\n \"text-balance text-center text-muted-foreground text-sm\",\n \"[&_svg:not([class*='size-'])]:size-6\",\n className\n )}\n {...rest}\n data-slot={slot ?? \"conversation-empty\"}\n />\n );\n};\n\n/**\n * Present only while the pin is released, and pressing it takes the pin back.\n *\n * It floats *over* a transcript that is still growing underneath it, which is why it enters rather\n * than appears and why it paints its own ground: `variant=\"outline\"` sets `--btn-bg: transparent`,\n * so without a ground of its own the message it covers reads straight through it.\n *\n * It asks for that ground by **moving the variable**, not by writing `bg-background` on top. The\n * two look identical at rest and diverge the moment you hover: the wash is\n * `color-mix(…, var(--btn-bg), …)`, so a `bg-background` that only overrode the resting fill left\n * the hover mixing from `transparent` and the transcript showed through the button exactly when the\n * pointer was on it. `--btn-bg` is the seam the recipe documents; this is what it is for.\n */\nexport const ConversationScrollButton = (props: React.ComponentProps<typeof Button>) => {\n const { className, children, onClick, slot, ...rest } = props;\n const ctx = useCtx(\"ConversationScrollButton\");\n\n if (ctx.pinned) return null;\n\n return (\n <Button\n aria-label={children === undefined ? \"Scroll to the latest message\" : undefined}\n className={cn(\n \"absolute inset-x-0 bottom-4 z-10 mx-auto\",\n // `inset-x-0` is what centres it, and it also stretches it, so the width has to be stated.\n // The icon size states its own — `w-(--size)`, square — and a `w-fit` here would win the\n // merge and hand it the icon's width against the control's height.\n children === undefined ? undefined : \"w-fit\",\n \"[--btn-bg:var(--background)] shadow-lg/5\",\n \"fade-in-0 zoom-in-95 slide-in-from-bottom-2 animate-in\",\n \"motion-reduce:animate-none!\",\n className\n )}\n onClick={(event) => {\n onClick?.(event);\n if (!event.defaultPrevented) ctx.scrollToEnd();\n }}\n pill\n // Icon-only by default; give it words and it becomes a pill wide enough to hold them, the way\n // `PromptInputSubmit` does.\n size={children === undefined ? \"icon-sm\" : \"sm\"}\n variant=\"outline\"\n {...rest}\n slot={slot ?? \"conversation-scroll-button\"}\n >\n {children ?? <ArrowDownIcon />}\n </Button>\n );\n};\n"],"names":[],"mappings":";;;;;;AAcA;AAEE;AACA;AACA;AACF;AAgBE;AAOE;AAG8B;AAGhC;AACE;AAEA;AACE;AACA;AACe;AAEjB;AAMA;AACE;AAAqD;AAGvD;AAIE;AACU;AACZ;AAIA;AAAK;AAAJ;AACoE;AAEnE;AAAK;AAAJ;AACiE;AAC5D;AACe;AAElB;AAAA;AAAA;AACH;AAGN;AAUE;AAGA;AACE;AAAK;AAAJ;AACY;AACT;AACA;AACA;AAAA;AAEO;AACJ;AACD;AACe;AAInB;AAAK;AAAJ;AACW;AACD;AAER;AAAA;AAAA;AACH;AAGN;AAQE;AAEA;AACE;AAAK;AAAJ;AACY;AACT;AACA;AACA;AACA;AACA;AAAA;AAEE;AACe;AAAA;AAGzB;AAgBE;AAGA;AAGE;AAAC;AAAA;AACuE;AAC3D;AACT;AAAA;AAAA;AAAA;AAIqC;AACrC;AACA;AACA;AACA;AAAA;AAGA;AACiC;AACnC;AACI;AAGuC;AACnC;AACJ;AACU;AAEc;AAAA;AAGlC;;;;;;;"}
|
package/dist/markdown.d.ts
CHANGED
|
@@ -19,8 +19,8 @@ import { StreamdownProps } from 'streamdown';
|
|
|
19
19
|
*
|
|
20
20
|
* So it lives here, behind `@kanzo-tech/ai/markdown`, with `streamdown` as an **optional peer** —
|
|
21
21
|
* the same door `@kanzo-tech/ui/editor` stands behind for CodeMirror and `@kanzo-tech/graph/duckdb`
|
|
22
|
-
* for Mosaic
|
|
23
|
-
*
|
|
22
|
+
* for Mosaic. Nothing installs the tarball without the peers any more, so the door is held only by
|
|
23
|
+
* `index.ts` never naming `./markdown` — the `size` budget is what would notice it opening.
|
|
24
24
|
*
|
|
25
25
|
* ## The animation is Streamdown's now, not ours
|
|
26
26
|
*
|
package/dist/markdown.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.js","sources":["../src/markdown.tsx"],"sourcesContent":["// No `\"use client\"` here, and `client-boundary.test.ts` is what caught the first version that had\n// one: this module holds no state, no effect and no handler of its own. `streamdown/dist/index.js`\n// opens with the directive itself, so importing it establishes the boundary — a second one here\n// would opt the wrapper out of server rendering for nothing.\nimport { Streamdown, type StreamdownProps } from \"streamdown\";\nimport { cn } from \"@kanzo-tech/ui\";\n\n/**\n * **`MessageText`, for a source that streams markdown.**\n *\n * `MessageText` takes a plain string and renders it as text, which is right for a model answering\n * in prose and wrong the moment one emits a `**bold`, a list or a fence. The hard part is not the\n * markdown — it is the *incomplete* markdown: a stream delivers `**bo`, then `**bold`, then\n * `**bold**`, and a parser that renders each of those honestly makes the answer flicker between\n * literal asterisks and emphasis as it completes. Streamdown exists to close those, and closing\n * them well is a parser's worth of work rather than a component's.\n *\n * ## Why a subpath, and it is a one-way door\n *\n * **Measured before it was decided:** `streamdown` bundles to **495 kB minified, 128 kB brotli**\n * on its own — `parse5` 123 kB for `rehype-raw`'s HTML, `entities` 51 kB, `marked` 40 kB, the\n * unified stack behind them. `@kanzo-tech/ai`'s whole root barrel is budgeted at 20 kB and measures\n * 8.34. A static import from `index.ts` would take every host that renders plain prose from 8 kB to\n * roughly 136, for a parser it never calls.\n *\n * So it lives here, behind `@kanzo-tech/ai/markdown`, with `streamdown` as an **optional peer** —\n * the same door `@kanzo-tech/ui/editor` stands behind for CodeMirror and `@kanzo-tech/graph/duckdb`\n * for Mosaic
|
|
1
|
+
{"version":3,"file":"markdown.js","sources":["../src/markdown.tsx"],"sourcesContent":["// No `\"use client\"` here, and `client-boundary.test.ts` is what caught the first version that had\n// one: this module holds no state, no effect and no handler of its own. `streamdown/dist/index.js`\n// opens with the directive itself, so importing it establishes the boundary — a second one here\n// would opt the wrapper out of server rendering for nothing.\nimport { Streamdown, type StreamdownProps } from \"streamdown\";\nimport { cn } from \"@kanzo-tech/ui\";\n\n/**\n * **`MessageText`, for a source that streams markdown.**\n *\n * `MessageText` takes a plain string and renders it as text, which is right for a model answering\n * in prose and wrong the moment one emits a `**bold`, a list or a fence. The hard part is not the\n * markdown — it is the *incomplete* markdown: a stream delivers `**bo`, then `**bold`, then\n * `**bold**`, and a parser that renders each of those honestly makes the answer flicker between\n * literal asterisks and emphasis as it completes. Streamdown exists to close those, and closing\n * them well is a parser's worth of work rather than a component's.\n *\n * ## Why a subpath, and it is a one-way door\n *\n * **Measured before it was decided:** `streamdown` bundles to **495 kB minified, 128 kB brotli**\n * on its own — `parse5` 123 kB for `rehype-raw`'s HTML, `entities` 51 kB, `marked` 40 kB, the\n * unified stack behind them. `@kanzo-tech/ai`'s whole root barrel is budgeted at 20 kB and measures\n * 8.34. A static import from `index.ts` would take every host that renders plain prose from 8 kB to\n * roughly 136, for a parser it never calls.\n *\n * So it lives here, behind `@kanzo-tech/ai/markdown`, with `streamdown` as an **optional peer** —\n * the same door `@kanzo-tech/ui/editor` stands behind for CodeMirror and `@kanzo-tech/graph/duckdb`\n * for Mosaic. Nothing installs the tarball without the peers any more, so the door is held only by\n * `index.ts` never naming `./markdown` — the `size` budget is what would notice it opening.\n *\n * ## The animation is Streamdown's now, not ours\n *\n * `MessageText` hand-rolls a per-word cascade whose four rules are all read off Streamdown's\n * `animate.ts` — its docblock says so and names each one. Here the original is present, so it does\n * the work: `animated` with `sep: \"word\"` is the same idea from the source it was copied from, and\n * two implementations of one cascade is one too many. The numbers below are `MessageText`'s, so a\n * transcript that mixes the two components does not visibly change rhythm mid-conversation.\n *\n * **A message that mounts complete did not arrive**, so `streaming` also decides whether anything\n * animates at all — a transcript loaded from history must not replay itself, which is the one rule\n * of `MessageText` that has no counterpart in a prop and has to be spelled here.\n */\nexport interface MessageMarkdownProps\n extends Omit<StreamdownProps, \"children\" | \"mode\" | \"animated\"> {\n /** The answer so far, as markdown. Grows; never rewritten. */\n children: string;\n /** More is coming: closes the incomplete markdown, animates the arrival, draws the caret. */\n streaming?: boolean;\n /**\n * Renames the part. Declared here and nowhere else in the package: every other component takes\n * `slot` for free from `React.HTMLAttributes`, and `StreamdownProps` is react-markdown's\n * `Options` rather than the attributes of an element, so it carries no `slot` to inherit.\n */\n slot?: string;\n}\n\n/** `MessageText`'s cascade, so the two components keep the same rhythm. See its docblock. */\nconst STAGGER = 18;\n\nexport const MessageMarkdown = (props: MessageMarkdownProps) => {\n const { children, streaming = false, className, slot, ...rest } = props;\n\n return (\n // The element exists so the part has one. `Streamdown` renders a `div` of its own but drops\n // every prop it does not consume — measured, not assumed, and `markdown.test.tsx` is what keeps\n // it measured: `data-slot` and `data-streaming` handed to it reach no attribute at all. So the\n // two things a consumer selects on are written here, on an element we own, and only\n // Streamdown's own options are passed down.\n <div\n className={cn(\"min-w-0 break-words\", className)}\n data-streaming={streaming || undefined}\n data-slot={slot ?? \"message-markdown\"}\n >\n <Streamdown\n animated={streaming ? { sep: \"word\", stagger: STAGGER } : false}\n // Streamdown's own caret, rather than the `<span>` `MessageText` draws: it belongs at the\n // end of the last *rendered* block, which only the renderer knows — a caret appended by us\n // would sit after a table rather than inside the paragraph the model is still writing.\n caret={streaming ? \"block\" : undefined}\n mode={streaming ? \"streaming\" : \"static\"}\n // The whole point. `false` renders `**bo` as two asterisks and a `bo`.\n parseIncompleteMarkdown={streaming}\n {...rest}\n >\n {children}\n </Streamdown>\n </div>\n );\n};\n"],"names":["STAGGER","MessageMarkdown","props","children","streaming","className","slot","rest","jsx","cn","Streamdown"],"mappings":";;;AAyDA,MAAMA,IAAU,IAEHC,IAAkB,CAACC,MAAgC;AAC9D,QAAM,EAAE,UAAAC,GAAU,WAAAC,IAAY,IAAO,WAAAC,GAAW,MAAAC,GAAM,GAAGC,MAASL;AAElE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAME,gBAAAM;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAWC,EAAG,uBAAuBJ,CAAS;AAAA,QAC9C,kBAAgBD,KAAa;AAAA,QAC7B,aAAWE,KAAQ;AAAA,QAEnB,UAAA,gBAAAE;AAAA,UAACE;AAAA,UAAA;AAAA,YACC,UAAUN,IAAY,EAAE,KAAK,QAAQ,SAASJ,MAAY;AAAA,YAI1D,OAAOI,IAAY,UAAU;AAAA,YAC7B,MAAMA,IAAY,cAAc;AAAA,YAEhC,yBAAyBA;AAAA,YACxB,GAAGG;AAAA,YAEH,UAAAJ;AAAA,UAAA;AAAA,QAAA;AAAA,MACH;AAAA,IAAA;AAAA;AAGN;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kanzo-tech/ai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Kanzo AI surfaces — the transcript, the composer, a model's reasoning and the calls it makes, plus the field affordances that stream into a value. Sibling of @kanzo-tech/ui, not part of it: a Message has a role and one of the roles is `assistant`, which is domain knowledge the generic vocabulary does not carry.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"react": ">=19",
|
|
34
34
|
"react-dom": ">=19",
|
|
35
35
|
"streamdown": ">=2",
|
|
36
|
-
"@kanzo-tech/ui": "0.
|
|
36
|
+
"@kanzo-tech/ui": "0.11.0"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"tailwind-variants": "^3.2.2"
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"react-dom": "^19.0.0",
|
|
52
52
|
"rollup-plugin-preserve-directives": "^0.4.0",
|
|
53
53
|
"streamdown": "^2.5.0",
|
|
54
|
-
"@kanzo-tech/ui": "0.
|
|
55
|
-
"@kanzo-tech/theme": "0.
|
|
54
|
+
"@kanzo-tech/ui": "0.11.0",
|
|
55
|
+
"@kanzo-tech/theme": "0.11.0"
|
|
56
56
|
},
|
|
57
57
|
"//size-limit": "First measurement taken at the split, so this budget is a change detector from day one rather than a number inherited from somewhere. The four AI-assist modules that moved here cost @kanzo-tech/ui's root barrel about 4 kB when they arrived (38 -> 42 kB, recorded in that package's own note); the barrel is rebaselined down by the same move.",
|
|
58
58
|
"size-limit": [
|