@fadenstack/ui-web 0.2.0-rc.2 → 0.3.0-rc.1
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/dom.d.ts +22 -2
- package/dist/dom.d.ts.map +1 -1
- package/dist/dom.js +44 -2
- package/dist/dom.js.map +1 -1
- package/dist/icons.d.ts +5 -0
- package/dist/icons.d.ts.map +1 -1
- package/dist/icons.js +5 -0
- package/dist/icons.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/dom.ts +50 -2
- package/src/icons.ts +5 -0
- package/src/index.ts +12 -1
- package/styles.css +213 -19
package/dist/dom.d.ts
CHANGED
|
@@ -13,11 +13,31 @@ export declare function dismissOnOutside(anchor: HTMLElement, close: () => void)
|
|
|
13
13
|
export declare function fitToContent(box: HTMLTextAreaElement): void;
|
|
14
14
|
/** Focuses a text box with the caret after its text (after "Edit", or the host attached something). */
|
|
15
15
|
export declare function focusAtEnd(box: HTMLTextAreaElement): void;
|
|
16
|
+
/** What a key in the composer does (`composerKey`). */
|
|
17
|
+
export type ComposerAction = "send" | "stop" | "pick" | "next" | "previous" | "close";
|
|
16
18
|
/**
|
|
17
19
|
* What a key in the composer does: Enter sends (Shift+Enter is a new line, and an input method that is still
|
|
18
|
-
* composing keeps its Enter), Escape stops the running answer.
|
|
20
|
+
* composing keeps its Enter), Escape stops the running answer. While the skill picker is open (`pickerOpen`), the
|
|
21
|
+
* arrows move in it, Enter and Tab pick, and Escape closes it.
|
|
19
22
|
*/
|
|
20
|
-
export declare function composerKey(e: Pick<KeyboardEvent, "key" | "shiftKey" | "isComposing">, busy: boolean):
|
|
23
|
+
export declare function composerKey(e: Pick<KeyboardEvent, "key" | "shiftKey" | "isComposing">, busy: boolean, pickerOpen?: boolean): ComposerAction | undefined;
|
|
24
|
+
/** The pictures among pasted or dropped files. */
|
|
25
|
+
export declare function picturesIn(transfer: DataTransfer | null | undefined): File[];
|
|
26
|
+
/** A picture made ready for a message: a `data:` URL, and its size in pixels. */
|
|
27
|
+
export interface ReadPicture {
|
|
28
|
+
dataUrl: string;
|
|
29
|
+
width: number;
|
|
30
|
+
height: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A picture as a message takes it: at most `maxSide` pixels on its longer side (models scale larger ones down
|
|
34
|
+
* anyway, and the transcript keeps it), as JPEG on white (transparent parts of a screenshot stay readable).
|
|
35
|
+
* Throws when the browser cannot read it as a picture.
|
|
36
|
+
*/
|
|
37
|
+
export declare function readPicture(picture: Blob, options?: {
|
|
38
|
+
maxSide?: number;
|
|
39
|
+
quality?: number;
|
|
40
|
+
}): Promise<ReadPicture>;
|
|
21
41
|
/** How long a copy button shows its tick. */
|
|
22
42
|
export declare const COPIED_MS = 1500;
|
|
23
43
|
//# sourceMappingURL=dom.d.ts.map
|
package/dist/dom.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../src/dom.ts"],"names":[],"mappings":"AAAA,mGAAmG;AAEnG;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,WAAW,EAAE,SAAS,SAAK,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IAAC,IAAI,IAAI,IAAI,CAAA;CAAE,CAoBzG;AAED,wGAAwG;AACxG,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAanF;AAED,qFAAqF;AACrF,wBAAgB,YAAY,CAAC,GAAG,EAAE,mBAAmB,GAAG,IAAI,CAI3D;AAED,uGAAuG;AACvG,wBAAgB,UAAU,CAAC,GAAG,EAAE,mBAAmB,GAAG,IAAI,CAIzD;AAED
|
|
1
|
+
{"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../src/dom.ts"],"names":[],"mappings":"AAAA,mGAAmG;AAEnG;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,WAAW,EAAE,SAAS,SAAK,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IAAC,IAAI,IAAI,IAAI,CAAA;CAAE,CAoBzG;AAED,wGAAwG;AACxG,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAanF;AAED,qFAAqF;AACrF,wBAAgB,YAAY,CAAC,GAAG,EAAE,mBAAmB,GAAG,IAAI,CAI3D;AAED,uGAAuG;AACvG,wBAAgB,UAAU,CAAC,GAAG,EAAE,mBAAmB,GAAG,IAAI,CAIzD;AAED,uDAAuD;AACvD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC;AAEtF;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,KAAK,GAAG,UAAU,GAAG,aAAa,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,UAAQ,GAAG,cAAc,GAAG,SAAS,CAUrJ;AAED,kDAAkD;AAClD,wBAAgB,UAAU,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,EAAE,CAE5E;AAED,iFAAiF;AACjF,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAmB3H;AAED,6CAA6C;AAC7C,eAAO,MAAM,SAAS,OAAO,CAAC"}
|
package/dist/dom.js
CHANGED
|
@@ -57,15 +57,57 @@ export function focusAtEnd(box) {
|
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
59
|
* What a key in the composer does: Enter sends (Shift+Enter is a new line, and an input method that is still
|
|
60
|
-
* composing keeps its Enter), Escape stops the running answer.
|
|
60
|
+
* composing keeps its Enter), Escape stops the running answer. While the skill picker is open (`pickerOpen`), the
|
|
61
|
+
* arrows move in it, Enter and Tab pick, and Escape closes it.
|
|
61
62
|
*/
|
|
62
|
-
export function composerKey(e, busy) {
|
|
63
|
+
export function composerKey(e, busy, pickerOpen = false) {
|
|
64
|
+
if (pickerOpen && !e.isComposing) {
|
|
65
|
+
if (e.key === "ArrowDown")
|
|
66
|
+
return "next";
|
|
67
|
+
if (e.key === "ArrowUp")
|
|
68
|
+
return "previous";
|
|
69
|
+
if ((e.key === "Enter" && !e.shiftKey) || e.key === "Tab")
|
|
70
|
+
return "pick";
|
|
71
|
+
if (e.key === "Escape")
|
|
72
|
+
return "close";
|
|
73
|
+
}
|
|
63
74
|
if (e.key === "Enter" && !e.shiftKey && !e.isComposing)
|
|
64
75
|
return "send";
|
|
65
76
|
if (e.key === "Escape" && busy)
|
|
66
77
|
return "stop";
|
|
67
78
|
return undefined;
|
|
68
79
|
}
|
|
80
|
+
/** The pictures among pasted or dropped files. */
|
|
81
|
+
export function picturesIn(transfer) {
|
|
82
|
+
return Array.from(transfer?.files ?? []).filter((f) => f.type.startsWith("image/"));
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* A picture as a message takes it: at most `maxSide` pixels on its longer side (models scale larger ones down
|
|
86
|
+
* anyway, and the transcript keeps it), as JPEG on white (transparent parts of a screenshot stay readable).
|
|
87
|
+
* Throws when the browser cannot read it as a picture.
|
|
88
|
+
*/
|
|
89
|
+
export async function readPicture(picture, options = {}) {
|
|
90
|
+
const maxSide = options.maxSide ?? 1600;
|
|
91
|
+
const bitmap = await createImageBitmap(picture);
|
|
92
|
+
try {
|
|
93
|
+
const scale = Math.min(1, maxSide / Math.max(bitmap.width, bitmap.height, 1));
|
|
94
|
+
const width = Math.max(1, Math.round(bitmap.width * scale));
|
|
95
|
+
const height = Math.max(1, Math.round(bitmap.height * scale));
|
|
96
|
+
const canvas = document.createElement("canvas");
|
|
97
|
+
canvas.width = width;
|
|
98
|
+
canvas.height = height;
|
|
99
|
+
const context = canvas.getContext("2d");
|
|
100
|
+
if (!context)
|
|
101
|
+
throw new Error("No canvas to draw the picture on.");
|
|
102
|
+
context.fillStyle = "#fff";
|
|
103
|
+
context.fillRect(0, 0, width, height);
|
|
104
|
+
context.drawImage(bitmap, 0, 0, width, height);
|
|
105
|
+
return { dataUrl: canvas.toDataURL("image/jpeg", options.quality ?? 0.85), width, height };
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
bitmap.close();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
69
111
|
/** How long a copy button shows its tick. */
|
|
70
112
|
export const COPIED_MS = 1500;
|
|
71
113
|
//# sourceMappingURL=dom.js.map
|
package/dist/dom.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dom.js","sourceRoot":"","sources":["../src/dom.ts"],"names":[],"mappings":"AAAA,mGAAmG;AAEnG;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,GAAgB,EAAE,SAAS,GAAG,EAAE;IAC5D,IAAI,KAAK,GAAG,IAAI,CAAC;IACjB,MAAM,QAAQ,GAAG,GAAG,EAAE;QACpB,KAAK,GAAG,GAAG,CAAC,YAAY,GAAG,GAAG,CAAC,SAAS,GAAG,GAAG,CAAC,YAAY,GAAG,SAAS,CAAC;IAC1E,CAAC,CAAC;IACF,GAAG,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,MAAM,QAAQ,GAAG,OAAO,cAAc,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,cAAc,CAAC,GAAG,EAAE;QAC3F,IAAI,KAAK;YAAE,GAAG,CAAC,SAAS,GAAG,GAAG,CAAC,YAAY,CAAC;IAC9C,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,GAAG,CAAC,iBAAiB,CAAC;IACtC,IAAI,QAAQ,IAAI,OAAO;QAAE,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACnD,OAAO;QACL,IAAI,KAAK;YACP,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI;YACF,GAAG,CAAC,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC5C,QAAQ,EAAE,UAAU,EAAE,CAAC;QACzB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,gBAAgB,CAAC,MAAmB,EAAE,KAAiB;IACrE,MAAM,SAAS,GAAG,CAAC,CAAe,EAAE,EAAE;QACpC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAc,CAAC;YAAE,KAAK,EAAE,CAAC;IAClD,CAAC,CAAC;IACF,MAAM,KAAK,GAAG,CAAC,CAAgB,EAAE,EAAE;QACjC,IAAI,CAAC,CAAC,GAAG,KAAK,QAAQ;YAAE,KAAK,EAAE,CAAC;IAClC,CAAC,CAAC;IACF,MAAM,CAAC,aAAa,CAAC,gBAAgB,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;IAChE,MAAM,CAAC,aAAa,CAAC,gBAAgB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACxD,OAAO,GAAG,EAAE;QACV,MAAM,CAAC,aAAa,CAAC,mBAAmB,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;QACnE,MAAM,CAAC,aAAa,CAAC,mBAAmB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC7D,CAAC,CAAC;AACJ,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,YAAY,CAAC,GAAwB;IACnD,MAAM,IAAI,GAAG,GAAG,CAAC,aAAa,CAAC,WAAW,CAAC;IAC3C,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;IAC1B,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,WAAW,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AACtG,CAAC;AAED,uGAAuG;AACvG,MAAM,UAAU,UAAU,CAAC,GAAwB;IACjD,GAAG,CAAC,KAAK,EAAE,CAAC;IACZ,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC;IAC7B,GAAG,CAAC,iBAAiB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAClC,CAAC;
|
|
1
|
+
{"version":3,"file":"dom.js","sourceRoot":"","sources":["../src/dom.ts"],"names":[],"mappings":"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"}
|
package/dist/icons.d.ts
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export declare const iconPaths: {
|
|
8
8
|
readonly send: "M3.4 20.4 21 12 3.4 3.6l-.01 6.53L15 12 3.39 13.87z";
|
|
9
|
+
readonly image: "M21 19V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5z";
|
|
10
|
+
readonly camera: "M12 15.2a3.2 3.2 0 1 0 0-6.4 3.2 3.2 0 0 0 0 6.4zM9 2 7.17 4H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2h-3.17L15 2z";
|
|
11
|
+
readonly translate: "m12.87 15.07-2.54-2.51.03-.03A17.52 17.52 0 0 0 14.07 6H17V4h-7V2H8v2H1v2h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2zm-2.62 7 1.62-4.33L19.12 17z";
|
|
9
12
|
readonly stop: "M7 7h10v10H7z";
|
|
10
13
|
readonly plus: "M11 5h2v6h6v2h-6v6h-2v-6H5v-2h6z";
|
|
11
14
|
readonly history: "M13 3a9 9 0 0 0-9 9H1l3.9 3.9.1.1L9 12H6a7 7 0 1 1 2.05 4.95l-1.42 1.42A9 9 0 1 0 13 3zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8z";
|
|
@@ -17,6 +20,8 @@ export declare const iconPaths: {
|
|
|
17
20
|
readonly warning: "M1 21h22L12 2zm12-3h-2v-2h2zm0-4h-2v-4h2z";
|
|
18
21
|
readonly shield: "M12 1 3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5zm0 10.99h7c-.53 4.12-3.28 7.79-7 8.94V12H5V6.3l7-3.11z";
|
|
19
22
|
readonly info: "M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z";
|
|
23
|
+
/** After a link that opens in a new tab. */
|
|
24
|
+
readonly external: "M9 5v2h6.59L4 18.59 5.41 20 17 8.41V15h2V5z";
|
|
20
25
|
readonly edit: "M3 17.25V21h3.75L17.81 9.94l-3.75-3.75zM20.71 7.04a1 1 0 0 0 0-1.41l-2.34-2.34a1 1 0 0 0-1.41 0l-1.83 1.83 3.75 3.75z";
|
|
21
26
|
readonly retry: "M17.65 6.35A7.96 7.96 0 0 0 12 4a8 8 0 1 0 7.73 10h-2.08A6 6 0 1 1 12 6c1.66 0 3.14.69 4.22 1.78L13 11h7V4z";
|
|
22
27
|
readonly trash: "M6 19a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V7H6zM19 4h-3.5l-1-1h-5l-1 1H5v2h14z";
|
package/dist/icons.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../src/icons.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,SAAS
|
|
1
|
+
{"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../src/icons.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;IAgBpB,4CAA4C;;;;;;;;;;;;;CAapC,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,MAAM,OAAO,SAAS,CAAC"}
|
package/dist/icons.js
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export const iconPaths = {
|
|
8
8
|
send: "M3.4 20.4 21 12 3.4 3.6l-.01 6.53L15 12 3.39 13.87z",
|
|
9
|
+
image: "M21 19V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5z",
|
|
10
|
+
camera: "M12 15.2a3.2 3.2 0 1 0 0-6.4 3.2 3.2 0 0 0 0 6.4zM9 2 7.17 4H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2h-3.17L15 2z",
|
|
11
|
+
translate: "m12.87 15.07-2.54-2.51.03-.03A17.52 17.52 0 0 0 14.07 6H17V4h-7V2H8v2H1v2h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2zm-2.62 7 1.62-4.33L19.12 17z",
|
|
9
12
|
stop: "M7 7h10v10H7z",
|
|
10
13
|
plus: "M11 5h2v6h6v2h-6v6h-2v-6H5v-2h6z",
|
|
11
14
|
history: "M13 3a9 9 0 0 0-9 9H1l3.9 3.9.1.1L9 12H6a7 7 0 1 1 2.05 4.95l-1.42 1.42A9 9 0 1 0 13 3zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8z",
|
|
@@ -17,6 +20,8 @@ export const iconPaths = {
|
|
|
17
20
|
warning: "M1 21h22L12 2zm12-3h-2v-2h2zm0-4h-2v-4h2z",
|
|
18
21
|
shield: "M12 1 3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5zm0 10.99h7c-.53 4.12-3.28 7.79-7 8.94V12H5V6.3l7-3.11z",
|
|
19
22
|
info: "M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z",
|
|
23
|
+
/** After a link that opens in a new tab. */
|
|
24
|
+
external: "M9 5v2h6.59L4 18.59 5.41 20 17 8.41V15h2V5z",
|
|
20
25
|
edit: "M3 17.25V21h3.75L17.81 9.94l-3.75-3.75zM20.71 7.04a1 1 0 0 0 0-1.41l-2.34-2.34a1 1 0 0 0-1.41 0l-1.83 1.83 3.75 3.75z",
|
|
21
26
|
retry: "M17.65 6.35A7.96 7.96 0 0 0 12 4a8 8 0 1 0 7.73 10h-2.08A6 6 0 1 1 12 6c1.66 0 3.14.69 4.22 1.78L13 11h7V4z",
|
|
22
27
|
trash: "M6 19a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V7H6zM19 4h-3.5l-1-1h-5l-1 1H5v2h14z",
|
package/dist/icons.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"icons.js","sourceRoot":"","sources":["../src/icons.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,IAAI,EAAE,qDAAqD;IAC3D,IAAI,EAAE,eAAe;IACrB,IAAI,EAAE,kCAAkC;IACxC,OAAO,EAAE,8HAA8H;IACvI,OAAO,EAAE,0CAA0C;IACnD,IAAI,EAAE,sHAAsH;IAC5H,KAAK,EAAE,6CAA6C;IACpD,KAAK,EAAE,2FAA2F;IAClG,IAAI,EAAE,0KAA0K;IAChL,OAAO,EAAE,2CAA2C;IACpD,MAAM,EAAE,mHAAmH;IAC3H,IAAI,EAAE,sJAAsJ;IAC5J,IAAI,EAAE,uHAAuH;IAC7H,KAAK,EAAE,6GAA6G;IACpH,KAAK,EAAE,yEAAyE;IAChF,OAAO,EAAE,2DAA2D;IACpE,IAAI,EAAE,4EAA4E;IAClF,KAAK,EAAE,mRAAmR;IAC1R,KAAK,EAAE,0CAA0C;IACjD,IAAI,EAAE,iGAAiG;IACvG,MAAM,EAAE,yIAAyI;IACjJ,IAAI,EAAE,2FAA2F;IACjG,QAAQ,EAAE,+nBAA+nB;CACjoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"icons.js","sourceRoot":"","sources":["../src/icons.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,IAAI,EAAE,qDAAqD;IAC3D,KAAK,EAAE,8GAA8G;IACrH,MAAM,EAAE,4IAA4I;IACpJ,SAAS,EAAE,4QAA4Q;IACvR,IAAI,EAAE,eAAe;IACrB,IAAI,EAAE,kCAAkC;IACxC,OAAO,EAAE,8HAA8H;IACvI,OAAO,EAAE,0CAA0C;IACnD,IAAI,EAAE,sHAAsH;IAC5H,KAAK,EAAE,6CAA6C;IACpD,KAAK,EAAE,2FAA2F;IAClG,IAAI,EAAE,0KAA0K;IAChL,OAAO,EAAE,2CAA2C;IACpD,MAAM,EAAE,mHAAmH;IAC3H,IAAI,EAAE,sJAAsJ;IAC5J,4CAA4C;IAC5C,QAAQ,EAAE,6CAA6C;IACvD,IAAI,EAAE,uHAAuH;IAC7H,KAAK,EAAE,6GAA6G;IACpH,KAAK,EAAE,yEAAyE;IAChF,OAAO,EAAE,2DAA2D;IACpE,IAAI,EAAE,4EAA4E;IAClF,KAAK,EAAE,mRAAmR;IAC1R,KAAK,EAAE,0CAA0C;IACjD,IAAI,EAAE,iGAAiG;IACvG,MAAM,EAAE,yIAAyI;IACjJ,IAAI,EAAE,2FAA2F;IACjG,QAAQ,EAAE,+nBAA+nB;CACjoB,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { iconPaths, type IconName } from "./icons.js";
|
|
2
2
|
export { agentAvatar, type AgentAvatarOptions, type SvgNode } from "./avatar.js";
|
|
3
|
-
export { COPIED_MS, composerKey, dismissOnOutside, fitToContent, focusAtEnd, stickToBottom } from "./dom.js";
|
|
3
|
+
export { COPIED_MS, composerKey, dismissOnOutside, fitToContent, focusAtEnd, picturesIn, readPicture, stickToBottom, type ComposerAction, type ReadPicture, } from "./dom.js";
|
|
4
4
|
export { approvalIcon, diffMarker, headingTag, noticeIcon, noticeRole, toolBadges, toolClassName, toolStatusIcon, turnMeta, type Badge, } from "./presentation.js";
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,EACL,SAAS,EACT,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,UAAU,EACV,UAAU,EACV,WAAW,EACX,aAAa,EACb,KAAK,cAAc,EACnB,KAAK,WAAW,GACjB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,YAAY,EACZ,UAAU,EACV,UAAU,EACV,UAAU,EACV,UAAU,EACV,UAAU,EACV,aAAa,EACb,cAAc,EACd,QAAQ,EACR,KAAK,KAAK,GACX,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { iconPaths } from "./icons.js";
|
|
2
2
|
export { agentAvatar } from "./avatar.js";
|
|
3
|
-
export { COPIED_MS, composerKey, dismissOnOutside, fitToContent, focusAtEnd, stickToBottom } from "./dom.js";
|
|
3
|
+
export { COPIED_MS, composerKey, dismissOnOutside, fitToContent, focusAtEnd, picturesIn, readPicture, stickToBottom, } from "./dom.js";
|
|
4
4
|
export { approvalIcon, diffMarker, headingTag, noticeIcon, noticeRole, toolBadges, toolClassName, toolStatusIcon, turnMeta, } from "./presentation.js";
|
|
5
5
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAiB,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,WAAW,EAAyC,MAAM,aAAa,CAAC;AACjF,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAiB,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,WAAW,EAAyC,MAAM,aAAa,CAAC;AACjF,OAAO,EACL,SAAS,EACT,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,UAAU,EACV,UAAU,EACV,WAAW,EACX,aAAa,GAGd,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,YAAY,EACZ,UAAU,EACV,UAAU,EACV,UAAU,EACV,UAAU,EACV,UAAU,EACV,aAAa,EACb,cAAc,EACd,QAAQ,GAET,MAAM,mBAAmB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fadenstack/ui-web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0-rc.1",
|
|
4
4
|
"description": "The web look of the Fadenstack chat for any framework: the stylesheet, icons, the agent's avatar and the panel's browser behaviours",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Emagin8 UG (haftungsbeschränkt)",
|
|
@@ -41,6 +41,6 @@
|
|
|
41
41
|
"access": "public"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@fadenstack/ui": "0.
|
|
44
|
+
"@fadenstack/ui": "0.3.0-rc.1"
|
|
45
45
|
}
|
|
46
46
|
}
|
package/src/dom.ts
CHANGED
|
@@ -56,15 +56,63 @@ export function focusAtEnd(box: HTMLTextAreaElement): void {
|
|
|
56
56
|
box.setSelectionRange(end, end);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/** What a key in the composer does (`composerKey`). */
|
|
60
|
+
export type ComposerAction = "send" | "stop" | "pick" | "next" | "previous" | "close";
|
|
61
|
+
|
|
59
62
|
/**
|
|
60
63
|
* What a key in the composer does: Enter sends (Shift+Enter is a new line, and an input method that is still
|
|
61
|
-
* composing keeps its Enter), Escape stops the running answer.
|
|
64
|
+
* composing keeps its Enter), Escape stops the running answer. While the skill picker is open (`pickerOpen`), the
|
|
65
|
+
* arrows move in it, Enter and Tab pick, and Escape closes it.
|
|
62
66
|
*/
|
|
63
|
-
export function composerKey(e: Pick<KeyboardEvent, "key" | "shiftKey" | "isComposing">, busy: boolean):
|
|
67
|
+
export function composerKey(e: Pick<KeyboardEvent, "key" | "shiftKey" | "isComposing">, busy: boolean, pickerOpen = false): ComposerAction | undefined {
|
|
68
|
+
if (pickerOpen && !e.isComposing) {
|
|
69
|
+
if (e.key === "ArrowDown") return "next";
|
|
70
|
+
if (e.key === "ArrowUp") return "previous";
|
|
71
|
+
if ((e.key === "Enter" && !e.shiftKey) || e.key === "Tab") return "pick";
|
|
72
|
+
if (e.key === "Escape") return "close";
|
|
73
|
+
}
|
|
64
74
|
if (e.key === "Enter" && !e.shiftKey && !e.isComposing) return "send";
|
|
65
75
|
if (e.key === "Escape" && busy) return "stop";
|
|
66
76
|
return undefined;
|
|
67
77
|
}
|
|
68
78
|
|
|
79
|
+
/** The pictures among pasted or dropped files. */
|
|
80
|
+
export function picturesIn(transfer: DataTransfer | null | undefined): File[] {
|
|
81
|
+
return Array.from(transfer?.files ?? []).filter((f) => f.type.startsWith("image/"));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** A picture made ready for a message: a `data:` URL, and its size in pixels. */
|
|
85
|
+
export interface ReadPicture {
|
|
86
|
+
dataUrl: string;
|
|
87
|
+
width: number;
|
|
88
|
+
height: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* A picture as a message takes it: at most `maxSide` pixels on its longer side (models scale larger ones down
|
|
93
|
+
* anyway, and the transcript keeps it), as JPEG on white (transparent parts of a screenshot stay readable).
|
|
94
|
+
* Throws when the browser cannot read it as a picture.
|
|
95
|
+
*/
|
|
96
|
+
export async function readPicture(picture: Blob, options: { maxSide?: number; quality?: number } = {}): Promise<ReadPicture> {
|
|
97
|
+
const maxSide = options.maxSide ?? 1600;
|
|
98
|
+
const bitmap = await createImageBitmap(picture);
|
|
99
|
+
try {
|
|
100
|
+
const scale = Math.min(1, maxSide / Math.max(bitmap.width, bitmap.height, 1));
|
|
101
|
+
const width = Math.max(1, Math.round(bitmap.width * scale));
|
|
102
|
+
const height = Math.max(1, Math.round(bitmap.height * scale));
|
|
103
|
+
const canvas = document.createElement("canvas");
|
|
104
|
+
canvas.width = width;
|
|
105
|
+
canvas.height = height;
|
|
106
|
+
const context = canvas.getContext("2d");
|
|
107
|
+
if (!context) throw new Error("No canvas to draw the picture on.");
|
|
108
|
+
context.fillStyle = "#fff";
|
|
109
|
+
context.fillRect(0, 0, width, height);
|
|
110
|
+
context.drawImage(bitmap, 0, 0, width, height);
|
|
111
|
+
return { dataUrl: canvas.toDataURL("image/jpeg", options.quality ?? 0.85), width, height };
|
|
112
|
+
} finally {
|
|
113
|
+
bitmap.close();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
69
117
|
/** How long a copy button shows its tick. */
|
|
70
118
|
export const COPIED_MS = 1500;
|
package/src/icons.ts
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export const iconPaths = {
|
|
8
8
|
send: "M3.4 20.4 21 12 3.4 3.6l-.01 6.53L15 12 3.39 13.87z",
|
|
9
|
+
image: "M21 19V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5z",
|
|
10
|
+
camera: "M12 15.2a3.2 3.2 0 1 0 0-6.4 3.2 3.2 0 0 0 0 6.4zM9 2 7.17 4H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2h-3.17L15 2z",
|
|
11
|
+
translate: "m12.87 15.07-2.54-2.51.03-.03A17.52 17.52 0 0 0 14.07 6H17V4h-7V2H8v2H1v2h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2zm-2.62 7 1.62-4.33L19.12 17z",
|
|
9
12
|
stop: "M7 7h10v10H7z",
|
|
10
13
|
plus: "M11 5h2v6h6v2h-6v6h-2v-6H5v-2h6z",
|
|
11
14
|
history: "M13 3a9 9 0 0 0-9 9H1l3.9 3.9.1.1L9 12H6a7 7 0 1 1 2.05 4.95l-1.42 1.42A9 9 0 1 0 13 3zm-1 5v5l4.28 2.54.72-1.21-3.5-2.08V8z",
|
|
@@ -17,6 +20,8 @@ export const iconPaths = {
|
|
|
17
20
|
warning: "M1 21h22L12 2zm12-3h-2v-2h2zm0-4h-2v-4h2z",
|
|
18
21
|
shield: "M12 1 3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5zm0 10.99h7c-.53 4.12-3.28 7.79-7 8.94V12H5V6.3l7-3.11z",
|
|
19
22
|
info: "M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z",
|
|
23
|
+
/** After a link that opens in a new tab. */
|
|
24
|
+
external: "M9 5v2h6.59L4 18.59 5.41 20 17 8.41V15h2V5z",
|
|
20
25
|
edit: "M3 17.25V21h3.75L17.81 9.94l-3.75-3.75zM20.71 7.04a1 1 0 0 0 0-1.41l-2.34-2.34a1 1 0 0 0-1.41 0l-1.83 1.83 3.75 3.75z",
|
|
21
26
|
retry: "M17.65 6.35A7.96 7.96 0 0 0 12 4a8 8 0 1 0 7.73 10h-2.08A6 6 0 1 1 12 6c1.66 0 3.14.69 4.22 1.78L13 11h7V4z",
|
|
22
27
|
trash: "M6 19a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V7H6zM19 4h-3.5l-1-1h-5l-1 1H5v2h14z",
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
export { iconPaths, type IconName } from "./icons.js";
|
|
2
2
|
export { agentAvatar, type AgentAvatarOptions, type SvgNode } from "./avatar.js";
|
|
3
|
-
export {
|
|
3
|
+
export {
|
|
4
|
+
COPIED_MS,
|
|
5
|
+
composerKey,
|
|
6
|
+
dismissOnOutside,
|
|
7
|
+
fitToContent,
|
|
8
|
+
focusAtEnd,
|
|
9
|
+
picturesIn,
|
|
10
|
+
readPicture,
|
|
11
|
+
stickToBottom,
|
|
12
|
+
type ComposerAction,
|
|
13
|
+
type ReadPicture,
|
|
14
|
+
} from "./dom.js";
|
|
4
15
|
export {
|
|
5
16
|
approvalIcon,
|
|
6
17
|
diffMarker,
|
package/styles.css
CHANGED
|
@@ -22,7 +22,9 @@
|
|
|
22
22
|
--fdn-surface-hover: rgba(31, 31, 31, 0.06);
|
|
23
23
|
--fdn-text: #1f1f1f;
|
|
24
24
|
--fdn-text-muted: #474747;
|
|
25
|
-
|
|
25
|
+
/* Quiet text on the panel's background: 5.2:1 on white, 4.7:1 on the surface (a chip's subtitle). Text in a menu,
|
|
26
|
+
an approval card, a notice or a code block is muted, which keeps 4.5:1 on their hover and soft colours too. */
|
|
27
|
+
--fdn-text-faint: #6b6d6b;
|
|
26
28
|
--fdn-border: #c7c7c7;
|
|
27
29
|
--fdn-hairline: #e3e3e3;
|
|
28
30
|
--fdn-accent: #0b57d0;
|
|
@@ -221,6 +223,13 @@
|
|
|
221
223
|
color: var(--fdn-text);
|
|
222
224
|
}
|
|
223
225
|
|
|
226
|
+
/* A button whose menu is open looks pressed; the gear also while About, which its menu opened, is open. */
|
|
227
|
+
.fdn-icon-button[aria-expanded="true"],
|
|
228
|
+
.fdn-icon-button.fdn-open {
|
|
229
|
+
background: var(--fdn-surface-hover);
|
|
230
|
+
color: var(--fdn-text);
|
|
231
|
+
}
|
|
232
|
+
|
|
224
233
|
.fdn-icon-button:disabled {
|
|
225
234
|
opacity: 0.38;
|
|
226
235
|
cursor: default;
|
|
@@ -299,7 +308,7 @@
|
|
|
299
308
|
.fdn-menu-description,
|
|
300
309
|
.fdn-menu-note {
|
|
301
310
|
display: block;
|
|
302
|
-
color: var(--fdn-text-
|
|
311
|
+
color: var(--fdn-text-muted);
|
|
303
312
|
font-size: 12px;
|
|
304
313
|
}
|
|
305
314
|
|
|
@@ -307,14 +316,19 @@
|
|
|
307
316
|
padding: 6px 10px;
|
|
308
317
|
}
|
|
309
318
|
|
|
310
|
-
/* The settings: a heading over each group (the languages), the host's own items below a line.
|
|
319
|
+
/* The settings: a heading over each group (the languages), the host's own items below a line. A heading on a menu
|
|
320
|
+
(and About's title) is muted, as all text on a surface. */
|
|
321
|
+
.fdn-menu-heading,
|
|
322
|
+
.fdn-about-title {
|
|
323
|
+
color: var(--fdn-text-muted);
|
|
324
|
+
font-size: 12px;
|
|
325
|
+
font-weight: 500;
|
|
326
|
+
}
|
|
327
|
+
|
|
311
328
|
.fdn-menu-heading {
|
|
312
329
|
display: block;
|
|
313
330
|
padding-block: 6px 2px;
|
|
314
331
|
padding-inline: 10px;
|
|
315
|
-
color: var(--fdn-text-faint);
|
|
316
|
-
font-size: 12px;
|
|
317
|
-
font-weight: 500;
|
|
318
332
|
}
|
|
319
333
|
|
|
320
334
|
.fdn-menu-extra {
|
|
@@ -328,6 +342,16 @@
|
|
|
328
342
|
overflow-y: auto;
|
|
329
343
|
}
|
|
330
344
|
|
|
345
|
+
/* The settings name languages in their own scripts; the fonts for Chinese have no weight 500 and fall back to 400, so
|
|
346
|
+
every item here is 400 (as the host's own items are), and the picked one 600, which those fonts show as bold. */
|
|
347
|
+
.fdn-settings .fdn-menu-label {
|
|
348
|
+
font-weight: 400;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.fdn-settings [aria-checked="true"] .fdn-menu-label {
|
|
352
|
+
font-weight: 600;
|
|
353
|
+
}
|
|
354
|
+
|
|
331
355
|
.fdn-history {
|
|
332
356
|
max-height: min(420px, 70vh);
|
|
333
357
|
overflow-y: auto;
|
|
@@ -366,7 +390,7 @@
|
|
|
366
390
|
}
|
|
367
391
|
|
|
368
392
|
.fdn-history-when {
|
|
369
|
-
color: var(--fdn-text-
|
|
393
|
+
color: var(--fdn-text-muted);
|
|
370
394
|
font-size: 12px;
|
|
371
395
|
white-space: nowrap;
|
|
372
396
|
}
|
|
@@ -477,8 +501,8 @@
|
|
|
477
501
|
align-items: center;
|
|
478
502
|
gap: 2px;
|
|
479
503
|
margin-inline-start: -4px;
|
|
504
|
+
color: var(--fdn-text-muted); /* quiet icons; not by opacity, which took the meta's text under 4.5:1 */
|
|
480
505
|
font-size: 12px;
|
|
481
|
-
opacity: 0.85;
|
|
482
506
|
}
|
|
483
507
|
|
|
484
508
|
.fdn-turn-actions .fdn-muted {
|
|
@@ -624,7 +648,7 @@
|
|
|
624
648
|
min-height: 30px;
|
|
625
649
|
padding-block: 2px 0;
|
|
626
650
|
padding-inline: 12px 4px;
|
|
627
|
-
color: var(--fdn-text-
|
|
651
|
+
color: var(--fdn-text-muted);
|
|
628
652
|
font-size: 12px;
|
|
629
653
|
}
|
|
630
654
|
|
|
@@ -786,7 +810,7 @@
|
|
|
786
810
|
}
|
|
787
811
|
|
|
788
812
|
.fdn-label {
|
|
789
|
-
color: var(--fdn-text-
|
|
813
|
+
color: var(--fdn-text-muted);
|
|
790
814
|
font-size: 11.5px;
|
|
791
815
|
font-weight: 500;
|
|
792
816
|
}
|
|
@@ -845,6 +869,12 @@
|
|
|
845
869
|
color: var(--fdn-text-muted);
|
|
846
870
|
}
|
|
847
871
|
|
|
872
|
+
.fdn-approval-target {
|
|
873
|
+
margin: 8px 0 0;
|
|
874
|
+
font-weight: 600;
|
|
875
|
+
overflow-wrap: anywhere;
|
|
876
|
+
}
|
|
877
|
+
|
|
848
878
|
.fdn-approval-args {
|
|
849
879
|
margin-top: 8px;
|
|
850
880
|
padding: 8px 10px;
|
|
@@ -852,16 +882,26 @@
|
|
|
852
882
|
background: var(--fdn-bg);
|
|
853
883
|
}
|
|
854
884
|
|
|
855
|
-
|
|
885
|
+
/* The answers in two pairs (Allow, Allow for this chat | Allow always, Deny): one row where they fit, else a pair a
|
|
886
|
+
row, never three and one. Narrower padding than other buttons, so all four fit in a 420 px task pane. */
|
|
887
|
+
.fdn-approval-actions,
|
|
888
|
+
.fdn-approval-pair {
|
|
856
889
|
display: flex;
|
|
857
890
|
flex-wrap: wrap;
|
|
858
891
|
gap: 6px;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
.fdn-approval-actions {
|
|
859
895
|
margin-top: 10px;
|
|
860
896
|
}
|
|
861
897
|
|
|
898
|
+
.fdn-chat .fdn-approval-actions .fdn-button {
|
|
899
|
+
padding-inline: 10px;
|
|
900
|
+
}
|
|
901
|
+
|
|
862
902
|
.fdn-approval-state {
|
|
863
903
|
margin-top: 8px;
|
|
864
|
-
color: var(--fdn-text-
|
|
904
|
+
color: var(--fdn-text-muted);
|
|
865
905
|
font-size: 12px;
|
|
866
906
|
}
|
|
867
907
|
|
|
@@ -870,7 +910,9 @@
|
|
|
870
910
|
color: var(--fdn-success);
|
|
871
911
|
}
|
|
872
912
|
|
|
873
|
-
.fdn-
|
|
913
|
+
/* Text buttons (approval cards, About). Under `.fdn-chat`, so their colour and weight outrank the reset that has
|
|
914
|
+
every button in the panel inherit its font and colour (`.fdn-chat button`). */
|
|
915
|
+
.fdn-chat .fdn-button {
|
|
874
916
|
height: 32px;
|
|
875
917
|
padding: 0 14px;
|
|
876
918
|
border: 1px solid var(--fdn-border);
|
|
@@ -881,27 +923,27 @@
|
|
|
881
923
|
cursor: pointer;
|
|
882
924
|
}
|
|
883
925
|
|
|
884
|
-
.fdn-button:hover:not(:disabled) {
|
|
926
|
+
.fdn-chat .fdn-button:hover:not(:disabled) {
|
|
885
927
|
background: var(--fdn-surface-hover);
|
|
886
928
|
}
|
|
887
929
|
|
|
888
|
-
.fdn-button.fdn-primary {
|
|
930
|
+
.fdn-chat .fdn-button.fdn-primary {
|
|
889
931
|
border-color: var(--fdn-accent);
|
|
890
932
|
background: var(--fdn-accent);
|
|
891
933
|
color: var(--fdn-on-accent);
|
|
892
934
|
}
|
|
893
935
|
|
|
894
|
-
.fdn-button.fdn-primary:hover:not(:disabled) {
|
|
936
|
+
.fdn-chat .fdn-button.fdn-primary:hover:not(:disabled) {
|
|
895
937
|
background: var(--fdn-accent);
|
|
896
938
|
filter: brightness(1.08);
|
|
897
939
|
}
|
|
898
940
|
|
|
899
|
-
.fdn-button.fdn-quiet {
|
|
941
|
+
.fdn-chat .fdn-button.fdn-quiet {
|
|
900
942
|
border-color: transparent;
|
|
901
943
|
color: var(--fdn-text-muted);
|
|
902
944
|
}
|
|
903
945
|
|
|
904
|
-
.fdn-button:disabled {
|
|
946
|
+
.fdn-chat .fdn-button:disabled {
|
|
905
947
|
opacity: 0.45;
|
|
906
948
|
cursor: default;
|
|
907
949
|
}
|
|
@@ -938,7 +980,7 @@
|
|
|
938
980
|
}
|
|
939
981
|
|
|
940
982
|
.fdn-notice-detail {
|
|
941
|
-
color: var(--fdn-text-
|
|
983
|
+
color: var(--fdn-text-muted);
|
|
942
984
|
font-size: 11.5px;
|
|
943
985
|
}
|
|
944
986
|
|
|
@@ -1090,6 +1132,7 @@
|
|
|
1090
1132
|
}
|
|
1091
1133
|
|
|
1092
1134
|
.fdn-composer {
|
|
1135
|
+
position: relative; /* the skill picker opens above it */
|
|
1093
1136
|
margin: 0 10px 10px;
|
|
1094
1137
|
padding-block: 8px 6px; /* the send button sits 6 in from the 12 corner */
|
|
1095
1138
|
padding-inline: 12px 6px;
|
|
@@ -1098,6 +1141,117 @@
|
|
|
1098
1141
|
background: var(--fdn-bg);
|
|
1099
1142
|
}
|
|
1100
1143
|
|
|
1144
|
+
/* Under the composer: answers come from an AI and can be wrong. Quiet, but always there. Below it, on a line of
|
|
1145
|
+
its own and as quiet, the Fadenstack link (`branding: false` hides it). */
|
|
1146
|
+
.fdn-footer {
|
|
1147
|
+
margin: -4px 12px 8px;
|
|
1148
|
+
color: var(--fdn-text-faint);
|
|
1149
|
+
font-size: 11.5px;
|
|
1150
|
+
line-height: 1.4;
|
|
1151
|
+
text-align: center;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
.fdn-ai-notice,
|
|
1155
|
+
.fdn-footer-brand {
|
|
1156
|
+
margin: 0;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
/* A notice that wraps does so in even lines, never one word alone on the last; where balance is not supported,
|
|
1160
|
+
pretty keeps the last line from being one word, and older engines wrap as usual. */
|
|
1161
|
+
.fdn-ai-notice {
|
|
1162
|
+
text-wrap: pretty;
|
|
1163
|
+
text-wrap: balance;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
/* Just enough to tell the link's line from a notice that wraps. */
|
|
1167
|
+
.fdn-ai-notice + .fdn-footer-brand {
|
|
1168
|
+
margin-block-start: 2px;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
/* Muted like the notice, underlined so it is not told apart by colour alone; no arrow on so small a line. */
|
|
1172
|
+
.fdn-footer-link {
|
|
1173
|
+
color: inherit;
|
|
1174
|
+
text-decoration: underline;
|
|
1175
|
+
text-underline-offset: 2px;
|
|
1176
|
+
white-space: nowrap;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
.fdn-footer-link:hover {
|
|
1180
|
+
color: var(--fdn-text-muted);
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
/* A link that opens in a new tab: a small arrow after its text, pointing the way the text runs. */
|
|
1184
|
+
.fdn-external .fdn-icon {
|
|
1185
|
+
display: inline-block;
|
|
1186
|
+
margin-inline-start: 2px;
|
|
1187
|
+
vertical-align: -1px;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
.fdn-external .fdn-icon:dir(rtl) {
|
|
1191
|
+
transform: scaleX(-1);
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
/* About: an overlay below the settings' gear, like the menus. Its text starts where the menu items' labels start
|
|
1195
|
+
(6 the menu + 6 the item + 16 the check + 8 the gap), so it opens in place of the item that opened it. */
|
|
1196
|
+
.fdn-about {
|
|
1197
|
+
display: flex;
|
|
1198
|
+
flex-direction: column;
|
|
1199
|
+
gap: 10px;
|
|
1200
|
+
padding-block: 6px 12px;
|
|
1201
|
+
padding-inline: 36px 6px;
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
.fdn-about-head {
|
|
1205
|
+
display: flex;
|
|
1206
|
+
align-items: center;
|
|
1207
|
+
justify-content: space-between;
|
|
1208
|
+
gap: 8px;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
.fdn-about-agent,
|
|
1212
|
+
.fdn-about-environment {
|
|
1213
|
+
display: flex;
|
|
1214
|
+
flex-direction: column;
|
|
1215
|
+
gap: 1px;
|
|
1216
|
+
padding-inline-end: 6px;
|
|
1217
|
+
overflow-wrap: anywhere;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
.fdn-about-name {
|
|
1221
|
+
font-size: 14px;
|
|
1222
|
+
font-weight: 500;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
.fdn-about-line {
|
|
1226
|
+
color: var(--fdn-text-muted);
|
|
1227
|
+
font-size: 12px;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
/* One link per line: nothing wraps unevenly, whatever the language makes of their names. */
|
|
1231
|
+
.fdn-about-links {
|
|
1232
|
+
display: flex;
|
|
1233
|
+
flex-direction: column;
|
|
1234
|
+
align-items: flex-start;
|
|
1235
|
+
gap: 4px;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
.fdn-about-link {
|
|
1239
|
+
color: var(--fdn-accent);
|
|
1240
|
+
text-decoration: none;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
.fdn-about-link:hover {
|
|
1244
|
+
text-decoration: underline;
|
|
1245
|
+
text-underline-offset: 2px;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
.fdn-about .fdn-button {
|
|
1249
|
+
display: inline-flex;
|
|
1250
|
+
align-items: center;
|
|
1251
|
+
align-self: flex-start;
|
|
1252
|
+
gap: 6px;
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1101
1255
|
.fdn-composer:focus-within {
|
|
1102
1256
|
border-color: var(--fdn-accent);
|
|
1103
1257
|
box-shadow: 0 0 0 1px var(--fdn-accent);
|
|
@@ -1225,8 +1379,14 @@
|
|
|
1225
1379
|
white-space: nowrap;
|
|
1226
1380
|
}
|
|
1227
1381
|
|
|
1382
|
+
/* Short by intent (a site, a page count); a longer one is cut to one line, never wrapped out of the chip. */
|
|
1228
1383
|
.fdn-chip-sub {
|
|
1384
|
+
min-width: 0;
|
|
1385
|
+
max-width: 50%;
|
|
1386
|
+
overflow: hidden;
|
|
1229
1387
|
color: var(--fdn-text-faint);
|
|
1388
|
+
text-overflow: ellipsis;
|
|
1389
|
+
white-space: nowrap;
|
|
1230
1390
|
}
|
|
1231
1391
|
|
|
1232
1392
|
.fdn-chip-remove {
|
|
@@ -1244,3 +1404,37 @@
|
|
|
1244
1404
|
.fdn-chip-remove:hover {
|
|
1245
1405
|
background: var(--fdn-surface-hover);
|
|
1246
1406
|
}
|
|
1407
|
+
|
|
1408
|
+
/* A picture on its chip: small, cut to a square. */
|
|
1409
|
+
.fdn-chip-image {
|
|
1410
|
+
width: 16px;
|
|
1411
|
+
height: 16px;
|
|
1412
|
+
border-radius: 3px;
|
|
1413
|
+
object-fit: cover;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
.fdn-chip-skill .fdn-icon {
|
|
1417
|
+
color: var(--fdn-accent);
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
/* The skills `$` lists: above the composer, as wide as it, the active one marked as the pointer marks one. */
|
|
1421
|
+
.fdn-skill-picker {
|
|
1422
|
+
top: auto;
|
|
1423
|
+
bottom: calc(100% + 4px);
|
|
1424
|
+
inset-inline: 0;
|
|
1425
|
+
min-width: 0;
|
|
1426
|
+
max-width: none;
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
.fdn-skill-list {
|
|
1430
|
+
max-height: min(320px, 50vh);
|
|
1431
|
+
overflow-y: auto;
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
.fdn-skill-option[aria-selected="true"] {
|
|
1435
|
+
background: var(--fdn-surface-hover);
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
.fdn-skill-hint {
|
|
1439
|
+
padding-block-end: 2px;
|
|
1440
|
+
}
|