@schlenkr/quassel 0.5.6 → 0.7.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.
@@ -1,7 +1,7 @@
1
1
  import { ReactNode, Ref } from "react";
2
2
  import { ChatTexts } from "./texts";
3
3
  import type { ChatAttachmentInput } from "./events";
4
- import type { SendShortcut } from "./options";
4
+ import type { ChatMicrophone, SendShortcut } from "./options";
5
5
  /** A toolbar button, declarative: icon, text or both - plus click handler. */
6
6
  export interface ToolbarAction {
7
7
  icon?: ReactNode;
@@ -21,20 +21,14 @@ export interface ChatInputHandle {
21
21
  allowEmpty?: boolean;
22
22
  }) => Promise<void>;
23
23
  }
24
- /**
25
- * The input card: borderless textarea on top, toolbar below a hairline. Custom buttons come
26
- * declaratively through `actions` or freely through the left/right slots; while work is running
27
- * the stop button stays visible, and typing shows the send button next to it, which then
28
- * interjects. `rows` sets the initial height; with `maxRows` the textarea grows with its content
29
- * up to that number of rows and scrolls after that. `inline` starts with one row and grows the same way.
30
- */
31
- export declare function ChatInputToolbar({ onSend, onStop, running, disabled, sendDisabled, layout, detailsContainer, onErrorChange, inputAriaControls, rows, maxRows, actions, toolbarLeft, toolbarRight, texts, handleRef, attachments: attachmentsEnabled, attachmentCapabilities, attachmentCapabilitiesError, onAttachmentsChange, initialValue, onDraftChange, sendShortcut, autoFocus, onAutoFocusSettled, }: {
24
+ /** A composer with attachments, host slots and steering into a running turn. */
25
+ export declare function ChatInputToolbar({ onSend, onStop, running, disabled, sendDisabled, layout, detailsContainer, onErrorChange, inputAriaControls, rows, maxRows, actions, toolbarLeft, toolbarRight, microphone, replaceInput, texts, handleRef, attachments: attachmentsEnabled, attachmentCapabilities, attachmentCapabilitiesError, onAttachmentsChange, initialValue, onDraftChange, sendShortcut, autoFocus, onAutoFocusSettled, }: {
32
26
  onSend: (text: string, attachments?: ChatAttachmentInput[]) => void | Promise<void>;
33
27
  onStop?: () => void;
34
28
  running?: boolean;
35
29
  disabled?: boolean;
36
30
  sendDisabled?: boolean;
37
- layout?: "card" | "toolbar" | "inline";
31
+ layout?: "card" | "toolbar" | "inline" | "pill";
38
32
  detailsContainer?: HTMLElement | null;
39
33
  onErrorChange?: (error: string | undefined) => void;
40
34
  inputAriaControls?: string;
@@ -43,6 +37,8 @@ export declare function ChatInputToolbar({ onSend, onStop, running, disabled, se
43
37
  actions?: ToolbarAction[];
44
38
  toolbarLeft?: ReactNode;
45
39
  toolbarRight?: ReactNode;
40
+ microphone?: ChatMicrophone;
41
+ replaceInput?: ReactNode;
46
42
  texts?: Partial<ChatTexts>;
47
43
  handleRef?: Ref<ChatInputHandle>;
48
44
  /** false = no attaching: neither button nor dropping or pasting files. */
@@ -0,0 +1,13 @@
1
+ import type { ChatMicrophone } from "./options";
2
+ import type { ChatTexts } from "./texts";
3
+ export declare function ComposerPrimaryAction({ hasContent, running, sending, disabled, sendDisabled, microphone, texts, onSend, onStop }: {
4
+ hasContent: boolean;
5
+ running: boolean;
6
+ sending: boolean;
7
+ disabled: boolean;
8
+ sendDisabled: boolean;
9
+ microphone?: ChatMicrophone;
10
+ texts: Required<ChatTexts>;
11
+ onSend: () => void;
12
+ onStop?: () => void;
13
+ }): import("react").JSX.Element;
@@ -18,6 +18,7 @@ export interface QuasselToggleProps {
18
18
  className?: string;
19
19
  disabled?: boolean;
20
20
  title?: string;
21
+ "aria-label"?: string;
21
22
  pressed?: boolean;
22
23
  onPressedChange?: (pressed: boolean) => void;
23
24
  children?: ReactNode;
package/README.md CHANGED
@@ -43,13 +43,14 @@ variables too.
43
43
  - Streaming-safe Markdown (tables, code with copy button, lists) via Streamdown.
44
44
  - Steering: typing while a turn runs sends into it; stop, retry and failed-send recovery built in.
45
45
  - Attachments by button, drag and drop or paste, with previews and size limits.
46
+ - Optional pill composer: one animated action for microphone, send, steering and stop, with host-controlled recording and transcription.
46
47
  - Condensed conversation: all user inputs and the latest answer between them, independently of tool/thinking detail.
47
48
  - Timestamps, day separators, message actions, configurable bubbles and appearance.
48
49
  - Screen reader friendly: the transcript is not a live region; finished replies and new
49
50
  actions are announced once, with the host in control of the wording.
50
51
  - Complete English and German UI labels through `englishTexts` / `germanTexts`; `texts` can override any label (English by default).
51
52
  - Grouped steps show their error count even while collapsed.
52
- - Text size follows the host: no overrides needed to fit in (see below).
53
+ - One text size per host, a fixed small size and shared Markdown typography (see below).
53
54
 
54
55
  ## Install
55
56
 
@@ -91,30 +92,55 @@ export function Chat({ send, stop }: { send: (text: string) => Promise<void>; st
91
92
 
92
93
  Any backend that emits the `@schlenkr/quassel/events` stream works with the same components.
93
94
 
94
- ## Text size
95
+ ## Pill composer
95
96
 
96
- quassel starts at 13px with a line height of 1.5, a 10px message gap and a 3px dense
97
- step gap. Tool rows and timestamps use 88% of the text size. Expanded tool groups have a
98
- 1px gap between rows; tool rows have 1px of vertical padding on each side.
97
+ Use `layout="pill"` for a larger, rounded composer with one 48px primary action. Host buttons
98
+ go into `toolbarLeft` and `toolbarRight`; attachments stay just before the primary action.
99
99
 
100
- Every size derives from one base, so nested elements never compound. Set `--qsl-font-size`
101
- on a wrapper, or `appearance.fontSize`, to change it. Use `1em` to follow the host's font size.
102
- `appearance` also offers `toolFontScale`, `toolGroupGap` and `toolRowPadding`.
100
+ ```tsx
101
+ <ChatInputToolbar
102
+ layout="pill"
103
+ maxRows={5}
104
+ running={running}
105
+ onSend={send}
106
+ onStop={stop}
107
+ microphone={microphone}
108
+ replaceInput={microphone.state === "recording" ? <LiveWaveform /> : undefined}
109
+ toolbarLeft={<ConversationControls />}
110
+ />
111
+ ```
103
112
 
104
- User message backgrounds fill the available row by default while text keeps its width and
105
- alignment. `bubbleOptions={{ userBackgroundFullWidth: false }}` restores a fitted background.
106
- Timestamps sit beside messages by default; their size follows the tool text unless overridden:
113
+ Empty input shows the microphone, text or attachments show send, and an empty running turn
114
+ shows stop. Typing while running sends into that turn. Without `microphone`, empty input shows
115
+ a disabled send button. The host owns audio, press-and-hold or click-to-latch behavior, and
116
+ transcription through the typed `ChatMicrophone` API. Recording has a live color and an aura
117
+ driven by `level` (0-1); preparing and transcribing show a spinner. The aura renders outside
118
+ clipping ancestors and animations respect reduced motion. Card, inline and toolbar keep their defaults.
119
+ See the [API, state table and microphone integration](https://github.com/SchlenkR/quassel/blob/main/docs/guide.md#pill-composer) and the gallery's Pill composer demo.
120
+
121
+ ## Text size
122
+
123
+ quassel defaults to 13px. Set `--qsl-font-size` on a wrapper, or `1em` to inherit the host's
124
+ font size. Messages, the composer, action cards and text buttons use that base with line height
125
+ 1.5, the pill input included. All small UI text, including tools and timestamps, uses 85% with
126
+ line height 1.4.
127
+ `--qsl-font` and `--qsl-mono` select the font families. There are no other typography inputs.
128
+ Sizes derive from an absolute base, so nested small elements never compound.
129
+
130
+ `Markdown` uses the same typography in answers and standalone documents:
107
131
 
108
132
  ```tsx
109
- <ChatMessages
110
- messages={messages}
111
- showTimestamps
112
- timestampOptions={{ position: "above", fontScale: 0.88 }}
113
- />
133
+ <Markdown text={documentText} />
114
134
  ```
115
135
 
116
- `--qsl-text-trace`, `--qsl-timestamp-font-size` and the other `--qsl-text-*` variables override
117
- individual sizes. The [guide](https://github.com/SchlenkR/quassel/blob/main/docs/guide.md#text-size) lists the full API.
136
+ User message backgrounds fill the available row by default while text keeps its width and
137
+ alignment. `bubbleOptions={{ userBackgroundFullWidth: false }}` restores a fitted background.
138
+ Timestamps sit beside messages by default; `timestampOptions={{ position: "above" }}` moves
139
+ them above. Message gaps and tool row spacing remain configurable through `appearance`.
140
+
141
+ For 0.6, replace separate size and line-height overrides with `--qsl-font-size` and remove
142
+ all typography fields from `appearance` and timestamp options. The [migration guide](https://github.com/SchlenkR/quassel/blob/main/docs/guide.md#migration-to-06)
143
+ lists the removed variables and options.
118
144
 
119
145
  ## Development
120
146
 
@@ -0,0 +1,4 @@
1
+ export declare function RecordingAura({ anchor, level }: {
2
+ anchor: HTMLButtonElement;
3
+ level?: number;
4
+ }): import("react").ReactPortal;