@bitbaum/chatkit 0.7.0 → 0.8.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/README.md CHANGED
@@ -15,7 +15,7 @@ interface that calls them.)
15
15
  ## Install
16
16
 
17
17
  ```sh
18
- pnpm add @bitbaum/chatkit@^0.6.0
18
+ pnpm add @bitbaum/chatkit@^0.8.0
19
19
  ```
20
20
 
21
21
  **Install releases from npm, never a commit or a branch.** A semver range is
@@ -51,6 +51,37 @@ import "@bitbaum/chatkit/styles.css";
51
51
  />
52
52
  ```
53
53
 
54
+ ### The whole screen — `ChatScreen`
55
+
56
+ A chat page is the conversation, edge to edge and top to bottom, as in the
57
+ ChatGPT and Claude apps. The header and the composer float OVER it instead of
58
+ each taking a strip of the screen: words scroll under both and fade as they
59
+ pass, the header's buttons are frosted pills (`ck-glass`), and the composer is
60
+ a see-through pill. The last line of an answer always rests above the
61
+ composer — the scroller is padded by the measured height of each.
62
+
63
+ ```tsx
64
+ <ChatScreen
65
+ fill="viewport" // or "parent": fill the box you give it
66
+ header={<>
67
+ <button className="ck-glass" aria-label="Chats"><IconMenu /></button>
68
+ <span style={{ flex: 1 }} />
69
+ <button className="ck-glass" aria-label="New chat"><IconPen /></button>
70
+ </>}
71
+ composer={<Composer … />} // plus an error or a queue, if you have one
72
+ footnote="Answers can be wrong."
73
+ >
74
+ <ChatThread … />
75
+ </ChatScreen>
76
+ ```
77
+
78
+ `fill="viewport"` follows the VISUAL viewport, so the composer rides above the
79
+ phone keyboard. An app with its own thread pads its scroller with the two
80
+ measured heights: `padding-block: var(--ck-top-inset) var(--ck-bottom-inset)`.
81
+ The reading column is `--ck-measure` (48rem) wide with `--ck-gutter` (1rem)
82
+ to the edge; the page behind it is `--ck-page`. `demo/screen.html` is the
83
+ reference: look at it at 390px before changing any of this.
84
+
54
85
  ### The microphone
55
86
 
56
87
  `voice` is on by default. Give it a server leg so it works everywhere:
@@ -199,6 +230,8 @@ on `Composer`, `labels` on `ChatThread`.
199
230
  8. Starters in the empty state; suggested replies under the latest answer;
200
231
  the speaker's name on every answer once more than one agent speaks.
201
232
  9. Nothing overflows at 320–390px.
233
+ 10. In `ChatScreen`, the conversation fills the display and opens at the
234
+ latest message, with its end above the composer, not behind it.
202
235
 
203
236
  `pnpm run verify` proves all of it: unit and render tests, then
204
237
  `test/browser.mjs` drives Chromium through the demo at desktop and 390px, light
@@ -0,0 +1,37 @@
1
+ import { type ReactNode } from "react";
2
+ /**
3
+ * The chat as the whole screen, the way the ChatGPT and Claude apps lay it
4
+ * out: the conversation runs edge to edge and top to bottom, and the controls
5
+ * FLOAT over it — a see-through header at the top, the composer at the bottom
6
+ * — instead of each taking a strip of the screen away from the reading.
7
+ *
8
+ * Words scroll under both. They fade out as they pass beneath the header and
9
+ * the composer, so the controls stay legible without an opaque bar behind
10
+ * them; the last line of an answer always rests above the composer, never
11
+ * behind it (the scroller is padded by the measured height of each).
12
+ *
13
+ * The heights are published as `--ck-top-inset` and `--ck-bottom-inset` on
14
+ * the screen, so an app whose thread is its own scroller pads it with them:
15
+ *
16
+ * .my-thread { padding-block: var(--ck-top-inset) var(--ck-bottom-inset); }
17
+ *
18
+ * chatkit's own `ChatThread` does that already.
19
+ */
20
+ export declare function ChatScreen({ header, composer, footnote, children, fill, className, }: {
21
+ /** Floats over the top of the conversation. Give its buttons `ck-glass`. */
22
+ header?: ReactNode;
23
+ /** The composer (and anything that belongs with it: an error, a queue). */
24
+ composer: ReactNode;
25
+ /** One quiet line under the composer ("Answers can be wrong"). */
26
+ footnote?: ReactNode;
27
+ /** The conversation: a `ChatThread`, or an app's own scroller. */
28
+ children: ReactNode;
29
+ /**
30
+ * "parent" fills the box the app gives it. "viewport" takes the visual
31
+ * viewport — the height that shrinks when the phone keyboard opens, so the
32
+ * composer rides on top of the keyboard instead of behind it.
33
+ */
34
+ fill?: "parent" | "viewport";
35
+ className?: string;
36
+ }): import("react").JSX.Element;
37
+ //# sourceMappingURL=Screen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Screen.d.ts","sourceRoot":"","sources":["../../src/react/Screen.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA2B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGhE;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,IAAe,EACf,SAAS,GACV,EAAE;IACD,4EAA4E;IAC5E,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,2EAA2E;IAC3E,QAAQ,EAAE,SAAS,CAAC;IACpB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,kEAAkE;IAClE,QAAQ,EAAE,SAAS,CAAC;IACpB;;;;OAIG;IACH,IAAI,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,+BA2CA"}
@@ -0,0 +1,56 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useLayoutEffect, useRef } from "react";
4
+ import { useViewportHeight } from "./hooks.js";
5
+ /**
6
+ * The chat as the whole screen, the way the ChatGPT and Claude apps lay it
7
+ * out: the conversation runs edge to edge and top to bottom, and the controls
8
+ * FLOAT over it — a see-through header at the top, the composer at the bottom
9
+ * — instead of each taking a strip of the screen away from the reading.
10
+ *
11
+ * Words scroll under both. They fade out as they pass beneath the header and
12
+ * the composer, so the controls stay legible without an opaque bar behind
13
+ * them; the last line of an answer always rests above the composer, never
14
+ * behind it (the scroller is padded by the measured height of each).
15
+ *
16
+ * The heights are published as `--ck-top-inset` and `--ck-bottom-inset` on
17
+ * the screen, so an app whose thread is its own scroller pads it with them:
18
+ *
19
+ * .my-thread { padding-block: var(--ck-top-inset) var(--ck-bottom-inset); }
20
+ *
21
+ * chatkit's own `ChatThread` does that already.
22
+ */
23
+ export function ChatScreen({ header, composer, footnote, children, fill = "parent", className, }) {
24
+ const rootRef = useRef(null);
25
+ const headerRef = useRef(null);
26
+ const dockRef = useRef(null);
27
+ useViewportHeight(rootRef, fill === "viewport");
28
+ const hasHeader = header != null && header !== false;
29
+ // Measured, not guessed: the composer grows with a long draft, a staged
30
+ // screenshot or a dictation problem, and the reading has to move with it.
31
+ useLayoutEffect(() => {
32
+ const root = rootRef.current;
33
+ if (!root)
34
+ return;
35
+ const sync = () => {
36
+ root.style.setProperty("--ck-top-inset", `${headerRef.current?.offsetHeight ?? 0}px`);
37
+ root.style.setProperty("--ck-bottom-inset", `${dockRef.current?.offsetHeight ?? 0}px`);
38
+ };
39
+ sync();
40
+ if (typeof ResizeObserver === "undefined")
41
+ return;
42
+ const observer = new ResizeObserver(sync);
43
+ if (headerRef.current)
44
+ observer.observe(headerRef.current);
45
+ if (dockRef.current)
46
+ observer.observe(dockRef.current);
47
+ return () => observer.disconnect();
48
+ }, [hasHeader]);
49
+ const cls = ["ck-screen"];
50
+ if (fill === "viewport")
51
+ cls.push("ck-screen-viewport");
52
+ if (className)
53
+ cls.push(className);
54
+ return (_jsxs("div", { ref: rootRef, className: cls.join(" "), children: [_jsx("div", { className: "ck-screen-body", children: children }), hasHeader && (_jsx("div", { ref: headerRef, className: "ck-screen-header", children: _jsx("div", { className: "ck-screen-header-row", children: header }) })), _jsx("div", { ref: dockRef, className: "ck-screen-dock", children: _jsxs("div", { className: "ck-screen-column", children: [composer, footnote != null && _jsx("p", { className: "ck-screen-footnote", children: footnote })] }) })] }));
55
+ }
56
+ //# sourceMappingURL=Screen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Screen.js","sourceRoot":"","sources":["../../src/react/Screen.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,eAAe,EAAE,MAAM,EAAkB,MAAM,OAAO,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,UAAU,CAAC,EACzB,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,IAAI,GAAG,QAAQ,EACf,SAAS,GAiBV;IACC,MAAM,OAAO,GAAG,MAAM,CAAwB,IAAI,CAAC,CAAC;IACpD,MAAM,SAAS,GAAG,MAAM,CAAwB,IAAI,CAAC,CAAC;IACtD,MAAM,OAAO,GAAG,MAAM,CAAwB,IAAI,CAAC,CAAC;IACpD,iBAAiB,CAAC,OAAO,EAAE,IAAI,KAAK,UAAU,CAAC,CAAC;IAChD,MAAM,SAAS,GAAG,MAAM,IAAI,IAAI,IAAI,MAAM,KAAK,KAAK,CAAC;IAErD,wEAAwE;IACxE,0EAA0E;IAC1E,eAAe,CAAC,GAAG,EAAE;QACnB,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;QAC7B,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,MAAM,IAAI,GAAG,GAAG,EAAE;YAChB,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,gBAAgB,EAAE,GAAG,SAAS,CAAC,OAAO,EAAE,YAAY,IAAI,CAAC,IAAI,CAAC,CAAC;YACtF,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,mBAAmB,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,YAAY,IAAI,CAAC,IAAI,CAAC,CAAC;QACzF,CAAC,CAAC;QACF,IAAI,EAAE,CAAC;QACP,IAAI,OAAO,cAAc,KAAK,WAAW;YAAE,OAAO;QAClD,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,SAAS,CAAC,OAAO;YAAE,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC3D,IAAI,OAAO,CAAC,OAAO;YAAE,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACvD,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;IACrC,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IAEhB,MAAM,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC;IAC1B,IAAI,IAAI,KAAK,UAAU;QAAE,GAAG,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IACxD,IAAI,SAAS;QAAE,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACnC,OAAO,CACL,eAAK,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,aACzC,cAAK,SAAS,EAAC,gBAAgB,YAAE,QAAQ,GAAO,EAC/C,SAAS,IAAI,CACZ,cAAK,GAAG,EAAE,SAAS,EAAE,SAAS,EAAC,kBAAkB,YAC/C,cAAK,SAAS,EAAC,sBAAsB,YAAE,MAAM,GAAO,GAChD,CACP,EACD,cAAK,GAAG,EAAE,OAAO,EAAE,SAAS,EAAC,gBAAgB,YAC3C,eAAK,SAAS,EAAC,kBAAkB,aAC9B,QAAQ,EACR,QAAQ,IAAI,IAAI,IAAI,YAAG,SAAS,EAAC,oBAAoB,YAAE,QAAQ,GAAK,IACjE,GACF,IACF,CACP,CAAC;AACJ,CAAC"}
@@ -33,7 +33,7 @@ export declare function useStickToBottom(changes: DependencyList, grew: number):
33
33
  * keyboard (orangecat `ViewportHeightSync`, #1094). Rounded down: sub-pixel
34
34
  * heights leave a 1px seam under the composer on some devices.
35
35
  */
36
- export declare function useViewportHeight(target?: RefObject<HTMLElement | null>): void;
36
+ export declare function useViewportHeight(target?: RefObject<HTMLElement | null>, enabled?: boolean): void;
37
37
  /** Grow a textarea to its content up to `max` px, keyed on the VALUE so a
38
38
  * prefill, a dictated transcript or the clear after send resize it too.
39
39
  *
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/react/hooks.ts"],"names":[],"mappings":"AAEA,OAAO,EAML,KAAK,cAAc,EACnB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,SAAO;;iBAUhC,MAAM;EAYhB;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM;;;;;;EA0BrE;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,QAoBvE;AAED;;;;;;;;gFAQgF;AAChF,wBAAgB,WAAW,CAAC,GAAG,EAAE,SAAS,CAAC,mBAAmB,GAAG,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,SAAM,QAqB/F"}
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/react/hooks.ts"],"names":[],"mappings":"AAEA,OAAO,EAML,KAAK,cAAc,EACnB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,SAAO;;iBAUhC,MAAM;EAYhB;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM;;;;;;EAgDrE;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EAAE,OAAO,UAAO,QAqBvF;AAED;;;;;;;;gFAQgF;AAChF,wBAAgB,WAAW,CAAC,GAAG,EAAE,SAAS,CAAC,mBAAmB,GAAG,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,SAAM,QAqB/F"}
@@ -51,6 +51,31 @@ export function useStickToBottom(changes, grew) {
51
51
  if (following)
52
52
  endRef.current?.scrollIntoView({ block: "end" });
53
53
  }, [following, ...changes]);
54
+ // Pinned through SIZE changes too, not only new tokens: a composer that
55
+ // grows (a long draft, a staged screenshot), ChatScreen measuring its insets
56
+ // after the first paint, a rotated phone — each moves the bottom away, and
57
+ // a reader who was at the bottom must still be there afterwards.
58
+ const followingRef = useRef(following);
59
+ followingRef.current = following;
60
+ const observed = useRef(null);
61
+ const observer = useRef(null);
62
+ useLayoutEffect(() => {
63
+ const el = scrollRef.current;
64
+ if (el === observed.current || typeof ResizeObserver === "undefined")
65
+ return;
66
+ observer.current?.disconnect();
67
+ observed.current = el;
68
+ if (!el)
69
+ return;
70
+ observer.current = new ResizeObserver(() => {
71
+ if (followingRef.current)
72
+ el.scrollTop = el.scrollHeight;
73
+ });
74
+ observer.current.observe(el);
75
+ if (el.firstElementChild)
76
+ observer.current.observe(el.firstElementChild);
77
+ });
78
+ useEffect(() => () => observer.current?.disconnect(), []);
54
79
  const previous = useRef(grew);
55
80
  useEffect(() => {
56
81
  if (grew > previous.current)
@@ -68,8 +93,10 @@ export function useStickToBottom(changes, grew) {
68
93
  * keyboard (orangecat `ViewportHeightSync`, #1094). Rounded down: sub-pixel
69
94
  * heights leave a 1px seam under the composer on some devices.
70
95
  */
71
- export function useViewportHeight(target) {
96
+ export function useViewportHeight(target, enabled = true) {
72
97
  useEffect(() => {
98
+ if (!enabled)
99
+ return;
73
100
  const vv = window.visualViewport;
74
101
  const el = target?.current ?? document.documentElement;
75
102
  const sync = () => el.style.setProperty("--ck-viewport-height", `${Math.floor(vv?.height ?? window.innerHeight)}px`);
@@ -83,7 +110,7 @@ export function useViewportHeight(target) {
83
110
  window.removeEventListener("orientationchange", sync);
84
111
  el.style.removeProperty("--ck-viewport-height");
85
112
  };
86
- }, [target]);
113
+ }, [target, enabled]);
87
114
  }
88
115
  /** Grow a textarea to its content up to `max` px, keyed on the VALUE so a
89
116
  * prefill, a dictated transcript or the clear after send resize it too.
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/react/hooks.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/react/hooks.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,7 @@ export { Composer, DEFAULT_COMPOSER_LABELS } from "./Composer.js";
11
11
  export type { ComposerProps, ComposerLabels, ComposerLabelOverrides, ComposerVoice, } from "./Composer.js";
12
12
  export { ChatThread, ChatMessage, ChatReplies, ChatStarters, DEFAULT_THREAD_LABELS, } from "./Thread.js";
13
13
  export type { ChatMessageData, ChatSpeaker, LiveTurn, Starter, ThreadLabels } from "./Thread.js";
14
+ export { ChatScreen } from "./Screen.js";
14
15
  export { Markdown, defaultRenderLink } from "./Markdown.js";
15
16
  export type { CitationMap, RenderLink } from "./Markdown.js";
16
17
  export { useDictation } from "./use-dictation.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,QAAQ,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAClE,YAAY,EACV,aAAa,EACb,cAAc,EACd,sBAAsB,EACtB,aAAa,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,UAAU,EACV,WAAW,EACX,WAAW,EACX,YAAY,EACZ,qBAAqB,GACtB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACjG,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC5D,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,mBAAmB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACpG,OAAO,EAAE,UAAU,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AACzE,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,QAAQ,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAClE,YAAY,EACV,aAAa,EACb,cAAc,EACd,sBAAsB,EACtB,aAAa,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,UAAU,EACV,WAAW,EACX,WAAW,EACX,YAAY,EACZ,qBAAqB,GACtB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACjG,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC5D,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,mBAAmB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACpG,OAAO,EAAE,UAAU,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AACzE,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
@@ -9,6 +9,7 @@
9
9
  */
10
10
  export { Composer, DEFAULT_COMPOSER_LABELS } from "./Composer.js";
11
11
  export { ChatThread, ChatMessage, ChatReplies, ChatStarters, DEFAULT_THREAD_LABELS, } from "./Thread.js";
12
+ export { ChatScreen } from "./Screen.js";
12
13
  export { Markdown, defaultRenderLink } from "./Markdown.js";
13
14
  export { useDictation } from "./use-dictation.js";
14
15
  export { AttachMenu, DEFAULT_ATTACH_MENU_LABELS } from "./AttachMenu.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,QAAQ,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAOlE,OAAO,EACL,UAAU,EACV,WAAW,EACX,WAAW,EACX,YAAY,EACZ,qBAAqB,GACtB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,UAAU,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAEzE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,QAAQ,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAOlE,OAAO,EACL,UAAU,EACV,WAAW,EACX,WAAW,EACX,YAAY,EACZ,qBAAqB,GACtB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,UAAU,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAEzE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitbaum/chatkit",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "One chat for every product: a composer with a microphone that always works, a thread that does not fight the reader, messages with copy and retry, and LLM markdown \u2014 the fleet's chat standard as a package.",
5
5
  "license": "MIT",
6
6
  "author": "Cato",
package/styles.css CHANGED
@@ -20,6 +20,7 @@
20
20
  /* Defaults at ZERO specificity on the root: an app's own :root (or any
21
21
  container) rule setting a --ck-* variable always wins. Declaring these on
22
22
  the chat's elements instead would silently override the app. */
23
+ --ck-page: hsl(var(--surface-page, 0 0% 99%));
23
24
  --ck-bg: hsl(var(--surface-base, 0 0% 100%));
24
25
  --ck-raised: hsl(var(--surface-raised, 0 0% 96%));
25
26
  --ck-overlay: hsl(var(--surface-overlay, 0 0% 98%));
@@ -43,8 +44,15 @@
43
44
  --ck-text: 1rem;
44
45
  --ck-leading: 1.6;
45
46
  --ck-target: 2.75rem;
47
+ /* ChatScreen: the reading column, and the margin it keeps from the edge. */
48
+ --ck-measure: 48rem;
49
+ --ck-gutter: 1rem;
50
+ /* The person's own words. Neutral, not the accent: the accent is for the
51
+ one thing to press, and a column of tinted pills spent it on every turn. */
52
+ --ck-bubble: var(--ck-raised);
46
53
  }
47
54
 
55
+ .ck-screen,
48
56
  .ck-composer-wrap,
49
57
  .ck-thread-wrap,
50
58
  .ck-starters,
@@ -53,6 +61,7 @@
53
61
  color: var(--ck-fg);
54
62
  }
55
63
 
64
+ .ck-screen *,
56
65
  .ck-composer-wrap *,
57
66
  .ck-thread-wrap *,
58
67
  .ck-starters *,
@@ -468,7 +477,7 @@
468
477
  max-width: min(85%, 42rem);
469
478
  padding: 0.625rem 1rem;
470
479
  border-radius: calc(var(--ck-radius) * 1.25);
471
- background: var(--ck-accent-soft);
480
+ background: var(--ck-bubble);
472
481
  color: var(--ck-fg);
473
482
  font-size: var(--ck-text);
474
483
  line-height: var(--ck-leading);
@@ -590,6 +599,156 @@
590
599
  box-shadow: 0 6px 20px -8px rgb(0 0 0 / 0.35);
591
600
  }
592
601
 
602
+ /* ── screen ─────────────────────────────────────────────────────────────
603
+ The whole display is the conversation; header and composer float over it.
604
+ --ck-top-inset / --ck-bottom-inset are measured by ChatScreen. */
605
+ .ck-screen {
606
+ position: relative;
607
+ display: flex;
608
+ flex-direction: column;
609
+ height: 100%;
610
+ min-height: 0;
611
+ min-width: 0;
612
+ overflow: hidden;
613
+ background: var(--ck-page);
614
+ }
615
+ .ck-screen-viewport {
616
+ height: var(--ck-viewport-height, 100dvh);
617
+ }
618
+ .ck-screen-body {
619
+ position: relative;
620
+ display: flex;
621
+ flex: 1;
622
+ flex-direction: column;
623
+ min-height: 0;
624
+ min-width: 0;
625
+ }
626
+ /* Words pass under the header and fade as they do: the page colour at the
627
+ very top, nothing by the bottom of the row. No bar, no border. */
628
+ .ck-screen-header {
629
+ position: absolute;
630
+ inset: 0 0 auto;
631
+ z-index: 3;
632
+ padding: env(safe-area-inset-top, 0px) var(--ck-gutter) 0;
633
+ background: linear-gradient(
634
+ to bottom,
635
+ var(--ck-page) 0%,
636
+ color-mix(in srgb, var(--ck-page) 75%, transparent) 55%,
637
+ transparent 100%
638
+ );
639
+ pointer-events: none;
640
+ }
641
+ .ck-screen-header-row {
642
+ display: flex;
643
+ align-items: center;
644
+ gap: 0.5rem;
645
+ min-width: 0;
646
+ min-height: 3.5rem;
647
+ padding-block: 0.375rem;
648
+ }
649
+ .ck-screen-header-row > * {
650
+ pointer-events: auto;
651
+ }
652
+ .ck-screen-dock {
653
+ position: absolute;
654
+ inset: auto 0 0;
655
+ z-index: 3;
656
+ padding: 1.5rem var(--ck-gutter) calc(0.5rem + env(safe-area-inset-bottom, 0px));
657
+ background: linear-gradient(to top, var(--ck-page) 0%, var(--ck-page) 40%, transparent 100%);
658
+ pointer-events: none;
659
+ }
660
+ .ck-screen-column {
661
+ display: flex;
662
+ flex-direction: column;
663
+ gap: 0.5rem;
664
+ width: 100%;
665
+ max-width: var(--ck-measure);
666
+ margin-inline: auto;
667
+ pointer-events: auto;
668
+ }
669
+ .ck-screen-footnote {
670
+ margin: 0;
671
+ color: var(--ck-muted);
672
+ font-size: 0.75rem;
673
+ line-height: 1.4;
674
+ text-align: center;
675
+ }
676
+ /* The composer as a floating pill: see-through enough to show the words
677
+ passing under it, solid enough to type on. */
678
+ .ck-screen-dock .ck-composer {
679
+ border-color: color-mix(in srgb, var(--ck-line-2) 70%, transparent);
680
+ border-radius: 1.75rem;
681
+ background: color-mix(in srgb, var(--ck-bg) 86%, transparent);
682
+ -webkit-backdrop-filter: blur(20px) saturate(1.4);
683
+ backdrop-filter: blur(20px) saturate(1.4);
684
+ box-shadow: 0 6px 28px -12px rgb(0 0 0 / 0.28);
685
+ }
686
+ .ck-screen-dock .ck-voice-bar {
687
+ border-radius: 1.75rem;
688
+ }
689
+ /* The thread scrolls the full width (the scrollbar sits at the screen's
690
+ edge, and a swipe anywhere moves it); its column keeps a reading measure. */
691
+ .ck-screen .ck-thread {
692
+ padding: var(--ck-top-inset, 0px) var(--ck-gutter) calc(var(--ck-bottom-inset, 0px) + 0.75rem);
693
+ scroll-padding-top: var(--ck-top-inset, 0px);
694
+ scroll-padding-bottom: var(--ck-bottom-inset, 0px);
695
+ }
696
+ .ck-screen .ck-thread-inner {
697
+ width: 100%;
698
+ max-width: var(--ck-measure);
699
+ margin-inline: auto;
700
+ }
701
+ .ck-screen .ck-jump {
702
+ bottom: calc(var(--ck-bottom-inset, 0px) + 0.5rem);
703
+ z-index: 4;
704
+ }
705
+ .ck-screen-body > .ck-starters,
706
+ .ck-screen-body > .ck-thread-loading {
707
+ flex: 1;
708
+ justify-content: center;
709
+ width: 100%;
710
+ max-width: calc(var(--ck-measure) + 2 * var(--ck-gutter));
711
+ margin-inline: auto;
712
+ padding: var(--ck-top-inset, 0px) var(--ck-gutter) var(--ck-bottom-inset, 0px);
713
+ overflow-y: auto;
714
+ }
715
+
716
+ /* A control floating over the conversation: a frosted pill, the way the
717
+ header buttons sit over the words in the ChatGPT app. For an app's own
718
+ header buttons inside ChatScreen. */
719
+ .ck-glass {
720
+ display: inline-flex;
721
+ align-items: center;
722
+ justify-content: center;
723
+ gap: 0.375rem;
724
+ min-width: var(--ck-target);
725
+ min-height: var(--ck-target);
726
+ padding: 0 0.75rem;
727
+ border: 1px solid color-mix(in srgb, var(--ck-line-2) 60%, transparent);
728
+ border-radius: 999px;
729
+ background: color-mix(in srgb, var(--ck-bg) 70%, transparent);
730
+ -webkit-backdrop-filter: blur(16px) saturate(1.4);
731
+ backdrop-filter: blur(16px) saturate(1.4);
732
+ box-shadow: 0 2px 10px -4px rgb(0 0 0 / 0.18);
733
+ color: var(--ck-fg);
734
+ font: inherit;
735
+ font-size: 0.9375rem;
736
+ cursor: pointer;
737
+ }
738
+ .ck-glass:hover {
739
+ background: color-mix(in srgb, var(--ck-bg) 88%, transparent);
740
+ }
741
+ .ck-glass:focus-visible {
742
+ outline: 2px solid var(--ck-accent);
743
+ outline-offset: 2px;
744
+ }
745
+ @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
746
+ .ck-glass,
747
+ .ck-screen-dock .ck-composer {
748
+ background: var(--ck-bg);
749
+ }
750
+ }
751
+
593
752
  /* markdown: 16px, readable measure, no leaked ** or > */
594
753
  .ck-md {
595
754
  min-width: 0;