@bitbaum/chatkit 0.8.2 → 0.10.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 +55 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/markdown.d.ts +39 -5
- package/dist/markdown.d.ts.map +1 -1
- package/dist/markdown.js +108 -11
- package/dist/markdown.js.map +1 -1
- package/dist/react/AnswerActions.d.ts +39 -0
- package/dist/react/AnswerActions.d.ts.map +1 -0
- package/dist/react/AnswerActions.js +49 -0
- package/dist/react/AnswerActions.js.map +1 -0
- package/dist/react/AttachMenu.d.ts +11 -2
- package/dist/react/AttachMenu.d.ts.map +1 -1
- package/dist/react/AttachMenu.js +21 -24
- package/dist/react/AttachMenu.js.map +1 -1
- package/dist/react/Markdown.d.ts +53 -4
- package/dist/react/Markdown.d.ts.map +1 -1
- package/dist/react/Markdown.js +92 -28
- package/dist/react/Markdown.js.map +1 -1
- package/dist/react/Sheet.d.ts +20 -0
- package/dist/react/Sheet.d.ts.map +1 -0
- package/dist/react/Sheet.js +37 -0
- package/dist/react/Sheet.js.map +1 -0
- package/dist/react/Thread.d.ts +19 -2
- package/dist/react/Thread.d.ts.map +1 -1
- package/dist/react/Thread.js +10 -8
- package/dist/react/Thread.js.map +1 -1
- package/dist/react/icons.d.ts +3 -0
- package/dist/react/icons.d.ts.map +1 -1
- package/dist/react/icons.js +3 -0
- package/dist/react/icons.js.map +1 -1
- package/dist/react/index.d.ts +7 -2
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +4 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react/use-speech.d.ts +25 -0
- package/dist/react/use-speech.d.ts.map +1 -0
- package/dist/react/use-speech.js +66 -0
- package/dist/react/use-speech.js.map +1 -0
- package/dist/replies.d.ts +6 -3
- package/dist/replies.d.ts.map +1 -1
- package/dist/replies.js +8 -5
- package/dist/replies.js.map +1 -1
- package/dist/speech.d.ts +17 -0
- package/dist/speech.d.ts.map +1 -0
- package/dist/speech.js +81 -0
- package/dist/speech.js.map +1 -0
- package/package.json +1 -1
- package/styles.css +288 -0
package/dist/react/index.d.ts
CHANGED
|
@@ -12,8 +12,13 @@ export type { ComposerProps, ComposerLabels, ComposerLabelOverrides, ComposerVoi
|
|
|
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
14
|
export { ChatScreen } from "./Screen.js";
|
|
15
|
-
export { Markdown, defaultRenderLink } from "./Markdown.js";
|
|
16
|
-
export
|
|
15
|
+
export { Markdown, SourcesRow, defaultRenderLink, defaultRenderCitation } from "./Markdown.js";
|
|
16
|
+
export { AnswerActions, DEFAULT_ANSWER_ACTIONS_LABELS } from "./AnswerActions.js";
|
|
17
|
+
export type { AnswerActionsLabels } from "./AnswerActions.js";
|
|
18
|
+
export { useSpeech } from "./use-speech.js";
|
|
19
|
+
export type { SpeechController } from "./use-speech.js";
|
|
20
|
+
export { Sheet, prefersSheet } from "./Sheet.js";
|
|
21
|
+
export type { CitationMap, RenderLink, RenderCitation, RenderSources } from "./Markdown.js";
|
|
17
22
|
export { useDictation } from "./use-dictation.js";
|
|
18
23
|
export type { DictationController, DictationStatus, UseDictationOptions } from "./use-dictation.js";
|
|
19
24
|
export { AttachMenu, DEFAULT_ATTACH_MENU_LABELS } from "./AttachMenu.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,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,eAAe,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,UAAU,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC/F,OAAO,EAAE,aAAa,EAAE,6BAA6B,EAAE,MAAM,oBAAoB,CAAC;AAClF,YAAY,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACjD,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC5F,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"}
|
package/dist/react/index.js
CHANGED
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
export { Composer, DEFAULT_COMPOSER_LABELS } from "./Composer.js";
|
|
11
11
|
export { ChatThread, ChatMessage, ChatReplies, ChatStarters, DEFAULT_THREAD_LABELS, } from "./Thread.js";
|
|
12
12
|
export { ChatScreen } from "./Screen.js";
|
|
13
|
-
export { Markdown, defaultRenderLink } from "./Markdown.js";
|
|
13
|
+
export { Markdown, SourcesRow, defaultRenderLink, defaultRenderCitation } from "./Markdown.js";
|
|
14
|
+
export { AnswerActions, DEFAULT_ANSWER_ACTIONS_LABELS } from "./AnswerActions.js";
|
|
15
|
+
export { useSpeech } from "./use-speech.js";
|
|
16
|
+
export { Sheet, prefersSheet } from "./Sheet.js";
|
|
14
17
|
export { useDictation } from "./use-dictation.js";
|
|
15
18
|
export { AttachMenu, DEFAULT_ATTACH_MENU_LABELS } from "./AttachMenu.js";
|
|
16
19
|
export { useAttachments } from "./use-attachments.js";
|
package/dist/react/index.js.map
CHANGED
|
@@ -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,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,eAAe,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,UAAU,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC/F,OAAO,EAAE,aAAa,EAAE,6BAA6B,EAAE,MAAM,oBAAoB,CAAC;AAElF,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEjD,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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type Speaker } from "../speech.js";
|
|
2
|
+
export type SpeechController = {
|
|
3
|
+
/** Something can speak here: the app's speaker, or the browser's voice. */
|
|
4
|
+
supported: boolean;
|
|
5
|
+
speaking: boolean;
|
|
6
|
+
/** Seconds since this reading started; 0 while silent. */
|
|
7
|
+
elapsed: number;
|
|
8
|
+
/** Read this markdown aloud (flattened to prose first), stopping any other. */
|
|
9
|
+
speak: (markdown: string) => void;
|
|
10
|
+
stop: () => void;
|
|
11
|
+
toggle: (markdown: string) => void;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Read an answer aloud — the play button under every answer in the reference
|
|
15
|
+
* apps. The browser's own voices by default: no upload, no key, works
|
|
16
|
+
* offline. An app with a studio voice passes its own `speaker` (loki's
|
|
17
|
+
* Orpheus voice, through the same two methods) and the button, the timer and
|
|
18
|
+
* the stop are the same.
|
|
19
|
+
*
|
|
20
|
+
* One reading at a time: play on a second answer stops the first. Unmounting
|
|
21
|
+
* stops it too — a voice that keeps reading a page you left is the
|
|
22
|
+
* microphone bug's twin.
|
|
23
|
+
*/
|
|
24
|
+
export declare function useSpeech(speaker?: Speaker | null): SpeechController;
|
|
25
|
+
//# sourceMappingURL=use-speech.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-speech.d.ts","sourceRoot":"","sources":["../../src/react/use-speech.ts"],"names":[],"mappings":"AAGA,OAAO,EAA0C,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AAIpF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,2EAA2E;IAC3E,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,gBAAgB,CA4DpE"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
3
|
+
import { createSynthSpeaker, plainTextForSpeech } from "../speech.js";
|
|
4
|
+
const noSubscribe = () => () => { };
|
|
5
|
+
/**
|
|
6
|
+
* Read an answer aloud — the play button under every answer in the reference
|
|
7
|
+
* apps. The browser's own voices by default: no upload, no key, works
|
|
8
|
+
* offline. An app with a studio voice passes its own `speaker` (loki's
|
|
9
|
+
* Orpheus voice, through the same two methods) and the button, the timer and
|
|
10
|
+
* the stop are the same.
|
|
11
|
+
*
|
|
12
|
+
* One reading at a time: play on a second answer stops the first. Unmounting
|
|
13
|
+
* stops it too — a voice that keeps reading a page you left is the
|
|
14
|
+
* microphone bug's twin.
|
|
15
|
+
*/
|
|
16
|
+
export function useSpeech(speaker) {
|
|
17
|
+
const synthSupported = useSyncExternalStore(noSubscribe, () => typeof window !== "undefined" && "speechSynthesis" in window, () => false);
|
|
18
|
+
const supported = Boolean(speaker) || synthSupported;
|
|
19
|
+
const [speaking, setSpeaking] = useState(false);
|
|
20
|
+
const [elapsed, setElapsed] = useState(0);
|
|
21
|
+
const synth = useRef(null);
|
|
22
|
+
const active = useRef(null);
|
|
23
|
+
const token = useRef(0);
|
|
24
|
+
const stop = useCallback(() => {
|
|
25
|
+
token.current++;
|
|
26
|
+
active.current?.cancel();
|
|
27
|
+
active.current = null;
|
|
28
|
+
setSpeaking(false);
|
|
29
|
+
setElapsed(0);
|
|
30
|
+
}, []);
|
|
31
|
+
const speak = useCallback((markdown) => {
|
|
32
|
+
const who = speaker ?? (synth.current ??= createSynthSpeaker());
|
|
33
|
+
if (!who)
|
|
34
|
+
return;
|
|
35
|
+
const text = plainTextForSpeech(markdown);
|
|
36
|
+
if (!text)
|
|
37
|
+
return;
|
|
38
|
+
stop();
|
|
39
|
+
const my = ++token.current;
|
|
40
|
+
active.current = who;
|
|
41
|
+
setSpeaking(true);
|
|
42
|
+
void who.speak(text).then(() => {
|
|
43
|
+
if (my === token.current) {
|
|
44
|
+
active.current = null;
|
|
45
|
+
setSpeaking(false);
|
|
46
|
+
setElapsed(0);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}, [speaker, stop]);
|
|
50
|
+
const toggle = useCallback((markdown) => {
|
|
51
|
+
if (speaking)
|
|
52
|
+
stop();
|
|
53
|
+
else
|
|
54
|
+
speak(markdown);
|
|
55
|
+
}, [speak, speaking, stop]);
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (!speaking)
|
|
58
|
+
return;
|
|
59
|
+
const started = Date.now();
|
|
60
|
+
const t = setInterval(() => setElapsed((Date.now() - started) / 1000), 500);
|
|
61
|
+
return () => clearInterval(t);
|
|
62
|
+
}, [speaking]);
|
|
63
|
+
useEffect(() => () => stop(), [stop]);
|
|
64
|
+
return { supported, speaking, elapsed, speak, stop, toggle };
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=use-speech.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-speech.js","sourceRoot":"","sources":["../../src/react/use-speech.ts"],"names":[],"mappings":"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"}
|
package/dist/replies.d.ts
CHANGED
|
@@ -24,9 +24,12 @@ export type RepliesOptions = {
|
|
|
24
24
|
maxLength?: number;
|
|
25
25
|
};
|
|
26
26
|
/**
|
|
27
|
-
* Append to a system prompt.
|
|
28
|
-
*
|
|
29
|
-
* makes
|
|
27
|
+
* Append to a system prompt. The model leads: every answer ends with the two
|
|
28
|
+
* to four things the person is most likely to say next, the first being the
|
|
29
|
+
* recommended next step. That is what makes a chat on a phone usable with a
|
|
30
|
+
* thumb — the answer is read, the next move is one tap (owner, 2026-10-21:
|
|
31
|
+
* "AI helps us to answer and lead the conversation"). Only an answer that
|
|
32
|
+
* ends the conversation leaves the block off.
|
|
30
33
|
*/
|
|
31
34
|
export declare const REPLIES_INSTRUCTION: string;
|
|
32
35
|
/**
|
package/dist/replies.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replies.d.ts","sourceRoot":"","sources":["../src/replies.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,eAAO,MAAM,aAAa,kBAAkB,CAAC;AAE7C,MAAM,MAAM,cAAc,GAAG;IAC3B,4CAA4C;IAC5C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"replies.d.ts","sourceRoot":"","sources":["../src/replies.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,eAAO,MAAM,aAAa,kBAAkB,CAAC;AAE7C,MAAM,MAAM,cAAc,GAAG;IAC3B,4CAA4C;IAC5C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,QAOpB,CAAC;AA+Bb;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,cAAmB,GAC3B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAarC"}
|
package/dist/replies.js
CHANGED
|
@@ -18,17 +18,20 @@
|
|
|
18
18
|
*/
|
|
19
19
|
export const REPLIES_FENCE = "quick_replies";
|
|
20
20
|
/**
|
|
21
|
-
* Append to a system prompt.
|
|
22
|
-
*
|
|
23
|
-
* makes
|
|
21
|
+
* Append to a system prompt. The model leads: every answer ends with the two
|
|
22
|
+
* to four things the person is most likely to say next, the first being the
|
|
23
|
+
* recommended next step. That is what makes a chat on a phone usable with a
|
|
24
|
+
* thumb — the answer is read, the next move is one tap (owner, 2026-10-21:
|
|
25
|
+
* "AI helps us to answer and lead the conversation"). Only an answer that
|
|
26
|
+
* ends the conversation leaves the block off.
|
|
24
27
|
*/
|
|
25
28
|
export const REPLIES_INSTRUCTION = [
|
|
26
29
|
"## Suggested replies",
|
|
27
|
-
"
|
|
30
|
+
"End every answer with a fenced block of 2–4 short replies the person could tap instead of typing. Put the most useful next step FIRST — the one you would recommend — then alternatives:",
|
|
28
31
|
"```" + REPLIES_FENCE,
|
|
29
32
|
'["Yes, do it", "Show me first", "Not now"]',
|
|
30
33
|
"```",
|
|
31
|
-
"Rules: write them in the person's language, in their voice (what THEY would say, not what you would). Each under 40 characters. Each must make sense sent on its own. Never repeat what they already said. Put the block last, after everything else. Leave it out
|
|
34
|
+
"Rules: write them in the person's language, in their voice (what THEY would say, not what you would). Each under 40 characters. Each must make sense sent on its own. Never repeat what they already said. Put the block last, after everything else. Leave it out only when the conversation is over (a goodbye, a thanks with nothing left to do).",
|
|
32
35
|
].join("\n");
|
|
33
36
|
const COMPLETE = new RegExp("\\n?```" + REPLIES_FENCE + "\\s*([\\s\\S]*?)```", "i");
|
|
34
37
|
// Still streaming: the fence has opened (or is opening) and not yet closed.
|
package/dist/replies.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replies.js","sourceRoot":"","sources":["../src/replies.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAC;AAS7C
|
|
1
|
+
{"version":3,"file":"replies.js","sourceRoot":"","sources":["../src/replies.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAC;AAS7C;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,sBAAsB;IACtB,0LAA0L;IAC1L,KAAK,GAAG,aAAa;IACrB,4CAA4C;IAC5C,KAAK;IACL,sVAAsV;CACvV,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,SAAS,GAAG,aAAa,GAAG,qBAAqB,EAAE,GAAG,CAAC,CAAC;AACpF,4EAA4E;AAC5E,MAAM,SAAS,GAAG,IAAI,MAAM,CAAC,YAAY,GAAG,aAAa,GAAG,uBAAuB,EAAE,GAAG,CAAC,CAAC;AAE1F,SAAS,KAAK,CAAC,GAAW,EAAE,EAAE,GAAG,GAAG,CAAC,EAAE,SAAS,GAAG,EAAE,EAAkB;IACrE,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,uEAAuE;QACvE,MAAM,GAAG,GAAG;aACT,KAAK,CAAC,OAAO,CAAC;aACd,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,0BAA0B,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC,CAAC;IACzF,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,CAAC;IACtC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;QAC1B,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,SAAS;QACvC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QAChC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAClE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChB,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG;YAAE,MAAM;IAC/B,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,IAAY,EACZ,UAA0B,EAAE;IAE5B,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QAChG,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,OAAO,CAAC,EAAE,CAAC;IACjE,CAAC;IACD,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClC,yEAAyE;IACzE,4CAA4C;IAC5C,IAAI,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,KAAK,EAAE,CAAC;QACrC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACpE,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;AAC/B,CAAC"}
|
package/dist/speech.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare function plainTextForSpeech(markdown: string): string;
|
|
2
|
+
/**
|
|
3
|
+
* Which voice language to ask for. The fleet's operators write mixed German
|
|
4
|
+
* and English; a German sentence is detectable cheaply enough by its most
|
|
5
|
+
* common function words to pick the voice, and the page's language otherwise.
|
|
6
|
+
*/
|
|
7
|
+
export declare function guessSpeechLang(text: string, fallback?: string): string;
|
|
8
|
+
/** Who reads the words. The browser's own voice by default (`useSpeech`); an
|
|
9
|
+
* app with a studio voice hands in its own with the same two methods. */
|
|
10
|
+
export type Speaker = {
|
|
11
|
+
/** Resolves when the text has been read, or cut off. Never rejects. */
|
|
12
|
+
speak(text: string): Promise<void>;
|
|
13
|
+
cancel(): void;
|
|
14
|
+
};
|
|
15
|
+
/** The browser's own voice: free, offline, no key. `null` where there is none. */
|
|
16
|
+
export declare function createSynthSpeaker(): Speaker | null;
|
|
17
|
+
//# sourceMappingURL=speech.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"speech.d.ts","sourceRoot":"","sources":["../src/speech.ts"],"names":[],"mappings":"AAUA,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA8B3D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAO,GAAG,MAAM,CAWrE;AAED;0EAC0E;AAC1E,MAAM,MAAM,OAAO,GAAG;IACpB,uEAAuE;IACvE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,IAAI,IAAI,CAAC;CAChB,CAAC;AAEF,kFAAkF;AAClF,wBAAgB,kBAAkB,IAAI,OAAO,GAAG,IAAI,CAuBnD"}
|
package/dist/speech.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An answer as it should be READ ALOUD.
|
|
3
|
+
*
|
|
4
|
+
* Markdown read literally is noise — "hash hash Summary, asterisk asterisk",
|
|
5
|
+
* a citation "F eight" after every claim — so the text is flattened first.
|
|
6
|
+
* From loki's `speech-text`, where the play button under every answer first
|
|
7
|
+
* lived. Pure, so it is tested without a voice.
|
|
8
|
+
*/
|
|
9
|
+
import { extractReplies } from "./replies.js";
|
|
10
|
+
export function plainTextForSpeech(markdown) {
|
|
11
|
+
// Suggested replies are buttons, never words: a quick_replies block is
|
|
12
|
+
// dropped outright rather than announced as "(code)".
|
|
13
|
+
const prose = extractReplies(markdown).text;
|
|
14
|
+
return (prose
|
|
15
|
+
// Fenced code is not prose; say that it is there rather than reading it.
|
|
16
|
+
.replace(/```[\s\S]*?```/g, " (code) ")
|
|
17
|
+
.replace(/`([^`]*)`/g, "$1")
|
|
18
|
+
// Citations like [F8] or [D1] are for the eye.
|
|
19
|
+
.replace(/\[(?:[A-Z]{1,2}\d{1,3}(?:,\s*[A-Z]{1,2}\d{1,3})*)\]/g, "")
|
|
20
|
+
// Links and images: keep the words, drop the URL.
|
|
21
|
+
.replace(/!\[[^\]]*\]\([^)]*\)/g, "")
|
|
22
|
+
.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1")
|
|
23
|
+
.replace(/https?:\/\/\S+/g, "")
|
|
24
|
+
// Headings, emphasis, quotes, bullets, tasks, rules, tables.
|
|
25
|
+
.replace(/^\s{0,3}#{1,6}\s+/gm, "")
|
|
26
|
+
.replace(/^\s*>\s?/gm, "")
|
|
27
|
+
.replace(/^\s*[-*+]\s+(?:\[[ xX]\]\s+)?/gm, "")
|
|
28
|
+
.replace(/^\s*\d+[.)]\s+/gm, "")
|
|
29
|
+
.replace(/^\s*[-*_]{3,}\s*$/gm, "")
|
|
30
|
+
.replace(/^\s*\|?\s*:?-{2,}:?\s*(\|\s*:?-{2,}:?\s*)*\|?\s*$/gm, "")
|
|
31
|
+
.replace(/\|/g, " ")
|
|
32
|
+
.replace(/(\*\*|__)(.*?)\1/g, "$2")
|
|
33
|
+
.replace(/(\*|_)(.*?)\1/g, "$2")
|
|
34
|
+
.replace(/~~(.*?)~~/g, "$1")
|
|
35
|
+
.replace(/[ \t]+/g, " ")
|
|
36
|
+
.replace(/\s*\n\s*\n\s*/g, "\n")
|
|
37
|
+
.trim());
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Which voice language to ask for. The fleet's operators write mixed German
|
|
41
|
+
* and English; a German sentence is detectable cheaply enough by its most
|
|
42
|
+
* common function words to pick the voice, and the page's language otherwise.
|
|
43
|
+
*/
|
|
44
|
+
export function guessSpeechLang(text, fallback = "en") {
|
|
45
|
+
const sample = text.toLowerCase().slice(0, 600);
|
|
46
|
+
const german = (sample.match(/\b(und|nicht|das|ist|ich|mit|für|auf|eine?|der|die|sind|wird|noch|auch)\b/g) ?? []).length;
|
|
47
|
+
const english = (sample.match(/\b(and|not|the|is|with|for|on|a|an|are|will|also|this|that)\b/g) ?? []).length;
|
|
48
|
+
if (german > english && german >= 3)
|
|
49
|
+
return "de";
|
|
50
|
+
if (english >= 3)
|
|
51
|
+
return "en";
|
|
52
|
+
return fallback;
|
|
53
|
+
}
|
|
54
|
+
/** The browser's own voice: free, offline, no key. `null` where there is none. */
|
|
55
|
+
export function createSynthSpeaker() {
|
|
56
|
+
if (typeof window === "undefined" || !("speechSynthesis" in window))
|
|
57
|
+
return null;
|
|
58
|
+
let current = null;
|
|
59
|
+
return {
|
|
60
|
+
speak(text) {
|
|
61
|
+
return new Promise((resolve) => {
|
|
62
|
+
window.speechSynthesis.cancel();
|
|
63
|
+
const u = new SpeechSynthesisUtterance(text);
|
|
64
|
+
u.lang = guessSpeechLang(text, document.documentElement.lang || "en");
|
|
65
|
+
u.onend = () => {
|
|
66
|
+
if (current === u)
|
|
67
|
+
current = null;
|
|
68
|
+
resolve();
|
|
69
|
+
};
|
|
70
|
+
u.onerror = u.onend;
|
|
71
|
+
current = u;
|
|
72
|
+
window.speechSynthesis.speak(u);
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
cancel() {
|
|
76
|
+
window.speechSynthesis.cancel();
|
|
77
|
+
current = null;
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=speech.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"speech.js","sourceRoot":"","sources":["../src/speech.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitbaum/chatkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.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
|
@@ -122,6 +122,15 @@
|
|
|
122
122
|
}
|
|
123
123
|
.ck-input::placeholder {
|
|
124
124
|
color: var(--ck-muted);
|
|
125
|
+
/* One line, always: an empty box is one row high on every phone, and a
|
|
126
|
+
long placeholder is cut at the edge rather than wrapped into a second
|
|
127
|
+
line that the box then clips in half (320px, 2026-10-21). */
|
|
128
|
+
white-space: nowrap;
|
|
129
|
+
overflow: hidden;
|
|
130
|
+
text-overflow: ellipsis;
|
|
131
|
+
}
|
|
132
|
+
.ck-input:placeholder-shown {
|
|
133
|
+
overflow: hidden;
|
|
125
134
|
}
|
|
126
135
|
.ck-actions {
|
|
127
136
|
display: flex;
|
|
@@ -786,7 +795,175 @@
|
|
|
786
795
|
margin-top: 0.25em;
|
|
787
796
|
}
|
|
788
797
|
.ck-md-h {
|
|
798
|
+
margin: 0;
|
|
789
799
|
font-weight: 650;
|
|
800
|
+
line-height: 1.3;
|
|
801
|
+
}
|
|
802
|
+
.ck-md > .ck-md-h + * {
|
|
803
|
+
margin-top: 0.5em;
|
|
804
|
+
}
|
|
805
|
+
.ck-md > * + .ck-md-h {
|
|
806
|
+
margin-top: 1.25em;
|
|
807
|
+
}
|
|
808
|
+
.ck-md-h1 {
|
|
809
|
+
font-size: 1.375em;
|
|
810
|
+
}
|
|
811
|
+
.ck-md-h2 {
|
|
812
|
+
font-size: 1.1875em;
|
|
813
|
+
}
|
|
814
|
+
.ck-md-h3 {
|
|
815
|
+
font-size: 1.0625em;
|
|
816
|
+
}
|
|
817
|
+
.ck-md ul ul,
|
|
818
|
+
.ck-md ol ul {
|
|
819
|
+
margin-top: 0.25em;
|
|
820
|
+
}
|
|
821
|
+
.ck-task {
|
|
822
|
+
list-style: none;
|
|
823
|
+
margin-left: -1.25em;
|
|
824
|
+
}
|
|
825
|
+
.ck-task > input {
|
|
826
|
+
margin: 0 0.5em 0 0;
|
|
827
|
+
vertical-align: -0.1em;
|
|
828
|
+
accent-color: var(--ck-accent);
|
|
829
|
+
pointer-events: none;
|
|
830
|
+
}
|
|
831
|
+
.ck-hr {
|
|
832
|
+
height: 1px;
|
|
833
|
+
margin: 1.25em 0;
|
|
834
|
+
border: 0;
|
|
835
|
+
background: var(--ck-line);
|
|
836
|
+
}
|
|
837
|
+
.ck-img {
|
|
838
|
+
display: block;
|
|
839
|
+
max-width: 100%;
|
|
840
|
+
height: auto;
|
|
841
|
+
margin: 0.25em 0;
|
|
842
|
+
border: 1px solid var(--ck-line);
|
|
843
|
+
border-radius: var(--ck-radius-sm);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/* A list of named things, as cards: a badge, the name, the body. The row is
|
|
847
|
+
a grid so a long name wraps under itself, never under the badge. */
|
|
848
|
+
.ck-cards {
|
|
849
|
+
display: flex;
|
|
850
|
+
flex-direction: column;
|
|
851
|
+
gap: 0.875em;
|
|
852
|
+
margin: 0;
|
|
853
|
+
padding: 0;
|
|
854
|
+
list-style: none;
|
|
855
|
+
}
|
|
856
|
+
.ck-card {
|
|
857
|
+
display: grid;
|
|
858
|
+
grid-template-columns: 2.5em minmax(0, 1fr);
|
|
859
|
+
gap: 0.75em;
|
|
860
|
+
align-items: start;
|
|
861
|
+
}
|
|
862
|
+
.ck-card-badge {
|
|
863
|
+
display: flex;
|
|
864
|
+
align-items: center;
|
|
865
|
+
justify-content: center;
|
|
866
|
+
width: 2.5em;
|
|
867
|
+
height: 2.5em;
|
|
868
|
+
border-radius: 0.75em;
|
|
869
|
+
background: var(--ck-raised);
|
|
870
|
+
color: var(--ck-fg-2);
|
|
871
|
+
font-size: 1em;
|
|
872
|
+
font-weight: 600;
|
|
873
|
+
font-variant-numeric: tabular-nums;
|
|
874
|
+
}
|
|
875
|
+
.ck-cards:not(.ck-cards-numbered) .ck-card-badge:not(.ck-card-icon) {
|
|
876
|
+
font-size: 1.5em;
|
|
877
|
+
line-height: 1;
|
|
878
|
+
}
|
|
879
|
+
.ck-card-icon {
|
|
880
|
+
font-size: 1.25em;
|
|
881
|
+
}
|
|
882
|
+
.ck-card-body {
|
|
883
|
+
min-width: 0;
|
|
884
|
+
padding-top: 0.2em;
|
|
885
|
+
}
|
|
886
|
+
.ck-card-title {
|
|
887
|
+
font-weight: 650;
|
|
888
|
+
}
|
|
889
|
+
.ck-card-text {
|
|
890
|
+
margin-top: 0.25em;
|
|
891
|
+
color: var(--ck-fg-2);
|
|
892
|
+
}
|
|
893
|
+
.ck-card-body > ul {
|
|
894
|
+
margin-top: 0.375em;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
/* A table that scrolls sideways on a phone instead of breaking the page. */
|
|
898
|
+
.ck-table-wrap {
|
|
899
|
+
max-width: 100%;
|
|
900
|
+
overflow-x: auto;
|
|
901
|
+
-webkit-overflow-scrolling: touch;
|
|
902
|
+
}
|
|
903
|
+
.ck-table {
|
|
904
|
+
width: 100%;
|
|
905
|
+
border-collapse: collapse;
|
|
906
|
+
font-size: 0.9375em;
|
|
907
|
+
line-height: 1.45;
|
|
908
|
+
}
|
|
909
|
+
.ck-table th,
|
|
910
|
+
.ck-table td {
|
|
911
|
+
padding: 0.5em 0.75em 0.5em 0;
|
|
912
|
+
border-bottom: 1px solid var(--ck-line);
|
|
913
|
+
text-align: left;
|
|
914
|
+
vertical-align: top;
|
|
915
|
+
}
|
|
916
|
+
.ck-table th {
|
|
917
|
+
color: var(--ck-fg);
|
|
918
|
+
font-weight: 650;
|
|
919
|
+
}
|
|
920
|
+
.ck-table td {
|
|
921
|
+
color: var(--ck-fg);
|
|
922
|
+
}
|
|
923
|
+
.ck-table tr:last-child td {
|
|
924
|
+
border-bottom: 0;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
/* The sources an answer cites, once, under it: a label and a chip each. */
|
|
928
|
+
.ck-sources {
|
|
929
|
+
display: flex;
|
|
930
|
+
flex-wrap: wrap;
|
|
931
|
+
align-items: center;
|
|
932
|
+
gap: 0.375rem;
|
|
933
|
+
margin-top: 1em;
|
|
934
|
+
}
|
|
935
|
+
.ck-sources-label {
|
|
936
|
+
margin-right: 0.25rem;
|
|
937
|
+
color: var(--ck-muted);
|
|
938
|
+
font-size: 0.8125rem;
|
|
939
|
+
}
|
|
940
|
+
.ck-source {
|
|
941
|
+
display: inline-flex;
|
|
942
|
+
align-items: center;
|
|
943
|
+
gap: 0.375em;
|
|
944
|
+
max-width: 100%;
|
|
945
|
+
min-height: 2rem;
|
|
946
|
+
padding: 0 0.625rem;
|
|
947
|
+
border: 1px solid var(--ck-line);
|
|
948
|
+
border-radius: 999px;
|
|
949
|
+
background: var(--ck-raised);
|
|
950
|
+
color: var(--ck-fg-2);
|
|
951
|
+
font-size: 0.8125rem;
|
|
952
|
+
text-decoration: none;
|
|
953
|
+
}
|
|
954
|
+
a.ck-source:hover {
|
|
955
|
+
border-color: var(--ck-line-2);
|
|
956
|
+
color: var(--ck-fg);
|
|
957
|
+
}
|
|
958
|
+
.ck-source-id {
|
|
959
|
+
font-family: var(--ck-mono);
|
|
960
|
+
font-size: 0.75em;
|
|
961
|
+
color: var(--ck-muted);
|
|
962
|
+
}
|
|
963
|
+
.ck-source-label {
|
|
964
|
+
overflow: hidden;
|
|
965
|
+
text-overflow: ellipsis;
|
|
966
|
+
white-space: nowrap;
|
|
790
967
|
}
|
|
791
968
|
.ck-md blockquote {
|
|
792
969
|
padding-left: 0.875em;
|
|
@@ -889,6 +1066,13 @@
|
|
|
889
1066
|
.ck-reply:hover {
|
|
890
1067
|
border-color: var(--ck-accent);
|
|
891
1068
|
}
|
|
1069
|
+
/* The first reply is the recommended next step: the same pill, drawn as the
|
|
1070
|
+
lead — a firmer line and the reading colour — so the eye lands on it. */
|
|
1071
|
+
.ck-reply-lead {
|
|
1072
|
+
border-color: var(--ck-line-2);
|
|
1073
|
+
background: var(--ck-raised);
|
|
1074
|
+
font-weight: 500;
|
|
1075
|
+
}
|
|
892
1076
|
.ck-reply:disabled {
|
|
893
1077
|
opacity: 0.5;
|
|
894
1078
|
cursor: default;
|
|
@@ -1026,6 +1210,110 @@
|
|
|
1026
1210
|
cursor: not-allowed;
|
|
1027
1211
|
opacity: 0.45;
|
|
1028
1212
|
}
|
|
1213
|
+
/* An app's own rows under Camera · Photos · Files in the attach sheet. */
|
|
1214
|
+
.ck-sheet-extra {
|
|
1215
|
+
display: flex;
|
|
1216
|
+
flex-direction: column;
|
|
1217
|
+
gap: 0.125rem;
|
|
1218
|
+
margin-top: 0.75rem;
|
|
1219
|
+
padding-top: 0.75rem;
|
|
1220
|
+
border-top: 1px solid var(--ck-line);
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
/* while an answer is read aloud: Stop, a quiet wave, the time so far */
|
|
1224
|
+
.ck-player {
|
|
1225
|
+
align-items: center;
|
|
1226
|
+
}
|
|
1227
|
+
.ck-action-stop {
|
|
1228
|
+
color: var(--ck-fg);
|
|
1229
|
+
}
|
|
1230
|
+
.ck-player-wave {
|
|
1231
|
+
display: inline-flex;
|
|
1232
|
+
align-items: center;
|
|
1233
|
+
gap: 2px;
|
|
1234
|
+
height: 1rem;
|
|
1235
|
+
}
|
|
1236
|
+
.ck-player-wave > span {
|
|
1237
|
+
display: block;
|
|
1238
|
+
width: 2px;
|
|
1239
|
+
height: 0.375rem;
|
|
1240
|
+
border-radius: 1px;
|
|
1241
|
+
background: var(--ck-fg-2);
|
|
1242
|
+
animation: ck-wave 1s ease-in-out infinite;
|
|
1243
|
+
}
|
|
1244
|
+
.ck-player-wave > span:nth-child(2n) {
|
|
1245
|
+
animation-delay: 0.15s;
|
|
1246
|
+
}
|
|
1247
|
+
.ck-player-wave > span:nth-child(3n) {
|
|
1248
|
+
animation-delay: 0.3s;
|
|
1249
|
+
}
|
|
1250
|
+
@keyframes ck-wave {
|
|
1251
|
+
50% {
|
|
1252
|
+
height: 1rem;
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
.ck-player-time {
|
|
1256
|
+
font-size: 0.8125rem;
|
|
1257
|
+
font-variant-numeric: tabular-nums;
|
|
1258
|
+
color: var(--ck-fg-2);
|
|
1259
|
+
}
|
|
1260
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1261
|
+
.ck-player-wave > span {
|
|
1262
|
+
animation: none;
|
|
1263
|
+
height: 0.75rem;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
/* the sources sheet on a touch screen: one row per record */
|
|
1268
|
+
.ck-sources-open {
|
|
1269
|
+
cursor: pointer;
|
|
1270
|
+
font: inherit;
|
|
1271
|
+
}
|
|
1272
|
+
.ck-sources-open > svg {
|
|
1273
|
+
font-size: 1rem;
|
|
1274
|
+
}
|
|
1275
|
+
.ck-source-list {
|
|
1276
|
+
display: flex;
|
|
1277
|
+
flex-direction: column;
|
|
1278
|
+
margin: 0;
|
|
1279
|
+
padding: 0;
|
|
1280
|
+
list-style: none;
|
|
1281
|
+
}
|
|
1282
|
+
.ck-source-row {
|
|
1283
|
+
display: flex;
|
|
1284
|
+
align-items: flex-start;
|
|
1285
|
+
gap: 0.75rem;
|
|
1286
|
+
min-height: var(--ck-target);
|
|
1287
|
+
padding: 0.625rem 0.25rem;
|
|
1288
|
+
border-top: 1px solid var(--ck-line);
|
|
1289
|
+
color: var(--ck-fg);
|
|
1290
|
+
text-decoration: none;
|
|
1291
|
+
}
|
|
1292
|
+
.ck-source-list > li:first-child .ck-source-row {
|
|
1293
|
+
border-top: 0;
|
|
1294
|
+
}
|
|
1295
|
+
.ck-source-row .ck-source-id {
|
|
1296
|
+
margin-top: 0.125rem;
|
|
1297
|
+
}
|
|
1298
|
+
.ck-source-body {
|
|
1299
|
+
display: flex;
|
|
1300
|
+
min-width: 0;
|
|
1301
|
+
flex-direction: column;
|
|
1302
|
+
gap: 0.125rem;
|
|
1303
|
+
}
|
|
1304
|
+
.ck-source-title {
|
|
1305
|
+
font-weight: 500;
|
|
1306
|
+
overflow-wrap: anywhere;
|
|
1307
|
+
}
|
|
1308
|
+
.ck-source-detail {
|
|
1309
|
+
font-size: 0.8125rem;
|
|
1310
|
+
color: var(--ck-fg-2);
|
|
1311
|
+
overflow-wrap: anywhere;
|
|
1312
|
+
}
|
|
1313
|
+
a.ck-source-row:hover .ck-source-title {
|
|
1314
|
+
text-decoration: underline;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1029
1317
|
@keyframes ck-rise {
|
|
1030
1318
|
from {
|
|
1031
1319
|
transform: translateY(1.5rem);
|