@bitbaum/chatkit 0.2.1 → 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 +137 -8
- package/bin/chatkit-check-file-inputs.mjs +87 -0
- package/dist/attachments.d.ts +37 -0
- package/dist/attachments.d.ts.map +1 -1
- package/dist/attachments.js +75 -1
- package/dist/attachments.js.map +1 -1
- package/dist/composer.d.ts +4 -1
- package/dist/composer.d.ts.map +1 -1
- package/dist/composer.js +4 -2
- package/dist/composer.js.map +1 -1
- package/dist/dictation.d.ts +62 -2
- package/dist/dictation.d.ts.map +1 -1
- package/dist/dictation.js +103 -0
- package/dist/dictation.js.map +1 -1
- package/dist/image.d.ts +7 -0
- package/dist/image.d.ts.map +1 -0
- package/dist/image.js +41 -0
- package/dist/image.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/react/AttachMenu.d.ts +37 -0
- package/dist/react/AttachMenu.d.ts.map +1 -0
- package/dist/react/AttachMenu.js +70 -0
- package/dist/react/AttachMenu.js.map +1 -0
- package/dist/react/Composer.d.ts +24 -2
- package/dist/react/Composer.d.ts.map +1 -1
- package/dist/react/Composer.js +59 -10
- package/dist/react/Composer.js.map +1 -1
- package/dist/react/Screen.d.ts +37 -0
- package/dist/react/Screen.d.ts.map +1 -0
- package/dist/react/Screen.js +56 -0
- package/dist/react/Screen.js.map +1 -0
- package/dist/react/Thread.d.ts +27 -2
- package/dist/react/Thread.d.ts.map +1 -1
- package/dist/react/Thread.js +17 -4
- package/dist/react/Thread.js.map +1 -1
- package/dist/react/attach.d.ts +17 -0
- package/dist/react/attach.d.ts.map +1 -0
- package/dist/react/attach.js +16 -0
- package/dist/react/attach.js.map +1 -0
- package/dist/react/hooks.d.ts +9 -2
- package/dist/react/hooks.d.ts.map +1 -1
- package/dist/react/hooks.js +56 -5
- package/dist/react/hooks.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 +4 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +3 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react/use-attachments.d.ts.map +1 -1
- package/dist/react/use-attachments.js +6 -34
- package/dist/react/use-attachments.js.map +1 -1
- package/dist/react/use-dictation.d.ts +23 -0
- package/dist/react/use-dictation.d.ts.map +1 -1
- package/dist/react/use-dictation.js +175 -13
- package/dist/react/use-dictation.js.map +1 -1
- package/dist/replies.d.ts +41 -0
- package/dist/replies.d.ts.map +1 -0
- package/dist/replies.js +84 -0
- package/dist/replies.js.map +1 -0
- package/package.json +9 -1
- package/styles.css +339 -4
package/dist/replies.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Suggested replies: the two to four things the person is most likely to say
|
|
3
|
+
* next, as buttons under the answer. One tap sends it.
|
|
4
|
+
*
|
|
5
|
+
* From OrangeCat's Cat, where it was the most-used control in the chat and
|
|
6
|
+
* existed nowhere else in the fleet. The model writes the replies itself, at
|
|
7
|
+
* the end of its answer, in a fenced block:
|
|
8
|
+
*
|
|
9
|
+
* ```quick_replies
|
|
10
|
+
* ["Yes, set it up", "Show me the numbers first", "Not now"]
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* Asking the model in the same turn costs no second call and no latency, and
|
|
14
|
+
* the replies are written by the thing that knows what it just asked. This
|
|
15
|
+
* file is the one place the format lives: the instruction that asks for it,
|
|
16
|
+
* and the parser that takes it back out — including while it is still
|
|
17
|
+
* streaming, so a reader never watches raw JSON appear under an answer.
|
|
18
|
+
*/
|
|
19
|
+
export const REPLIES_FENCE = "quick_replies";
|
|
20
|
+
/**
|
|
21
|
+
* Append to a system prompt. Written so a model leaves the block off when no
|
|
22
|
+
* reply is likely — a button row on every answer is noise, and noise is what
|
|
23
|
+
* makes people stop reading buttons.
|
|
24
|
+
*/
|
|
25
|
+
export const REPLIES_INSTRUCTION = [
|
|
26
|
+
"## Suggested replies",
|
|
27
|
+
"When the person's next message is predictable — you asked a question, offered a choice, or there is an obvious next step — end your answer with a fenced block of 2–4 short replies they could tap instead of typing:",
|
|
28
|
+
"```" + REPLIES_FENCE,
|
|
29
|
+
'["Yes, do it", "Show me first", "Not now"]',
|
|
30
|
+
"```",
|
|
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 entirely when no reply is likely, or when the conversation is over.",
|
|
32
|
+
].join("\n");
|
|
33
|
+
const COMPLETE = new RegExp("\\n?```" + REPLIES_FENCE + "\\s*([\\s\\S]*?)```", "i");
|
|
34
|
+
// Still streaming: the fence has opened (or is opening) and not yet closed.
|
|
35
|
+
const OPEN_TAIL = new RegExp("\\n?```(?:" + REPLIES_FENCE + "[\\s\\S]*|q[a-z_]*)?$", "i");
|
|
36
|
+
function clean(raw, { max = 4, maxLength = 48 }) {
|
|
37
|
+
let parsed;
|
|
38
|
+
try {
|
|
39
|
+
parsed = JSON.parse(raw.trim());
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// A model sometimes writes one reply per line instead of a JSON array.
|
|
43
|
+
parsed = raw
|
|
44
|
+
.split(/\r?\n/)
|
|
45
|
+
.map((l) => l.replace(/^\s*(?:[-*•]|\d+[.)])\s*/, "").replace(/^["']|["'],?$/g, ""));
|
|
46
|
+
}
|
|
47
|
+
if (!Array.isArray(parsed))
|
|
48
|
+
return [];
|
|
49
|
+
const seen = new Set();
|
|
50
|
+
const out = [];
|
|
51
|
+
for (const item of parsed) {
|
|
52
|
+
if (typeof item !== "string")
|
|
53
|
+
continue;
|
|
54
|
+
const reply = item.trim();
|
|
55
|
+
const key = reply.toLowerCase();
|
|
56
|
+
if (!reply || reply.length > maxLength || seen.has(key))
|
|
57
|
+
continue;
|
|
58
|
+
seen.add(key);
|
|
59
|
+
out.push(reply);
|
|
60
|
+
if (out.length >= max)
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Split an answer into the text to show and its suggested replies. Safe on a
|
|
67
|
+
* partial, streaming answer: an unfinished block is hidden, never shown.
|
|
68
|
+
* Text with no block comes back unchanged with `replies: []`.
|
|
69
|
+
*/
|
|
70
|
+
export function extractReplies(text, options = {}) {
|
|
71
|
+
const match = COMPLETE.exec(text);
|
|
72
|
+
if (match) {
|
|
73
|
+
const rest = (text.slice(0, match.index) + text.slice(match.index + match[0].length)).trimEnd();
|
|
74
|
+
return { text: rest, replies: clean(match[1] ?? "", options) };
|
|
75
|
+
}
|
|
76
|
+
const tail = OPEN_TAIL.exec(text);
|
|
77
|
+
// A bare ``` at the end may be the start of an ordinary code block; only
|
|
78
|
+
// hide it while it could still become ours.
|
|
79
|
+
if (tail && tail[0].trim() !== "```") {
|
|
80
|
+
return { text: text.slice(0, tail.index).trimEnd(), replies: [] };
|
|
81
|
+
}
|
|
82
|
+
return { text, replies: [] };
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=replies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replies.js","sourceRoot":"","sources":["../src/replies.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.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",
|
|
@@ -36,8 +36,12 @@
|
|
|
36
36
|
},
|
|
37
37
|
"files": [
|
|
38
38
|
"dist",
|
|
39
|
+
"bin",
|
|
39
40
|
"styles.css"
|
|
40
41
|
],
|
|
42
|
+
"bin": {
|
|
43
|
+
"chatkit-check-file-inputs": "./bin/chatkit-check-file-inputs.mjs"
|
|
44
|
+
},
|
|
41
45
|
"exports": {
|
|
42
46
|
".": {
|
|
43
47
|
"types": "./dist/index.d.ts",
|
|
@@ -47,6 +51,10 @@
|
|
|
47
51
|
"types": "./dist/react/index.d.ts",
|
|
48
52
|
"default": "./dist/react/index.js"
|
|
49
53
|
},
|
|
54
|
+
"./attach": {
|
|
55
|
+
"types": "./dist/react/attach.d.ts",
|
|
56
|
+
"default": "./dist/react/attach.js"
|
|
57
|
+
},
|
|
50
58
|
"./styles.css": "./styles.css"
|
|
51
59
|
},
|
|
52
60
|
"scripts": {
|
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,18 +44,28 @@
|
|
|
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
|
-
.ck-starters
|
|
58
|
+
.ck-starters,
|
|
59
|
+
.ck-replies {
|
|
51
60
|
font-family: var(--ck-font);
|
|
52
61
|
color: var(--ck-fg);
|
|
53
62
|
}
|
|
54
63
|
|
|
64
|
+
.ck-screen *,
|
|
55
65
|
.ck-composer-wrap *,
|
|
56
66
|
.ck-thread-wrap *,
|
|
57
|
-
.ck-starters
|
|
67
|
+
.ck-starters *,
|
|
68
|
+
.ck-replies * {
|
|
58
69
|
box-sizing: border-box;
|
|
59
70
|
}
|
|
60
71
|
|
|
@@ -195,7 +206,7 @@
|
|
|
195
206
|
color: var(--ck-fg);
|
|
196
207
|
border: 1px solid var(--ck-line-2);
|
|
197
208
|
}
|
|
198
|
-
:where(.ck-composer-wrap, .ck-thread-wrap, .ck-starters) button:focus-visible,
|
|
209
|
+
:where(.ck-composer-wrap, .ck-thread-wrap, .ck-starters, .ck-replies) button:focus-visible,
|
|
199
210
|
.ck-input:focus-visible {
|
|
200
211
|
outline: 2px solid var(--ck-accent);
|
|
201
212
|
outline-offset: 2px;
|
|
@@ -275,6 +286,22 @@
|
|
|
275
286
|
.ck-voice-wave > span:nth-child(5n) {
|
|
276
287
|
animation-delay: -0.8s;
|
|
277
288
|
}
|
|
289
|
+
/* the words as they are heard: one line, its end always in view, the start
|
|
290
|
+
fading out — the person reads the newest words, not the oldest */
|
|
291
|
+
.ck-voice-live {
|
|
292
|
+
flex: 1;
|
|
293
|
+
min-width: 0;
|
|
294
|
+
overflow: hidden;
|
|
295
|
+
white-space: nowrap;
|
|
296
|
+
font-size: 0.9375rem;
|
|
297
|
+
line-height: 1.4;
|
|
298
|
+
color: var(--ck-fg);
|
|
299
|
+
-webkit-mask-image: linear-gradient(to right, transparent, #000 1.75rem);
|
|
300
|
+
mask-image: linear-gradient(to right, transparent, #000 1.75rem);
|
|
301
|
+
}
|
|
302
|
+
.ck-voice-live-dim {
|
|
303
|
+
color: var(--ck-fg-3);
|
|
304
|
+
}
|
|
278
305
|
.ck-voice-timer {
|
|
279
306
|
flex-shrink: 0;
|
|
280
307
|
font-size: 0.9375rem;
|
|
@@ -309,6 +336,25 @@
|
|
|
309
336
|
flex: 1;
|
|
310
337
|
min-width: 0;
|
|
311
338
|
}
|
|
339
|
+
.ck-problem-detail {
|
|
340
|
+
color: var(--ck-muted);
|
|
341
|
+
}
|
|
342
|
+
.ck-problem-retry {
|
|
343
|
+
flex-shrink: 0;
|
|
344
|
+
min-height: 2rem;
|
|
345
|
+
padding: 0 0.75rem;
|
|
346
|
+
border: 1px solid var(--ck-line-2);
|
|
347
|
+
border-radius: 999px;
|
|
348
|
+
background: transparent;
|
|
349
|
+
color: var(--ck-fg);
|
|
350
|
+
font: inherit;
|
|
351
|
+
font-size: 0.875rem;
|
|
352
|
+
cursor: pointer;
|
|
353
|
+
}
|
|
354
|
+
.ck-problem-retry:disabled {
|
|
355
|
+
opacity: 0.5;
|
|
356
|
+
cursor: default;
|
|
357
|
+
}
|
|
312
358
|
.ck-problem-x {
|
|
313
359
|
display: inline-flex;
|
|
314
360
|
flex-shrink: 0;
|
|
@@ -431,7 +477,7 @@
|
|
|
431
477
|
max-width: min(85%, 42rem);
|
|
432
478
|
padding: 0.625rem 1rem;
|
|
433
479
|
border-radius: calc(var(--ck-radius) * 1.25);
|
|
434
|
-
background: var(--ck-
|
|
480
|
+
background: var(--ck-bubble);
|
|
435
481
|
color: var(--ck-fg);
|
|
436
482
|
font-size: var(--ck-text);
|
|
437
483
|
line-height: var(--ck-leading);
|
|
@@ -553,6 +599,156 @@
|
|
|
553
599
|
box-shadow: 0 6px 20px -8px rgb(0 0 0 / 0.35);
|
|
554
600
|
}
|
|
555
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
|
+
|
|
556
752
|
/* markdown: 16px, readable measure, no leaked ** or > */
|
|
557
753
|
.ck-md {
|
|
558
754
|
min-width: 0;
|
|
@@ -651,6 +847,38 @@
|
|
|
651
847
|
color: var(--ck-fg);
|
|
652
848
|
}
|
|
653
849
|
|
|
850
|
+
/* suggested replies — under the latest answer, one tap sends */
|
|
851
|
+
.ck-replies {
|
|
852
|
+
display: flex;
|
|
853
|
+
flex-wrap: wrap;
|
|
854
|
+
gap: 0.5rem;
|
|
855
|
+
min-width: 0;
|
|
856
|
+
margin-top: 0.75rem;
|
|
857
|
+
}
|
|
858
|
+
.ck-reply {
|
|
859
|
+
min-width: var(--ck-target);
|
|
860
|
+
min-height: var(--ck-target);
|
|
861
|
+
max-width: 100%;
|
|
862
|
+
padding: 0.5rem 0.875rem;
|
|
863
|
+
border: 1px solid var(--ck-line);
|
|
864
|
+
border-radius: 999px;
|
|
865
|
+
background: var(--ck-bg);
|
|
866
|
+
color: var(--ck-fg);
|
|
867
|
+
font: inherit;
|
|
868
|
+
font-size: 0.9375rem;
|
|
869
|
+
line-height: 1.25;
|
|
870
|
+
text-align: left;
|
|
871
|
+
overflow-wrap: anywhere;
|
|
872
|
+
cursor: pointer;
|
|
873
|
+
}
|
|
874
|
+
.ck-reply:hover {
|
|
875
|
+
border-color: var(--ck-accent);
|
|
876
|
+
}
|
|
877
|
+
.ck-reply:disabled {
|
|
878
|
+
opacity: 0.5;
|
|
879
|
+
cursor: default;
|
|
880
|
+
}
|
|
881
|
+
|
|
654
882
|
.ck-spin {
|
|
655
883
|
animation: ck-spin 0.9s linear infinite;
|
|
656
884
|
}
|
|
@@ -687,3 +915,110 @@
|
|
|
687
915
|
animation: none;
|
|
688
916
|
}
|
|
689
917
|
}
|
|
918
|
+
|
|
919
|
+
/* attach sheet: Camera / Photos / Files, from the bottom edge on a phone —
|
|
920
|
+
where the thumb already is after tapping the paperclip. A native <dialog>
|
|
921
|
+
in the top layer, so no panel around the composer can clip it. */
|
|
922
|
+
.ck-sheet {
|
|
923
|
+
width: 100%;
|
|
924
|
+
max-width: 32rem;
|
|
925
|
+
max-height: none;
|
|
926
|
+
margin: auto auto 0;
|
|
927
|
+
padding: 0;
|
|
928
|
+
border: 0;
|
|
929
|
+
background: transparent;
|
|
930
|
+
color: var(--ck-fg);
|
|
931
|
+
font-family: var(--ck-font);
|
|
932
|
+
}
|
|
933
|
+
.ck-sheet::backdrop {
|
|
934
|
+
background: color-mix(in srgb, #000 40%, transparent);
|
|
935
|
+
}
|
|
936
|
+
.ck-sheet[open] .ck-sheet-panel {
|
|
937
|
+
animation: ck-rise 0.18s ease-out;
|
|
938
|
+
}
|
|
939
|
+
.ck-sheet-panel {
|
|
940
|
+
padding: 0.75rem 1rem calc(1rem + env(safe-area-inset-bottom, 0px));
|
|
941
|
+
border: 1px solid var(--ck-line);
|
|
942
|
+
border-bottom: 0;
|
|
943
|
+
border-radius: var(--ck-radius) var(--ck-radius) 0 0;
|
|
944
|
+
background: var(--ck-bg);
|
|
945
|
+
}
|
|
946
|
+
.ck-sheet-head {
|
|
947
|
+
display: flex;
|
|
948
|
+
align-items: center;
|
|
949
|
+
justify-content: space-between;
|
|
950
|
+
gap: 0.5rem;
|
|
951
|
+
margin-bottom: 0.75rem;
|
|
952
|
+
}
|
|
953
|
+
.ck-sheet-title {
|
|
954
|
+
font-size: 1rem;
|
|
955
|
+
font-weight: 600;
|
|
956
|
+
}
|
|
957
|
+
.ck-sheet-close {
|
|
958
|
+
border-color: transparent;
|
|
959
|
+
background: transparent;
|
|
960
|
+
}
|
|
961
|
+
.ck-attach-sources {
|
|
962
|
+
display: grid;
|
|
963
|
+
grid-auto-flow: column;
|
|
964
|
+
grid-auto-columns: minmax(0, 1fr);
|
|
965
|
+
gap: 0.5rem;
|
|
966
|
+
}
|
|
967
|
+
.ck-attach-source {
|
|
968
|
+
display: flex;
|
|
969
|
+
flex-direction: column;
|
|
970
|
+
align-items: center;
|
|
971
|
+
justify-content: center;
|
|
972
|
+
gap: 0.375rem;
|
|
973
|
+
min-height: 5rem;
|
|
974
|
+
padding: 0.75rem 0.25rem;
|
|
975
|
+
border: 1px solid var(--ck-line);
|
|
976
|
+
border-radius: var(--ck-radius-sm);
|
|
977
|
+
background: var(--ck-raised);
|
|
978
|
+
color: var(--ck-fg);
|
|
979
|
+
font: inherit;
|
|
980
|
+
font-size: 0.9375rem;
|
|
981
|
+
cursor: pointer;
|
|
982
|
+
}
|
|
983
|
+
.ck-attach-source > svg {
|
|
984
|
+
font-size: 1.375rem;
|
|
985
|
+
color: var(--ck-fg-2);
|
|
986
|
+
}
|
|
987
|
+
.ck-attach-source:hover {
|
|
988
|
+
border-color: var(--ck-line-2);
|
|
989
|
+
}
|
|
990
|
+
.ck-attach-source:focus-visible,
|
|
991
|
+
.ck-sheet-close:focus-visible {
|
|
992
|
+
outline: 2px solid var(--ck-accent);
|
|
993
|
+
outline-offset: 2px;
|
|
994
|
+
}
|
|
995
|
+
.ck-attach-trigger {
|
|
996
|
+
display: inline-flex;
|
|
997
|
+
align-items: center;
|
|
998
|
+
justify-content: center;
|
|
999
|
+
gap: 0.375rem;
|
|
1000
|
+
min-height: var(--ck-target);
|
|
1001
|
+
padding: 0 1rem;
|
|
1002
|
+
border: 1px solid var(--ck-line);
|
|
1003
|
+
border-radius: 999px;
|
|
1004
|
+
background: var(--ck-raised);
|
|
1005
|
+
color: var(--ck-fg);
|
|
1006
|
+
font: inherit;
|
|
1007
|
+
font-size: 0.9375rem;
|
|
1008
|
+
cursor: pointer;
|
|
1009
|
+
}
|
|
1010
|
+
.ck-attach-trigger:disabled {
|
|
1011
|
+
cursor: not-allowed;
|
|
1012
|
+
opacity: 0.45;
|
|
1013
|
+
}
|
|
1014
|
+
@keyframes ck-rise {
|
|
1015
|
+
from {
|
|
1016
|
+
transform: translateY(1.5rem);
|
|
1017
|
+
opacity: 0;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1021
|
+
.ck-sheet[open] .ck-sheet-panel {
|
|
1022
|
+
animation: none;
|
|
1023
|
+
}
|
|
1024
|
+
}
|