@hanzo/ui 8.0.28 → 8.0.32
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/backends/gui/button.d.ts +12 -0
- package/dist/backends/gui/button.d.ts.map +1 -1
- package/dist/backends/gui/button.js.map +1 -1
- package/dist/backends/gui/slot.cjs +11 -1
- package/dist/backends/gui/slot.d.ts +9 -0
- package/dist/backends/gui/slot.d.ts.map +1 -1
- package/dist/backends/gui/slot.js +9 -0
- package/dist/backends/gui/slot.js.map +1 -1
- package/dist/backends/gui/textarea.cjs +0 -8
- package/dist/backends/gui/textarea.d.ts +8 -1
- package/dist/backends/gui/textarea.d.ts.map +1 -1
- package/dist/backends/gui/textarea.js +0 -8
- package/dist/backends/gui/textarea.js.map +1 -1
- package/dist/chat/Code.cjs +70 -0
- package/dist/chat/Code.d.ts +25 -0
- package/dist/chat/Code.d.ts.map +1 -0
- package/dist/chat/Code.js +67 -0
- package/dist/chat/Code.js.map +1 -0
- package/dist/chat/Composer.cjs +56 -0
- package/dist/chat/Composer.d.ts +30 -0
- package/dist/chat/Composer.d.ts.map +1 -0
- package/dist/chat/Composer.js +53 -0
- package/dist/chat/Composer.js.map +1 -0
- package/dist/chat/Header.cjs +49 -0
- package/dist/chat/Header.d.ts +46 -0
- package/dist/chat/Header.d.ts.map +1 -0
- package/dist/chat/Header.js +43 -0
- package/dist/chat/Header.js.map +1 -0
- package/dist/chat/Message.cjs +27 -0
- package/dist/chat/Message.d.ts +14 -0
- package/dist/chat/Message.d.ts.map +1 -0
- package/dist/chat/Message.js +25 -0
- package/dist/chat/Message.js.map +1 -0
- package/dist/chat/Sidebar.cjs +81 -0
- package/dist/chat/Sidebar.d.ts +79 -0
- package/dist/chat/Sidebar.d.ts.map +1 -0
- package/dist/chat/Sidebar.js +71 -0
- package/dist/chat/Sidebar.js.map +1 -0
- package/dist/chat/Sources.cjs +25 -0
- package/dist/chat/Sources.d.ts +23 -0
- package/dist/chat/Sources.d.ts.map +1 -0
- package/dist/chat/Sources.js +22 -0
- package/dist/chat/Sources.js.map +1 -0
- package/dist/chat/Thread.cjs +42 -0
- package/dist/chat/Thread.d.ts +11 -0
- package/dist/chat/Thread.d.ts.map +1 -0
- package/dist/chat/Thread.js +40 -0
- package/dist/chat/Thread.js.map +1 -0
- package/dist/chat/index.cjs +58 -0
- package/dist/chat/index.d.ts +30 -0
- package/dist/chat/index.d.ts.map +1 -0
- package/dist/chat/index.js +30 -0
- package/dist/chat/index.js.map +1 -0
- package/dist/chat/send.cjs +44 -0
- package/dist/chat/send.d.ts +32 -0
- package/dist/chat/send.d.ts.map +1 -0
- package/dist/chat/send.js +40 -0
- package/dist/chat/send.js.map +1 -0
- package/dist/chat/stick.cjs +19 -0
- package/dist/chat/stick.d.ts +24 -0
- package/dist/chat/stick.d.ts.map +1 -0
- package/dist/chat/stick.js +16 -0
- package/dist/chat/stick.js.map +1 -0
- package/package.json +8 -2
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use client';"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SourceChip = exports.Sources = exports.CopyButton = exports.Code = exports.Aside = exports.AsideToggle = exports.ShareButton = exports.HeaderButton = exports.Header = exports.SidebarUser = exports.SidebarFolder = exports.SidebarItem = exports.SidebarSection = exports.SidebarScroll = exports.SidebarNewChat = exports.SidebarIconButton = exports.SidebarHeader = exports.Sidebar = exports.SLACK = exports.pinned = exports.sends = exports.ready = exports.Thread = exports.Message = exports.ASK = exports.Composer = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* @hanzo/ui/chat — the one chat shell, on @hanzo/gui primitives.
|
|
6
|
+
*
|
|
7
|
+
* Shell only: Thread scrolls, Message presents a turn, Composer takes a draft,
|
|
8
|
+
* Sidebar navigates between conversations, Header names the current one, Code
|
|
9
|
+
* and Sources present the two pieces of a turn every surface renders the same
|
|
10
|
+
* way. Content rendering (markdown, tool calls) stays with the surface — they
|
|
11
|
+
* disagree on the pipeline, and none of it is presentational.
|
|
12
|
+
*
|
|
13
|
+
* Everything is props-in, callbacks-out: no transport, no store, no routing, no
|
|
14
|
+
* active-id resolution. The same components serve a live conversation, a shared
|
|
15
|
+
* read-only transcript and a test fixture.
|
|
16
|
+
*
|
|
17
|
+
* Styling is `$` tokens throughout, never literal colours, so each brand retunes
|
|
18
|
+
* through its own theme and nothing here carries a Hanzo mark — which is what
|
|
19
|
+
* lets the same shell ship on Lux and Zoo surfaces without leaking a brand.
|
|
20
|
+
*
|
|
21
|
+
* The model picker is not here: it already ships at `@hanzo/ui/models`, and it
|
|
22
|
+
* is not chat-specific.
|
|
23
|
+
*/
|
|
24
|
+
var Composer_1 = require("./Composer.cjs");
|
|
25
|
+
Object.defineProperty(exports, "Composer", { enumerable: true, get: function () { return Composer_1.Composer; } });
|
|
26
|
+
Object.defineProperty(exports, "ASK", { enumerable: true, get: function () { return Composer_1.ASK; } });
|
|
27
|
+
var Message_1 = require("./Message.cjs");
|
|
28
|
+
Object.defineProperty(exports, "Message", { enumerable: true, get: function () { return Message_1.Message; } });
|
|
29
|
+
var Thread_1 = require("./Thread.cjs");
|
|
30
|
+
Object.defineProperty(exports, "Thread", { enumerable: true, get: function () { return Thread_1.Thread; } });
|
|
31
|
+
var send_1 = require("./send.cjs");
|
|
32
|
+
Object.defineProperty(exports, "ready", { enumerable: true, get: function () { return send_1.ready; } });
|
|
33
|
+
Object.defineProperty(exports, "sends", { enumerable: true, get: function () { return send_1.sends; } });
|
|
34
|
+
var stick_1 = require("./stick.cjs");
|
|
35
|
+
Object.defineProperty(exports, "pinned", { enumerable: true, get: function () { return stick_1.pinned; } });
|
|
36
|
+
Object.defineProperty(exports, "SLACK", { enumerable: true, get: function () { return stick_1.SLACK; } });
|
|
37
|
+
var Sidebar_1 = require("./Sidebar.cjs");
|
|
38
|
+
Object.defineProperty(exports, "Sidebar", { enumerable: true, get: function () { return Sidebar_1.Sidebar; } });
|
|
39
|
+
Object.defineProperty(exports, "SidebarHeader", { enumerable: true, get: function () { return Sidebar_1.SidebarHeader; } });
|
|
40
|
+
Object.defineProperty(exports, "SidebarIconButton", { enumerable: true, get: function () { return Sidebar_1.SidebarIconButton; } });
|
|
41
|
+
Object.defineProperty(exports, "SidebarNewChat", { enumerable: true, get: function () { return Sidebar_1.SidebarNewChat; } });
|
|
42
|
+
Object.defineProperty(exports, "SidebarScroll", { enumerable: true, get: function () { return Sidebar_1.SidebarScroll; } });
|
|
43
|
+
Object.defineProperty(exports, "SidebarSection", { enumerable: true, get: function () { return Sidebar_1.SidebarSection; } });
|
|
44
|
+
Object.defineProperty(exports, "SidebarItem", { enumerable: true, get: function () { return Sidebar_1.SidebarItem; } });
|
|
45
|
+
Object.defineProperty(exports, "SidebarFolder", { enumerable: true, get: function () { return Sidebar_1.SidebarFolder; } });
|
|
46
|
+
Object.defineProperty(exports, "SidebarUser", { enumerable: true, get: function () { return Sidebar_1.SidebarUser; } });
|
|
47
|
+
var Header_1 = require("./Header.cjs");
|
|
48
|
+
Object.defineProperty(exports, "Header", { enumerable: true, get: function () { return Header_1.Header; } });
|
|
49
|
+
Object.defineProperty(exports, "HeaderButton", { enumerable: true, get: function () { return Header_1.HeaderButton; } });
|
|
50
|
+
Object.defineProperty(exports, "ShareButton", { enumerable: true, get: function () { return Header_1.ShareButton; } });
|
|
51
|
+
Object.defineProperty(exports, "AsideToggle", { enumerable: true, get: function () { return Header_1.AsideToggle; } });
|
|
52
|
+
Object.defineProperty(exports, "Aside", { enumerable: true, get: function () { return Header_1.Aside; } });
|
|
53
|
+
var Code_1 = require("./Code.cjs");
|
|
54
|
+
Object.defineProperty(exports, "Code", { enumerable: true, get: function () { return Code_1.Code; } });
|
|
55
|
+
Object.defineProperty(exports, "CopyButton", { enumerable: true, get: function () { return Code_1.CopyButton; } });
|
|
56
|
+
var Sources_1 = require("./Sources.cjs");
|
|
57
|
+
Object.defineProperty(exports, "Sources", { enumerable: true, get: function () { return Sources_1.Sources; } });
|
|
58
|
+
Object.defineProperty(exports, "SourceChip", { enumerable: true, get: function () { return Sources_1.SourceChip; } });
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hanzo/ui/chat — the one chat shell, on @hanzo/gui primitives.
|
|
3
|
+
*
|
|
4
|
+
* Shell only: Thread scrolls, Message presents a turn, Composer takes a draft,
|
|
5
|
+
* Sidebar navigates between conversations, Header names the current one, Code
|
|
6
|
+
* and Sources present the two pieces of a turn every surface renders the same
|
|
7
|
+
* way. Content rendering (markdown, tool calls) stays with the surface — they
|
|
8
|
+
* disagree on the pipeline, and none of it is presentational.
|
|
9
|
+
*
|
|
10
|
+
* Everything is props-in, callbacks-out: no transport, no store, no routing, no
|
|
11
|
+
* active-id resolution. The same components serve a live conversation, a shared
|
|
12
|
+
* read-only transcript and a test fixture.
|
|
13
|
+
*
|
|
14
|
+
* Styling is `$` tokens throughout, never literal colours, so each brand retunes
|
|
15
|
+
* through its own theme and nothing here carries a Hanzo mark — which is what
|
|
16
|
+
* lets the same shell ship on Lux and Zoo surfaces without leaking a brand.
|
|
17
|
+
*
|
|
18
|
+
* The model picker is not here: it already ships at `@hanzo/ui/models`, and it
|
|
19
|
+
* is not chat-specific.
|
|
20
|
+
*/
|
|
21
|
+
export { Composer, ASK, type ComposerProps } from './Composer.js';
|
|
22
|
+
export { Message, type MessageProps, type Role } from './Message.js';
|
|
23
|
+
export { Thread, type ThreadProps } from './Thread.js';
|
|
24
|
+
export { ready, sends, type Mods } from './send.js';
|
|
25
|
+
export { pinned, SLACK, type Track } from './stick.js';
|
|
26
|
+
export { Sidebar, SidebarHeader, SidebarIconButton, SidebarNewChat, SidebarScroll, SidebarSection, SidebarItem, SidebarFolder, SidebarUser, type SidebarProps, type SidebarHeaderProps, type SidebarIconButtonProps, type SidebarNewChatProps, type SidebarSectionProps, type SidebarItemProps, type SidebarFolderProps, type SidebarUserProps, } from './Sidebar.js';
|
|
27
|
+
export { Header, HeaderButton, ShareButton, AsideToggle, Aside, type HeaderProps, type HeaderButtonProps, type ShareButtonProps, type AsideToggleProps, type AsideProps, } from './Header.js';
|
|
28
|
+
export { Code, CopyButton, type CodeProps, type CopyButtonProps } from './Code.js';
|
|
29
|
+
export { Sources, SourceChip, type Source, type SourcesProps, type SourceChipProps, } from './Sources.js';
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/chat/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAA;AAC9D,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,IAAI,EAAE,MAAM,WAAW,CAAA;AACjE,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;AACnD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,IAAI,EAAE,MAAM,QAAQ,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,EAAE,MAAM,SAAS,CAAA;AAEnD,OAAO,EACL,OAAO,EACP,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,cAAc,EACd,WAAW,EACX,aAAa,EACb,WAAW,EACX,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,GACtB,MAAM,WAAW,CAAA;AAElB,OAAO,EACL,MAAM,EACN,YAAY,EACZ,WAAW,EACX,WAAW,EACX,KAAK,EACL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,UAAU,GAChB,MAAM,UAAU,CAAA;AAEjB,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,QAAQ,CAAA;AAE/E,OAAO,EACL,OAAO,EACP,UAAU,EACV,KAAK,MAAM,EACX,KAAK,YAAY,EACjB,KAAK,eAAe,GACrB,MAAM,WAAW,CAAA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use client';/**
|
|
2
|
+
* @hanzo/ui/chat — the one chat shell, on @hanzo/gui primitives.
|
|
3
|
+
*
|
|
4
|
+
* Shell only: Thread scrolls, Message presents a turn, Composer takes a draft,
|
|
5
|
+
* Sidebar navigates between conversations, Header names the current one, Code
|
|
6
|
+
* and Sources present the two pieces of a turn every surface renders the same
|
|
7
|
+
* way. Content rendering (markdown, tool calls) stays with the surface — they
|
|
8
|
+
* disagree on the pipeline, and none of it is presentational.
|
|
9
|
+
*
|
|
10
|
+
* Everything is props-in, callbacks-out: no transport, no store, no routing, no
|
|
11
|
+
* active-id resolution. The same components serve a live conversation, a shared
|
|
12
|
+
* read-only transcript and a test fixture.
|
|
13
|
+
*
|
|
14
|
+
* Styling is `$` tokens throughout, never literal colours, so each brand retunes
|
|
15
|
+
* through its own theme and nothing here carries a Hanzo mark — which is what
|
|
16
|
+
* lets the same shell ship on Lux and Zoo surfaces without leaking a brand.
|
|
17
|
+
*
|
|
18
|
+
* The model picker is not here: it already ships at `@hanzo/ui/models`, and it
|
|
19
|
+
* is not chat-specific.
|
|
20
|
+
*/
|
|
21
|
+
export { Composer, ASK } from './Composer.js';
|
|
22
|
+
export { Message } from './Message.js';
|
|
23
|
+
export { Thread } from './Thread.js';
|
|
24
|
+
export { ready, sends } from './send.js';
|
|
25
|
+
export { pinned, SLACK } from './stick.js';
|
|
26
|
+
export { Sidebar, SidebarHeader, SidebarIconButton, SidebarNewChat, SidebarScroll, SidebarSection, SidebarItem, SidebarFolder, SidebarUser, } from './Sidebar.js';
|
|
27
|
+
export { Header, HeaderButton, ShareButton, AsideToggle, Aside, } from './Header.js';
|
|
28
|
+
export { Code, CopyButton } from './Code.js';
|
|
29
|
+
export { Sources, SourceChip, } from './Sources.js';
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/chat/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAsB,MAAM,YAAY,CAAA;AAC9D,OAAO,EAAE,OAAO,EAAgC,MAAM,WAAW,CAAA;AACjE,OAAO,EAAE,MAAM,EAAoB,MAAM,UAAU,CAAA;AACnD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAa,MAAM,QAAQ,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAc,MAAM,SAAS,CAAA;AAEnD,OAAO,EACL,OAAO,EACP,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,cAAc,EACd,WAAW,EACX,aAAa,EACb,WAAW,GASZ,MAAM,WAAW,CAAA;AAElB,OAAO,EACL,MAAM,EACN,YAAY,EACZ,WAAW,EACX,WAAW,EACX,KAAK,GAMN,MAAM,UAAU,CAAA;AAEjB,OAAO,EAAE,IAAI,EAAE,UAAU,EAAwC,MAAM,QAAQ,CAAA;AAE/E,OAAO,EACL,OAAO,EACP,UAAU,GAIX,MAAM,WAAW,CAAA"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use client';"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The two decisions a composer makes, as pure functions.
|
|
4
|
+
*
|
|
5
|
+
* Every Hanzo chat surface had re-implemented both inline, and they had drifted:
|
|
6
|
+
* one sent on a bare Enter while composing an IME candidate (committing a
|
|
7
|
+
* half-typed word), another let a whitespace-only draft through. Keeping them
|
|
8
|
+
* here means the rules are asserted once, without a DOM.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.ready = exports.sends = void 0;
|
|
12
|
+
/** The keyCode every browser reports for a keystroke an IME has claimed. */
|
|
13
|
+
const IME = 229;
|
|
14
|
+
/**
|
|
15
|
+
* Whether the IME owns this keystroke, from all three signals browsers use.
|
|
16
|
+
*
|
|
17
|
+
* `isComposing` alone is not enough. Safari does not set it reliably on the
|
|
18
|
+
* keydown that accepts a candidate, and reports the key as `Process` or with
|
|
19
|
+
* only the legacy `keyCode` of 229 — so a composer that trusts `isComposing`
|
|
20
|
+
* by itself still submits a half-typed word out from under a Japanese, Chinese
|
|
21
|
+
* or Korean writer. That is the bug this module exists to end, and it is not
|
|
22
|
+
* ended by checking one flag.
|
|
23
|
+
*/
|
|
24
|
+
const claimed = (key, mods) => mods.isComposing === true || key === 'Process' || mods.keyCode === IME;
|
|
25
|
+
/**
|
|
26
|
+
* Enter sends. Shift+Enter writes a newline, so a multi-line draft is always
|
|
27
|
+
* reachable from the keyboard. Cmd/Ctrl+Enter always sends — it is the force-send
|
|
28
|
+
* every surface already taught its users, and it holds even with Shift down.
|
|
29
|
+
* A keystroke the IME has claimed belongs to the IME, never to the composer.
|
|
30
|
+
*/
|
|
31
|
+
const sends = (key, mods = {}) => {
|
|
32
|
+
if (key !== 'Enter' || claimed(key, mods))
|
|
33
|
+
return false;
|
|
34
|
+
if (mods.metaKey === true || mods.ctrlKey === true)
|
|
35
|
+
return true;
|
|
36
|
+
return mods.shiftKey !== true && mods.altKey !== true;
|
|
37
|
+
};
|
|
38
|
+
exports.sends = sends;
|
|
39
|
+
/**
|
|
40
|
+
* Whether a draft may be sent: there has to be something other than whitespace,
|
|
41
|
+
* the surface must not already be waiting on a turn, and the field must be live.
|
|
42
|
+
*/
|
|
43
|
+
const ready = (value, busy = false, disabled = false) => !busy && !disabled && value.trim().length > 0;
|
|
44
|
+
exports.ready = ready;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two decisions a composer makes, as pure functions.
|
|
3
|
+
*
|
|
4
|
+
* Every Hanzo chat surface had re-implemented both inline, and they had drifted:
|
|
5
|
+
* one sent on a bare Enter while composing an IME candidate (committing a
|
|
6
|
+
* half-typed word), another let a whitespace-only draft through. Keeping them
|
|
7
|
+
* here means the rules are asserted once, without a DOM.
|
|
8
|
+
*/
|
|
9
|
+
/** Modifier state, in the shape web and native events agree on. */
|
|
10
|
+
export interface Mods {
|
|
11
|
+
shiftKey?: boolean;
|
|
12
|
+
altKey?: boolean;
|
|
13
|
+
metaKey?: boolean;
|
|
14
|
+
ctrlKey?: boolean;
|
|
15
|
+
/** `KeyboardEvent.isComposing` — true while an IME candidate window is open. */
|
|
16
|
+
isComposing?: boolean;
|
|
17
|
+
/** Legacy `KeyboardEvent.keyCode`. Read only to recognise Safari's IME. */
|
|
18
|
+
keyCode?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Enter sends. Shift+Enter writes a newline, so a multi-line draft is always
|
|
22
|
+
* reachable from the keyboard. Cmd/Ctrl+Enter always sends — it is the force-send
|
|
23
|
+
* every surface already taught its users, and it holds even with Shift down.
|
|
24
|
+
* A keystroke the IME has claimed belongs to the IME, never to the composer.
|
|
25
|
+
*/
|
|
26
|
+
export declare const sends: (key: string, mods?: Mods) => boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Whether a draft may be sent: there has to be something other than whitespace,
|
|
29
|
+
* the surface must not already be waiting on a turn, and the field must be live.
|
|
30
|
+
*/
|
|
31
|
+
export declare const ready: (value: string, busy?: boolean, disabled?: boolean) => boolean;
|
|
32
|
+
//# sourceMappingURL=send.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../../src/chat/send.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,mEAAmE;AACnE,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,gFAAgF;IAChF,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAkBD;;;;;GAKG;AACH,eAAO,MAAM,KAAK,GAAI,KAAK,MAAM,EAAE,OAAM,IAAS,KAAG,OAIpD,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,KAAK,GAAI,OAAO,MAAM,EAAE,cAAY,EAAE,kBAAgB,KAAG,OACvB,CAAA"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use client';/**
|
|
2
|
+
* The two decisions a composer makes, as pure functions.
|
|
3
|
+
*
|
|
4
|
+
* Every Hanzo chat surface had re-implemented both inline, and they had drifted:
|
|
5
|
+
* one sent on a bare Enter while composing an IME candidate (committing a
|
|
6
|
+
* half-typed word), another let a whitespace-only draft through. Keeping them
|
|
7
|
+
* here means the rules are asserted once, without a DOM.
|
|
8
|
+
*/
|
|
9
|
+
/** The keyCode every browser reports for a keystroke an IME has claimed. */
|
|
10
|
+
const IME = 229;
|
|
11
|
+
/**
|
|
12
|
+
* Whether the IME owns this keystroke, from all three signals browsers use.
|
|
13
|
+
*
|
|
14
|
+
* `isComposing` alone is not enough. Safari does not set it reliably on the
|
|
15
|
+
* keydown that accepts a candidate, and reports the key as `Process` or with
|
|
16
|
+
* only the legacy `keyCode` of 229 — so a composer that trusts `isComposing`
|
|
17
|
+
* by itself still submits a half-typed word out from under a Japanese, Chinese
|
|
18
|
+
* or Korean writer. That is the bug this module exists to end, and it is not
|
|
19
|
+
* ended by checking one flag.
|
|
20
|
+
*/
|
|
21
|
+
const claimed = (key, mods) => mods.isComposing === true || key === 'Process' || mods.keyCode === IME;
|
|
22
|
+
/**
|
|
23
|
+
* Enter sends. Shift+Enter writes a newline, so a multi-line draft is always
|
|
24
|
+
* reachable from the keyboard. Cmd/Ctrl+Enter always sends — it is the force-send
|
|
25
|
+
* every surface already taught its users, and it holds even with Shift down.
|
|
26
|
+
* A keystroke the IME has claimed belongs to the IME, never to the composer.
|
|
27
|
+
*/
|
|
28
|
+
export const sends = (key, mods = {}) => {
|
|
29
|
+
if (key !== 'Enter' || claimed(key, mods))
|
|
30
|
+
return false;
|
|
31
|
+
if (mods.metaKey === true || mods.ctrlKey === true)
|
|
32
|
+
return true;
|
|
33
|
+
return mods.shiftKey !== true && mods.altKey !== true;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Whether a draft may be sent: there has to be something other than whitespace,
|
|
37
|
+
* the surface must not already be waiting on a turn, and the field must be live.
|
|
38
|
+
*/
|
|
39
|
+
export const ready = (value, busy = false, disabled = false) => !busy && !disabled && value.trim().length > 0;
|
|
40
|
+
//# sourceMappingURL=send.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send.js","sourceRoot":"","sources":["../../src/chat/send.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAcH,4EAA4E;AAC5E,MAAM,GAAG,GAAG,GAAG,CAAA;AAEf;;;;;;;;;GASG;AACH,MAAM,OAAO,GAAG,CAAC,GAAW,EAAE,IAAU,EAAW,EAAE,CACnD,IAAI,CAAC,WAAW,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS,IAAI,IAAI,CAAC,OAAO,KAAK,GAAG,CAAA;AAExE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,GAAW,EAAE,OAAa,EAAE,EAAW,EAAE;IAC7D,IAAI,GAAG,KAAK,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC;QAAE,OAAO,KAAK,CAAA;IACvD,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAC/D,OAAO,IAAI,CAAC,QAAQ,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAA;AACvD,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,KAAa,EAAE,IAAI,GAAG,KAAK,EAAE,QAAQ,GAAG,KAAK,EAAW,EAAE,CAC9E,CAAC,IAAI,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use client';"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Stay-at-the-bottom, as a pure decision.
|
|
4
|
+
*
|
|
5
|
+
* A thread should follow a streaming answer, but it must stop following the
|
|
6
|
+
* moment the reader scrolls up to re-read something — otherwise every new token
|
|
7
|
+
* yanks them back down. The rule is a distance test against the bottom, and it
|
|
8
|
+
* is the piece each surface had written slightly differently.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.pinned = exports.SLACK = void 0;
|
|
12
|
+
/** How far from the bottom still counts as "at the bottom", in px. */
|
|
13
|
+
exports.SLACK = 48;
|
|
14
|
+
/**
|
|
15
|
+
* True while the viewport is within `slack` of the end — including the case
|
|
16
|
+
* where the content is shorter than the viewport and there is nothing to scroll.
|
|
17
|
+
*/
|
|
18
|
+
const pinned = ({ offset, viewport, content }, slack = exports.SLACK) => content - viewport - offset <= slack;
|
|
19
|
+
exports.pinned = pinned;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stay-at-the-bottom, as a pure decision.
|
|
3
|
+
*
|
|
4
|
+
* A thread should follow a streaming answer, but it must stop following the
|
|
5
|
+
* moment the reader scrolls up to re-read something — otherwise every new token
|
|
6
|
+
* yanks them back down. The rule is a distance test against the bottom, and it
|
|
7
|
+
* is the piece each surface had written slightly differently.
|
|
8
|
+
*/
|
|
9
|
+
/** How far from the bottom still counts as "at the bottom", in px. */
|
|
10
|
+
export declare const SLACK = 48;
|
|
11
|
+
export interface Track {
|
|
12
|
+
/** Scroll offset from the top. */
|
|
13
|
+
offset: number;
|
|
14
|
+
/** Height of the visible viewport. */
|
|
15
|
+
viewport: number;
|
|
16
|
+
/** Height of the full content. */
|
|
17
|
+
content: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* True while the viewport is within `slack` of the end — including the case
|
|
21
|
+
* where the content is shorter than the viewport and there is nothing to scroll.
|
|
22
|
+
*/
|
|
23
|
+
export declare const pinned: ({ offset, viewport, content }: Track, slack?: number) => boolean;
|
|
24
|
+
//# sourceMappingURL=stick.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stick.d.ts","sourceRoot":"","sources":["../../src/chat/stick.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,sEAAsE;AACtE,eAAO,MAAM,KAAK,KAAK,CAAA;AAEvB,MAAM,WAAW,KAAK;IACpB,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAA;IAChB,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;GAGG;AACH,eAAO,MAAM,MAAM,GAAI,+BAA+B,KAAK,EAAE,cAAa,KAAG,OACvC,CAAA"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use client';/**
|
|
2
|
+
* Stay-at-the-bottom, as a pure decision.
|
|
3
|
+
*
|
|
4
|
+
* A thread should follow a streaming answer, but it must stop following the
|
|
5
|
+
* moment the reader scrolls up to re-read something — otherwise every new token
|
|
6
|
+
* yanks them back down. The rule is a distance test against the bottom, and it
|
|
7
|
+
* is the piece each surface had written slightly differently.
|
|
8
|
+
*/
|
|
9
|
+
/** How far from the bottom still counts as "at the bottom", in px. */
|
|
10
|
+
export const SLACK = 48;
|
|
11
|
+
/**
|
|
12
|
+
* True while the viewport is within `slack` of the end — including the case
|
|
13
|
+
* where the content is shorter than the viewport and there is nothing to scroll.
|
|
14
|
+
*/
|
|
15
|
+
export const pinned = ({ offset, viewport, content }, slack = SLACK) => content - viewport - offset <= slack;
|
|
16
|
+
//# sourceMappingURL=stick.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stick.js","sourceRoot":"","sources":["../../src/chat/stick.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,sEAAsE;AACtE,MAAM,CAAC,MAAM,KAAK,GAAG,EAAE,CAAA;AAWvB;;;GAGG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAS,EAAE,KAAK,GAAG,KAAK,EAAW,EAAE,CACrF,OAAO,GAAG,QAAQ,GAAG,MAAM,IAAI,KAAK,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/ui",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.32",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Hanzo UI — the one canonical Hanzo component library, on @hanzo/gui + @hanzo/tokens. ONE substrate: every component renders through @hanzo/gui primitives, so the same import works on web, native (expo) and desktop (Tauri). Self-contained (theme.css), presentational, host-agnostic, clean-room.",
|
|
6
6
|
"exports": {
|
|
@@ -16,6 +16,12 @@
|
|
|
16
16
|
"require": "./dist/models/index.cjs",
|
|
17
17
|
"default": "./dist/models/index.js"
|
|
18
18
|
},
|
|
19
|
+
"./chat": {
|
|
20
|
+
"types": "./dist/chat/index.d.ts",
|
|
21
|
+
"import": "./dist/chat/index.js",
|
|
22
|
+
"require": "./dist/chat/index.cjs",
|
|
23
|
+
"default": "./dist/chat/index.js"
|
|
24
|
+
},
|
|
19
25
|
"./core": {
|
|
20
26
|
"types": "./dist/core/index.d.ts",
|
|
21
27
|
"import": "./dist/core/index.js",
|
|
@@ -197,7 +203,7 @@
|
|
|
197
203
|
"react": "^19.0.0",
|
|
198
204
|
"react-dom": "^19.0.0",
|
|
199
205
|
"react-native-web": "^0.21.2",
|
|
200
|
-
"typescript": "^
|
|
206
|
+
"typescript": "^5.9.3",
|
|
201
207
|
"vitest": "^4.1.8"
|
|
202
208
|
},
|
|
203
209
|
"publishConfig": {
|