@musnows/scriverse 1.0.7 → 1.0.8
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/ai.js +4 -1
- package/dist/ai.js.map +1 -1
- package/dist/public/ai-context-meter.js +45 -4
- package/dist/public/ai-render-scheduler.js +30 -0
- package/dist/public/app.js +108 -61
- package/dist/public/index.html +5 -3
- package/dist/public/markdown.js +22 -10
- package/dist/public/stream-markdown.js +107 -0
- package/dist/public/stream-typewriter.d.ts +1 -0
- package/dist/public/stream-typewriter.js +33 -19
- package/dist/public/styles.css +14 -15
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { renderMarkdownParts } from "./markdown.js?v=20260912-stream-render-v2";
|
|
2
|
+
|
|
3
|
+
// 保留末尾两个块,等待列表、引用和表格的后续行确定语义后再冻结前缀。
|
|
4
|
+
export function createMarkdownStreamState() {
|
|
5
|
+
let previous = "";
|
|
6
|
+
let committed = 0;
|
|
7
|
+
return {
|
|
8
|
+
update(value) {
|
|
9
|
+
const source = String(value ?? "").replace(/\r\n?/gu, "\n");
|
|
10
|
+
const reset = !source.startsWith(previous);
|
|
11
|
+
if (reset) committed = 0;
|
|
12
|
+
const parts = renderMarkdownParts(source.slice(committed));
|
|
13
|
+
const stableCount = Math.max(0, parts.length - 2);
|
|
14
|
+
if (stableCount) committed += parts[stableCount - 1].end;
|
|
15
|
+
previous = source;
|
|
16
|
+
return { reset, parts, stableCount };
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// 每个节点都是可让出主线程的边界,长列表和表格也不会作为一个大块集中挂载。
|
|
22
|
+
function* patchMarkdownNode(current, next, parent) {
|
|
23
|
+
if (!current || current.nodeType !== next.nodeType || current.nodeName !== next.nodeName) {
|
|
24
|
+
const replacement = next.cloneNode(false);
|
|
25
|
+
if (current) current.replaceWith(replacement);
|
|
26
|
+
else parent.append(replacement);
|
|
27
|
+
current = replacement;
|
|
28
|
+
}
|
|
29
|
+
if (current.nodeType === 3) {
|
|
30
|
+
if (current.data === next.data) return current;
|
|
31
|
+
if (next.data.startsWith(current.data)) current.appendData(next.data.slice(current.data.length));
|
|
32
|
+
else if (current.data !== next.data) current.data = next.data;
|
|
33
|
+
yield;
|
|
34
|
+
return current;
|
|
35
|
+
}
|
|
36
|
+
if (current.nodeType !== 1) return current;
|
|
37
|
+
for (const attribute of [...current.attributes]) {
|
|
38
|
+
if (!next.hasAttribute(attribute.name)) current.removeAttribute(attribute.name);
|
|
39
|
+
}
|
|
40
|
+
for (const attribute of next.attributes) {
|
|
41
|
+
if (current.getAttribute(attribute.name) !== attribute.value) current.setAttribute(attribute.name, attribute.value);
|
|
42
|
+
}
|
|
43
|
+
yield;
|
|
44
|
+
const children = [...current.childNodes];
|
|
45
|
+
const nextChildren = [...next.childNodes];
|
|
46
|
+
for (const [index, child] of nextChildren.entries()) yield* patchMarkdownNode(children[index], child, current);
|
|
47
|
+
for (const child of children.slice(nextChildren.length)) { child.remove(); yield; }
|
|
48
|
+
return current;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function createStreamingMarkdownRenderer(host, {
|
|
52
|
+
enqueue = (callback) => requestAnimationFrame(callback),
|
|
53
|
+
onRender = () => {},
|
|
54
|
+
now = () => performance.now(),
|
|
55
|
+
budgetMs = 3,
|
|
56
|
+
nodeBudget = 120
|
|
57
|
+
} = {}) {
|
|
58
|
+
const state = createMarkdownStreamState();
|
|
59
|
+
let tail = [];
|
|
60
|
+
let requestedValue = null;
|
|
61
|
+
let appliedValue = null;
|
|
62
|
+
let work = null;
|
|
63
|
+
let queued = false;
|
|
64
|
+
function* apply(value) {
|
|
65
|
+
const update = state.update(value);
|
|
66
|
+
if (update.reset) {
|
|
67
|
+
for (const child of [...host.childNodes]) { child.remove(); yield; }
|
|
68
|
+
tail = [];
|
|
69
|
+
}
|
|
70
|
+
const next = [];
|
|
71
|
+
for (const [index, part] of update.parts.entries()) {
|
|
72
|
+
const previous = tail[index];
|
|
73
|
+
if (previous?.html === part.html) { next.push(previous); continue; }
|
|
74
|
+
const template = host.ownerDocument.createElement("template");
|
|
75
|
+
template.innerHTML = part.html;
|
|
76
|
+
const node = yield* patchMarkdownNode(previous?.node, template.content.firstChild, host);
|
|
77
|
+
next.push({ html: part.html, node });
|
|
78
|
+
}
|
|
79
|
+
for (const item of tail.slice(next.length)) { item.node.remove(); yield; }
|
|
80
|
+
tail = next.slice(update.stableCount);
|
|
81
|
+
appliedValue = value;
|
|
82
|
+
}
|
|
83
|
+
const drain = () => {
|
|
84
|
+
queued = false;
|
|
85
|
+
const started = now();
|
|
86
|
+
if (!work && requestedValue !== appliedValue) work = apply(requestedValue);
|
|
87
|
+
let nodes = 0;
|
|
88
|
+
while (work && nodes < nodeBudget && now() - started < budgetMs) {
|
|
89
|
+
if (work.next().done) { work = null; break; }
|
|
90
|
+
nodes += 1;
|
|
91
|
+
}
|
|
92
|
+
onRender();
|
|
93
|
+
if (work || requestedValue !== appliedValue) schedule();
|
|
94
|
+
};
|
|
95
|
+
const schedule = () => {
|
|
96
|
+
if (queued) return;
|
|
97
|
+
queued = true;
|
|
98
|
+
enqueue(drain);
|
|
99
|
+
};
|
|
100
|
+
return (value) => {
|
|
101
|
+
const normalized = String(value ?? "");
|
|
102
|
+
if (normalized === requestedValue) return false;
|
|
103
|
+
requestedValue = normalized;
|
|
104
|
+
schedule();
|
|
105
|
+
return true;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
@@ -27,6 +27,7 @@ export function createStreamTypewriter<FrameHandle = number>(options: {
|
|
|
27
27
|
scheduleFrame?: (callback: () => void) => FrameHandle;
|
|
28
28
|
cancelFrame?: (handle: FrameHandle) => void;
|
|
29
29
|
reducedMotion?: boolean;
|
|
30
|
+
shouldAnimate?: () => boolean;
|
|
30
31
|
speedController?: StreamTypewriterSpeedController | null;
|
|
31
32
|
visibilitySource?: Pick<Document, "visibilityState" | "addEventListener" | "removeEventListener"> | null;
|
|
32
33
|
}): StreamTypewriter;
|
|
@@ -75,23 +75,34 @@ export function createStreamTypewriter({
|
|
|
75
75
|
cancelFrame = (handle) => window.cancelAnimationFrame(handle),
|
|
76
76
|
reducedMotion = typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true,
|
|
77
77
|
speedController = null,
|
|
78
|
+
shouldAnimate = () => true,
|
|
78
79
|
visibilitySource = typeof document === "undefined" ? null : document
|
|
79
80
|
}) {
|
|
80
81
|
if (typeof onRender !== "function") throw new TypeError("onRender must be a function");
|
|
81
82
|
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
let visibleText = "";
|
|
84
|
+
let visibleCharacterCount = 0;
|
|
85
|
+
let pendingCharacters = [];
|
|
86
|
+
let pendingOffset = 0;
|
|
87
|
+
const pendingCount = () => pendingCharacters.length - pendingOffset;
|
|
88
|
+
const takePending = (count) => {
|
|
89
|
+
const characters = pendingCharacters.slice(pendingOffset, pendingOffset + count);
|
|
90
|
+
visibleText += characters.join("");
|
|
91
|
+
visibleCharacterCount += characters.length;
|
|
92
|
+
pendingOffset += characters.length;
|
|
93
|
+
if (pendingOffset === pendingCharacters.length) { pendingCharacters = []; pendingOffset = 0; }
|
|
94
|
+
};
|
|
84
95
|
const idleResolvers = [];
|
|
85
96
|
let scheduledFrame = null;
|
|
86
97
|
let finishing = false;
|
|
87
98
|
let listeningForVisibility = false;
|
|
88
99
|
|
|
89
|
-
const snapshot = () =>
|
|
100
|
+
const snapshot = () => visibleText;
|
|
90
101
|
const visibilityChanged = () => {
|
|
91
|
-
if (
|
|
102
|
+
if (pendingCount()) typewriter.reveal();
|
|
92
103
|
};
|
|
93
104
|
const resolveIdle = () => {
|
|
94
|
-
if (
|
|
105
|
+
if (pendingCount() || scheduledFrame !== null) return;
|
|
95
106
|
if (listeningForVisibility) {
|
|
96
107
|
visibilitySource.removeEventListener("visibilitychange", visibilityChanged);
|
|
97
108
|
listeningForVisibility = false;
|
|
@@ -101,32 +112,33 @@ export function createStreamTypewriter({
|
|
|
101
112
|
};
|
|
102
113
|
const render = () => {
|
|
103
114
|
onRender(snapshot(), {
|
|
104
|
-
visibleCharacters:
|
|
105
|
-
pendingCharacters:
|
|
115
|
+
visibleCharacters: visibleCharacterCount,
|
|
116
|
+
pendingCharacters: pendingCount()
|
|
106
117
|
});
|
|
107
118
|
};
|
|
108
119
|
const schedule = () => {
|
|
109
|
-
if (visibilitySource?.visibilityState === "hidden") {
|
|
120
|
+
if (visibilitySource?.visibilityState === "hidden" || !shouldAnimate()) {
|
|
110
121
|
typewriter.reveal();
|
|
111
122
|
return;
|
|
112
123
|
}
|
|
113
|
-
if (scheduledFrame !== null ||
|
|
124
|
+
if (scheduledFrame !== null || pendingCount() === 0) return;
|
|
114
125
|
if (visibilitySource && !listeningForVisibility) {
|
|
115
126
|
visibilitySource.addEventListener("visibilitychange", visibilityChanged);
|
|
116
127
|
listeningForVisibility = true;
|
|
117
128
|
}
|
|
118
129
|
scheduledFrame = scheduleFrame(() => {
|
|
119
130
|
scheduledFrame = null;
|
|
120
|
-
const batchSize = reducedMotion
|
|
121
|
-
?
|
|
131
|
+
const batchSize = reducedMotion || !shouldAnimate()
|
|
132
|
+
? pendingCount()
|
|
122
133
|
: streamTypewriterBatchSize(
|
|
123
|
-
|
|
134
|
+
pendingCount(),
|
|
124
135
|
finishing,
|
|
125
136
|
speedController?.charactersPerSecond?.()
|
|
126
137
|
);
|
|
127
|
-
|
|
138
|
+
const catchUp = pendingCount() > 4096 ? Math.min(2048, Math.ceil(pendingCount() / 8)) : batchSize;
|
|
139
|
+
takePending(Math.max(batchSize, catchUp));
|
|
128
140
|
render();
|
|
129
|
-
if (
|
|
141
|
+
if (pendingCount()) schedule();
|
|
130
142
|
else resolveIdle();
|
|
131
143
|
});
|
|
132
144
|
};
|
|
@@ -136,7 +148,7 @@ export function createStreamTypewriter({
|
|
|
136
148
|
const characters = Array.from(String(value ?? ""));
|
|
137
149
|
if (!characters.length) return;
|
|
138
150
|
speedController?.observe?.(characters.length);
|
|
139
|
-
pendingCharacters.push(
|
|
151
|
+
for (const character of characters) pendingCharacters.push(character);
|
|
140
152
|
schedule();
|
|
141
153
|
},
|
|
142
154
|
replace(value) {
|
|
@@ -144,15 +156,17 @@ export function createStreamTypewriter({
|
|
|
144
156
|
cancelFrame(scheduledFrame);
|
|
145
157
|
scheduledFrame = null;
|
|
146
158
|
}
|
|
147
|
-
|
|
148
|
-
|
|
159
|
+
visibleText = String(value ?? "");
|
|
160
|
+
visibleCharacterCount = Array.from(visibleText).length;
|
|
161
|
+
pendingCharacters = [];
|
|
162
|
+
pendingOffset = 0;
|
|
149
163
|
finishing = false;
|
|
150
164
|
render();
|
|
151
165
|
resolveIdle();
|
|
152
166
|
return snapshot();
|
|
153
167
|
},
|
|
154
168
|
finish() {
|
|
155
|
-
if (!
|
|
169
|
+
if (!pendingCount() && scheduledFrame === null) return Promise.resolve(snapshot());
|
|
156
170
|
finishing = true;
|
|
157
171
|
const completed = new Promise((resolve) => idleResolvers.push(resolve));
|
|
158
172
|
schedule();
|
|
@@ -163,7 +177,7 @@ export function createStreamTypewriter({
|
|
|
163
177
|
cancelFrame(scheduledFrame);
|
|
164
178
|
scheduledFrame = null;
|
|
165
179
|
}
|
|
166
|
-
|
|
180
|
+
takePending(pendingCount());
|
|
167
181
|
finishing = false;
|
|
168
182
|
render();
|
|
169
183
|
resolveIdle();
|
package/dist/public/styles.css
CHANGED
|
@@ -3172,11 +3172,11 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
3172
3172
|
.ai-image-preview-body { display: grid; place-items: center; min-height: 180px; max-height: calc(88vh - 106px); padding: 16px 20px 20px; overflow: auto; background: var(--paper); }
|
|
3173
3173
|
.ai-image-preview-body img { display: block; max-width: 100%; max-height: calc(88vh - 150px); width: auto; height: auto; object-fit: contain; border-radius: 3px; }
|
|
3174
3174
|
.ai-attachment-input { position: absolute; width: 1px; height: 1px; overflow: hidden; opacity: 0; pointer-events: none; }
|
|
3175
|
-
.ai-attachment-button, .ai-scene-button { display: grid; place-items: center; width:
|
|
3175
|
+
.ai-attachment-button, .ai-scene-button { display: grid; place-items: center; width: 24px; min-width: 24px; min-height: 24px; height: 24px; padding: 0; border: 1px solid var(--line); border-radius: 4px; background: var(--surface); color: var(--muted); }
|
|
3176
3176
|
.ai-attachment-button:hover, .ai-attachment-button:focus-visible, .ai-scene-button:hover, .ai-scene-button:focus-visible, .ai-scene-button[aria-expanded="true"], .ai-scene-button.is-active { border-color: color-mix(in srgb, var(--accent) 56%, var(--line)); background: color-mix(in srgb, var(--accent) 8%, var(--surface)); color: var(--accent-dark); outline: 0; }
|
|
3177
3177
|
.ai-attachment-button:disabled, .ai-scene-button:disabled { cursor: wait; opacity: .56; }
|
|
3178
3178
|
.ai-scene-button.hidden { display: none; }
|
|
3179
|
-
.ai-image-button-icon, .ai-scene-button-icon { width:
|
|
3179
|
+
.ai-image-button-icon, .ai-scene-button-icon { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.7; }
|
|
3180
3180
|
|
|
3181
3181
|
.character-roleplay-memory-surface { display: grid; grid-column: 1 / -1; gap: 14px; min-width: 0; }
|
|
3182
3182
|
.roleplay-memory-toolbar { display: flex; align-items: center; justify-content: flex-end; gap: 8px; }
|
|
@@ -3230,7 +3230,7 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
3230
3230
|
.ai-skill-option > span { display: grid; gap: 2px; min-width: 0; }
|
|
3231
3231
|
.ai-skill-option em { color: var(--muted); font-size: 9px; font-style: normal; line-height: 1.35; overflow-wrap: anywhere; }
|
|
3232
3232
|
.ai-mention-empty { margin: 0; padding: 10px; color: var(--muted); font-size: 10px; text-align: center; }
|
|
3233
|
-
.ai-prompt { display: block; width: 100%; min-height: 104px; margin: 0; padding: 10px 10px
|
|
3233
|
+
.ai-prompt { display: block; width: 100%; min-height: 104px; margin: 0; padding: 10px 10px 36px; overflow-y: auto; border: 1px solid var(--line); border-radius: 4px; background: var(--surface); color: var(--ink); font-size: 12px; line-height: 1.45; white-space: pre-wrap; word-break: break-word; cursor: text; }
|
|
3234
3234
|
.ai-prompt:empty::before { color: var(--muted); content: attr(data-placeholder); pointer-events: none; }
|
|
3235
3235
|
.ai-prompt:focus { border-color: #a77768; box-shadow: 0 0 0 3px rgba(167,119,104,.12); outline: 0; }
|
|
3236
3236
|
.ai-prompt-reference { display: inline-flex; align-items: center; gap: 4px; max-width: 100%; margin: 0 2px; padding: 2px 4px 2px 7px; border: 1px solid color-mix(in srgb, var(--accent) 48%, var(--line)); border-radius: 999px; background: color-mix(in srgb, var(--accent) 13%, var(--paper)); color: var(--accent-dark); font-size: 10px; line-height: 1.25; vertical-align: baseline; }
|
|
@@ -3238,28 +3238,28 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
3238
3238
|
.ai-prompt-reference button { width: 16px; height: 16px; padding: 0; border: 0; border-radius: 50%; background: transparent; color: inherit; font: 14px/1 var(--font-latin), monospace; }
|
|
3239
3239
|
.ai-prompt-reference button:hover, .ai-prompt-reference button:focus-visible { background: color-mix(in srgb, var(--accent) 16%, transparent); color: var(--accent); }
|
|
3240
3240
|
.prompt-composer-actions { position: absolute; right: 8px; bottom: 8px; display: flex; gap: 6px; align-items: center; }
|
|
3241
|
-
.ai-context-meter { --context-usage: 0; --context-meter-color: var(--green); position: relative; display: grid; flex: 0 0
|
|
3242
|
-
.ai-context-meter::before { content: ""; position: absolute; inset:
|
|
3241
|
+
.ai-context-meter { --context-usage: 0; --context-meter-color: var(--green); position: relative; display: grid; flex: 0 0 24px; place-items: center; width: 24px; min-height: 24px; height: 24px; overflow: visible; border: 0; border-radius: 50%; background: conic-gradient(var(--context-meter-color) calc(var(--context-usage) * 1%), rgba(143,132,116,.2) 0); color: var(--ink); font-family: var(--font-latin); line-height: 1; cursor: pointer; }
|
|
3242
|
+
.ai-context-meter::before { content: ""; position: absolute; inset: 2px; border-radius: 50%; background: var(--paper); box-shadow: inset 0 0 0 1px rgba(143,132,116,.12); }
|
|
3243
3243
|
.ai-context-meter:hover, .ai-context-meter:focus-visible, .ai-context-meter[aria-expanded="true"] { outline: 0; filter: brightness(.96); }
|
|
3244
3244
|
.ai-context-meter:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); }
|
|
3245
|
-
.ai-context-meter
|
|
3245
|
+
.ai-context-meter.is-warning { --context-meter-color: #bf6a35; }.ai-context-meter.is-danger { --context-meter-color: #ad463c; }.ai-context-meter.is-empty { opacity: .62; }
|
|
3246
3246
|
.ai-context-popover { position: absolute; right: 0; bottom: calc(100% + 10px); z-index: 40; display: grid; width: min(320px, calc(100vw - 32px)); gap: 12px; padding: 14px; border: 1px solid var(--line); border-radius: 7px; background: var(--paper); color: var(--ink); box-shadow: 0 14px 38px rgba(36, 30, 24, .18); }
|
|
3247
3247
|
.ai-context-popover.hidden { display: none; }
|
|
3248
|
-
.ai-context-popover::after { position: absolute; right:
|
|
3248
|
+
.ai-context-popover::after { position: absolute; right: 67px; bottom: -6px; width: 10px; height: 10px; border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); background: var(--paper); content: ""; transform: rotate(45deg); }
|
|
3249
3249
|
.ai-context-popover-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding-bottom: 10px; border-bottom: 1px solid var(--line); }
|
|
3250
3250
|
.ai-context-popover-header > div { display: grid; gap: 3px; min-width: 0; }
|
|
3251
3251
|
.ai-context-popover-header strong { font-size: 12px; }
|
|
3252
3252
|
.ai-context-popover-header small { color: var(--muted); font: 9px/1.4 var(--font-latin), var(--font-cjk), sans-serif; }
|
|
3253
3253
|
.ai-context-popover-close { flex: 0 0 auto; width: 24px; height: 24px; padding: 0; border: 1px solid var(--line); border-radius: 4px; background: transparent; color: var(--muted); font: 17px/1 var(--font-latin), sans-serif; }
|
|
3254
3254
|
.ai-context-popover-close:hover, .ai-context-popover-close:focus-visible { border-color: var(--accent); color: var(--accent-dark); outline: 0; }
|
|
3255
|
-
.ai-model-picker { display: grid; flex: 0 0
|
|
3255
|
+
.ai-model-picker { display: grid; flex: 0 0 24px; place-items: center; width: 24px; min-width: 24px; min-height: 24px; height: 24px; padding: 0; border: 1px solid var(--line); border-radius: 4px; background: var(--surface); color: var(--muted); }
|
|
3256
3256
|
.ai-model-picker:hover, .ai-model-picker:focus-visible, .ai-model-picker[aria-expanded="true"] { border-color: color-mix(in srgb, var(--accent) 56%, var(--line)); background: color-mix(in srgb, var(--accent) 8%, var(--surface)); color: var(--accent-dark); outline: 0; }
|
|
3257
3257
|
.ai-model-picker:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent); }
|
|
3258
3258
|
.ai-model-picker:disabled { cursor: wait; opacity: .56; }
|
|
3259
|
-
.ai-model-picker-icon { width:
|
|
3259
|
+
.ai-model-picker-icon { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.7; }
|
|
3260
3260
|
.ai-model-popover { position: absolute; right: 0; bottom: calc(100% + 10px); z-index: 41; display: grid; width: min(280px, calc(100vw - 32px)); gap: 7px; padding: 12px; border: 1px solid var(--line); border-radius: 7px; background: var(--paper); color: var(--ink); box-shadow: 0 14px 38px rgba(36, 30, 24, .18); }
|
|
3261
3261
|
.ai-model-popover.hidden { display: none; }
|
|
3262
|
-
.ai-model-popover::after { position: absolute; right:
|
|
3262
|
+
.ai-model-popover::after { position: absolute; right: 37px; bottom: -6px; width: 10px; height: 10px; border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); background: var(--paper); content: ""; transform: rotate(45deg); }
|
|
3263
3263
|
.ai-model-popover > label { color: var(--muted); font-size: 10px; font-weight: 600; }
|
|
3264
3264
|
.ai-model-native-select { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
|
|
3265
3265
|
.ai-model-options { display: grid; max-height: 240px; gap: 3px; overflow-y: auto; }
|
|
@@ -3287,16 +3287,16 @@ select:focus, input:focus, textarea:focus { border-color: #a77768; box-shadow: 0
|
|
|
3287
3287
|
.ai-context-distribution-row[data-key="input"] { --distribution-color: var(--green); }
|
|
3288
3288
|
.ai-context-distribution-row[data-key="output"] { --distribution-color: #718a9f; }
|
|
3289
3289
|
.ai-context-distribution-row[data-key="left"] { --distribution-color: color-mix(in srgb, var(--muted) 55%, transparent); }
|
|
3290
|
-
.ai-send-button { display: grid; flex: 0 0
|
|
3290
|
+
.ai-send-button { display: grid; flex: 0 0 24px; place-items: center; width: 24px; min-width: 24px; min-height: 24px; height: 24px; padding: 0; border: 0; border-radius: 4px; background: var(--accent); color: #fff; box-shadow: 0 3px 8px rgba(139,61,44,.2); }
|
|
3291
3291
|
.ai-send-button:hover, .ai-send-button:focus-visible { background: var(--accent-dark); outline: 0; }
|
|
3292
|
-
.ai-send-button:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 24%, transparent), 0
|
|
3292
|
+
.ai-send-button:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 24%, transparent), 0 3px 8px rgba(139,61,44,.2); }
|
|
3293
3293
|
.ai-send-button:disabled { cursor: wait; opacity: .62; }
|
|
3294
|
-
.ai-send-button-icon { width:
|
|
3294
|
+
.ai-send-button-icon { width: 14px; height: 14px; overflow: visible; fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 1.8; }
|
|
3295
3295
|
.ai-message-images { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
|
|
3296
3296
|
.ai-message-image-preview { display: block; width: 68px; height: 68px; padding: 0; overflow: hidden; border: 1px solid color-mix(in srgb, var(--accent) 34%, var(--line)); border-radius: 5px; background: var(--paper); cursor: zoom-in; }
|
|
3297
3297
|
.ai-message-image-preview:hover, .ai-message-image-preview:focus-visible { outline: 2px solid color-mix(in srgb, var(--accent) 72%, transparent); outline-offset: 1px; }
|
|
3298
3298
|
.ai-message-image-preview img { display: block; width: 100%; height: 100%; object-fit: cover; }
|
|
3299
|
-
.ai-send-button.is-stop .ai-send-button-icon { width:
|
|
3299
|
+
.ai-send-button.is-stop .ai-send-button-icon { width: 12px; height: 12px; fill: currentColor; stroke: none; }
|
|
3300
3300
|
.ai-citations { display: grid; gap: 6px; max-height: 190px; overflow-y: auto; margin-bottom: 8px; }
|
|
3301
3301
|
.ai-citation-card { display: grid; grid-template-columns: minmax(0, 1fr) auto; border: 1px solid var(--line); border-radius: 5px; background: var(--paper); overflow: hidden; }
|
|
3302
3302
|
.ai-citation-main { min-width: 0; padding: 8px 9px; border: 0; background: transparent; text-align: left; }
|
|
@@ -4705,7 +4705,6 @@ body { font-size: var(--ui-font-size); }
|
|
|
4705
4705
|
.user-message-scene { font-size: calc(10px * var(--ai-font-scale)); }
|
|
4706
4706
|
.user-message-scene-label { font-size: calc(8px * var(--ai-font-scale)); }
|
|
4707
4707
|
.ai-prompt-reference { font-size: calc(10px * var(--ai-font-scale)); }
|
|
4708
|
-
.ai-context-meter b { font-size: calc(9px * var(--ai-font-scale)); }
|
|
4709
4708
|
.ai-context-popover-header strong { font-size: calc(12px * var(--ai-font-scale)); }
|
|
4710
4709
|
.ai-context-popover-header small, .ai-context-distribution-label { font-size: calc(10px * var(--ai-font-scale)); }
|
|
4711
4710
|
.ai-context-distribution-label small, .ai-context-distribution-label strong { font-size: calc(9px * var(--ai-font-scale)); }
|
package/dist/version.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const APP_VERSION = "1.0.
|
|
1
|
+
export const APP_VERSION = "1.0.8";
|
|
2
2
|
export const SCRIVERSE_BETA_COMMIT_ENV = "SCRIVERSE_BETA_COMMIT";
|
|
3
3
|
export function resolveBetaVersionLabel(environment) {
|
|
4
4
|
const commit = environment[SCRIVERSE_BETA_COMMIT_ENV]?.trim().toLocaleLowerCase() ?? "";
|