dsh-genui 0.1.3 → 0.2.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/README.md +11 -6
- package/README.zh-CN.md +11 -6
- package/lib/client.js +165 -9
- package/lib/index.js +6068 -5930
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -20,13 +20,13 @@ All three screenshots are from a live DSH session — not mockups.
|
|
|
20
20
|
|
|
21
21
|
### "Generate a calculator"
|
|
22
22
|
|
|
23
|
-

|
|
24
24
|
|
|
25
25
|
Digits, operators, parentheses, backspace, clear — every key works. That 666 was typed on the keypad, not written into the text by the model.
|
|
26
26
|
|
|
27
27
|
### "Generate a form"
|
|
28
28
|
|
|
29
|
-

|
|
30
30
|
|
|
31
31
|
One sentence produces a full project proposal form: required-field validation, radio group, dropdown, headcount stepper, date picker, toggle, checkboxes, long text.
|
|
32
32
|
|
|
@@ -34,7 +34,7 @@ Hit **Submit** and your answers go back into the conversation, so the AI can ran
|
|
|
34
34
|
|
|
35
35
|
### "Show the last 7 days of traffic as a line chart"
|
|
36
36
|
|
|
37
|
-

|
|
38
38
|
|
|
39
39
|
Two series, legend, axes, gridlines — a rendered chart, not ASCII art.
|
|
40
40
|
|
|
@@ -153,7 +153,11 @@ DSH locks in its plugin set at startup, so **nothing changes in the UI until you
|
|
|
153
153
|
1. Go back to the terminal running DSH and press `Ctrl+C`
|
|
154
154
|
2. Start it again: `dsh web`
|
|
155
155
|
3. Refresh the browser
|
|
156
|
-
4. **Start a new conversation**
|
|
156
|
+
4. **Start a new conversation**
|
|
157
|
+
|
|
158
|
+
### GenUI authoring prompt
|
|
159
|
+
|
|
160
|
+
GenUI authoring starts on by default and remembers your last choice across DSH restarts. Use the sparkle button in the composer tool row if you want to turn the authoring guidance off. Card rendering stays installed either way.
|
|
157
161
|
|
|
158
162
|
### Check that it worked
|
|
159
163
|
|
|
@@ -193,7 +197,7 @@ DSH needs pnpm to manage plugins. Run `corepack enable` (or `npm i -g pnpm`), th
|
|
|
193
197
|
|
|
194
198
|
**The AI doesn't use interfaces on its own?**
|
|
195
199
|
|
|
196
|
-
|
|
200
|
+
If the sparkle toggle is off, the model has no GenUI authoring guidance. Turn it back on, then say something like "give me a form / chart / interface."
|
|
197
201
|
|
|
198
202
|
**Does this change how DSH normally works?**
|
|
199
203
|
|
|
@@ -209,7 +213,7 @@ Yes. Run `dsh plugin remove`, restart, and DSH is back to its original state wit
|
|
|
209
213
|
|
|
210
214
|
In short: the model no longer writes only text — it also writes a *description of an interface*, and the browser renders that description into real components.
|
|
211
215
|
|
|
212
|
-
A bit more detail: the plugin adds a prompt section teaching the model to emit structured JSON (inside a `schemaJson` code block) when an interface would help. The DSH web client picks up
|
|
216
|
+
A bit more detail: the plugin adds a prompt section teaching the model to emit structured JSON (inside a `schemaJson` code block) when an interface would help. The composer sparkle toggle controls that section and is enabled by default. The DSH web client picks up the JSON and hands it to a renderer. It's streaming, so the interface appears as the model writes it — no waiting for the full reply.
|
|
213
217
|
|
|
214
218
|
The components come from [OpenTiny GenUI SDK](https://opentiny.design/genui-sdk) and the OpenTiny Vue library — OpenTiny's generative-UI stack, a spec plus rendering engine for letting LLMs produce interfaces. dsh-genui wires that into the DSH conversation flow.
|
|
215
219
|
|
|
@@ -219,6 +223,7 @@ On safety: the model can only use whitelisted components. It cannot inject HTML
|
|
|
219
223
|
|
|
220
224
|
## Links
|
|
221
225
|
|
|
226
|
+
- Changelog: <https://github.com/lhuans/dsh-genui/releases>
|
|
222
227
|
- npm package: <https://www.npmjs.com/package/dsh-genui>
|
|
223
228
|
- OpenTiny GenUI SDK: <https://github.com/opentiny/genui-sdk>
|
|
224
229
|
- DeepSeek Harness: <https://github.com/deepseek-ai/deepseek-harness>
|
package/README.zh-CN.md
CHANGED
|
@@ -20,13 +20,13 @@
|
|
|
20
20
|
|
|
21
21
|
### 说「生成一个计算器」
|
|
22
22
|
|
|
23
|
-

|
|
24
24
|
|
|
25
25
|
数字键、四则运算、括号、退格、清空,全都能按。算出来的 666 是真按出来的,不是 AI 写在文字里的。
|
|
26
26
|
|
|
27
27
|
### 说「生成一个表单」
|
|
28
28
|
|
|
29
|
-

|
|
30
30
|
|
|
31
31
|
一句话生成一整张立项申请表:必填校验、单选按钮组、下拉框、人数步进器、日期选择、开关、多选框、长文本,一次到位。
|
|
32
32
|
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
|
|
35
35
|
### 说「用折线图展示最近 7 天访问量」
|
|
36
36
|
|
|
37
|
-

|
|
38
38
|
|
|
39
39
|
双折线、图例、坐标轴、网格线,都是渲染出来的图,不是字符画。
|
|
40
40
|
|
|
@@ -153,7 +153,11 @@ DSH 在启动时就锁定了当前的插件集合,所以**装完插件不重
|
|
|
153
153
|
1. 回到跑着 DSH 的那个终端,按 `Ctrl+C` 停掉
|
|
154
154
|
2. 重新启动:`dsh web`
|
|
155
155
|
3. 浏览器刷新一下页面
|
|
156
|
-
4.
|
|
156
|
+
4. **开一个新会话**
|
|
157
|
+
|
|
158
|
+
### GenUI 提示词
|
|
159
|
+
|
|
160
|
+
GenUI 授权提示词默认开启,并且会记住你上一次的选择:DSH 重启后保持关闭或开启。想临时关闭时,点击输入框工具行里的星形按钮即可;它只控制发给模型的提示词,卡片渲染能力始终保留。
|
|
157
161
|
|
|
158
162
|
### 验证装好了没
|
|
159
163
|
|
|
@@ -193,7 +197,7 @@ DSH 管理插件依赖 pnpm。执行 `corepack enable`(或 `npm i -g pnpm`)
|
|
|
193
197
|
|
|
194
198
|
**AI 不主动用界面回答?**
|
|
195
199
|
|
|
196
|
-
|
|
200
|
+
如果星形开关是关闭的,模型不会收到 GenUI 提示词。重新打开开关,再说一句「用界面/表单/图表的方式给我」就行。
|
|
197
201
|
|
|
198
202
|
**会不会影响原来的用法?**
|
|
199
203
|
|
|
@@ -209,7 +213,7 @@ DSH 管理插件依赖 pnpm。执行 `corepack enable`(或 `npm i -g pnpm`)
|
|
|
209
213
|
|
|
210
214
|
一句话:AI 不再只写文字,它还会写一段「界面描述」,浏览器把这段描述渲染成真正的组件。
|
|
211
215
|
|
|
212
|
-
|
|
216
|
+
具体一点:插件默认给模型加一段提示词,教它在需要的时候输出一段结构化的 JSON(写在 `schemaJson` 代码块里);输入框里的星形开关控制是否加这段提示词。DSH 网页端拿到 JSON 后,交给渲染器变成实际组件。整个过程是流式的,AI 写到哪、界面就渲染到哪,不用等它把话说完。
|
|
213
217
|
|
|
214
218
|
组件本身来自 [OpenTiny GenUI SDK](https://opentiny.design/genui-sdk) 和 OpenTiny Vue 组件库——这是 OpenTiny 团队做的生成式 UI 方案,一整套「让大模型输出界面」的规范和渲染引擎。dsh-genui 做的事情,是把它接到了 DSH 的对话流里。
|
|
215
219
|
|
|
@@ -219,6 +223,7 @@ DSH 管理插件依赖 pnpm。执行 `corepack enable`(或 `npm i -g pnpm`)
|
|
|
219
223
|
|
|
220
224
|
## 相关链接
|
|
221
225
|
|
|
226
|
+
- 更新记录:<https://github.com/lhuans/dsh-genui/releases>
|
|
222
227
|
- npm 包:<https://www.npmjs.com/package/dsh-genui>
|
|
223
228
|
- OpenTiny GenUI SDK:<https://github.com/opentiny/genui-sdk>
|
|
224
229
|
- DeepSeek Harness:<https://github.com/deepseek-ai/deepseek-harness>
|
package/lib/client.js
CHANGED
|
@@ -7,6 +7,7 @@ window.__ModuleLoader__.load({
|
|
|
7
7
|
let react = require("react");
|
|
8
8
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
9
9
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
|
+
let react_dom = require("react-dom");
|
|
10
11
|
//#region src/client/genui-card-tag.ts
|
|
11
12
|
/** Custom element tag for the Vue GenUI card (thin client + runtime share this string). */
|
|
12
13
|
const DSH_GENUI_CARD_TAG = "dsh-genui-card";
|
|
@@ -117,9 +118,11 @@ window.__ModuleLoader__.load({
|
|
|
117
118
|
* Split assistant text into markdown and schema-card segments.
|
|
118
119
|
* @param text - full assistant text block (streaming or final).
|
|
119
120
|
* @param streaming - whether the turn is still generating.
|
|
121
|
+
* @param interrupted - whether generation was stopped after partial content.
|
|
120
122
|
* @returns ordered segments for mixed markdown + GenUI rendering.
|
|
121
123
|
*/
|
|
122
|
-
function splitAssistantText(text, streaming) {
|
|
124
|
+
function splitAssistantText(text, streaming, interrupted = false) {
|
|
125
|
+
const live = streaming || interrupted;
|
|
123
126
|
const segments = [];
|
|
124
127
|
let last = 0;
|
|
125
128
|
COMPLETE_FENCE.lastIndex = 0;
|
|
@@ -138,7 +141,7 @@ window.__ModuleLoader__.load({
|
|
|
138
141
|
}
|
|
139
142
|
const rest = text.slice(last);
|
|
140
143
|
if (rest.length === 0) return segments;
|
|
141
|
-
if (
|
|
144
|
+
if (live) {
|
|
142
145
|
const open = OPEN_FENCE.exec(rest);
|
|
143
146
|
if (open !== null && open.index !== void 0) {
|
|
144
147
|
const before = rest.slice(0, open.index);
|
|
@@ -162,13 +165,13 @@ window.__ModuleLoader__.load({
|
|
|
162
165
|
}
|
|
163
166
|
//#endregion
|
|
164
167
|
//#region \0dsh-css:module:src/client/genui-assistant.module.css.mjs
|
|
165
|
-
const css = ".vHCAGq_root{flex-direction:column;gap:.5rem;min-width:0;display:flex}.vHCAGq_body{flex-direction:column;gap:.75rem;min-width:0;display:flex}.vHCAGq_card{isolation:isolate;border-radius:8px;width:100%;max-width:100%;display:block;overflow:auto}.vHCAGq_card[data-dsh-genui-card-pending],.vHCAGq_card[data-dsh-genui-card-error]{min-height:4.5rem}.vHCAGq_think{opacity:.85;font-size:.9em}.vHCAGq_stopped{opacity:.7;font-style:italic}.vHCAGq_images{flex-wrap:wrap;gap:.5rem;display:flex}.vHCAGq_image{object-fit:contain;border-radius:6px;max-width:100%;max-height:240px}";
|
|
166
|
-
const tagId = "dsh-genui/src/client/genui-assistant.module.css";
|
|
167
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
168
|
+
const css$1 = ".vHCAGq_root{flex-direction:column;gap:.5rem;min-width:0;display:flex}.vHCAGq_body{flex-direction:column;gap:.75rem;min-width:0;display:flex}.vHCAGq_card{isolation:isolate;border-radius:8px;width:100%;max-width:100%;display:block;overflow:auto}.vHCAGq_card[data-dsh-genui-card-pending],.vHCAGq_card[data-dsh-genui-card-error]{min-height:4.5rem}.vHCAGq_think{opacity:.85;font-size:.9em}.vHCAGq_stopped{opacity:.7;font-style:italic}.vHCAGq_images{flex-wrap:wrap;gap:.5rem;display:flex}.vHCAGq_image{object-fit:contain;border-radius:6px;max-width:100%;max-height:240px}";
|
|
169
|
+
const tagId$1 = "dsh-genui/src/client/genui-assistant.module.css";
|
|
170
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
168
171
|
const tag = document.createElement("style");
|
|
169
172
|
tag.dataset.plugin = "dsh-genui";
|
|
170
|
-
tag.dataset.pluginCss = tagId;
|
|
171
|
-
tag.textContent = css;
|
|
173
|
+
tag.dataset.pluginCss = tagId$1;
|
|
174
|
+
tag.textContent = css$1;
|
|
172
175
|
document.head.appendChild(tag);
|
|
173
176
|
}
|
|
174
177
|
var genui_assistant_module_css_default = {
|
|
@@ -186,8 +189,12 @@ window.__ModuleLoader__.load({
|
|
|
186
189
|
* Mixed markdown + Vue GenUI card body for one assistant text block.
|
|
187
190
|
*/
|
|
188
191
|
/** Render markdown segments and schemaJson fences as GenUI cards. */
|
|
189
|
-
const GenuiTextBody = (0, react.memo)(function GenuiTextBody({ text, streaming, labels, mentions, customActions }) {
|
|
190
|
-
const segments = (0, react.useMemo)(() => splitAssistantText(text, streaming), [
|
|
192
|
+
const GenuiTextBody = (0, react.memo)(function GenuiTextBody({ text, streaming, interrupted, labels, mentions, customActions }) {
|
|
193
|
+
const segments = (0, react.useMemo)(() => splitAssistantText(text, streaming, interrupted), [
|
|
194
|
+
text,
|
|
195
|
+
streaming,
|
|
196
|
+
interrupted
|
|
197
|
+
]);
|
|
191
198
|
const nodes = [];
|
|
192
199
|
for (let i = 0; i < segments.length; i++) {
|
|
193
200
|
const segment = segments[i];
|
|
@@ -284,6 +291,7 @@ window.__ModuleLoader__.load({
|
|
|
284
291
|
rendered.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(GenuiTextBody, {
|
|
285
292
|
text: block.text,
|
|
286
293
|
streaming,
|
|
294
|
+
interrupted,
|
|
287
295
|
labels,
|
|
288
296
|
mentions,
|
|
289
297
|
customActions
|
|
@@ -338,6 +346,149 @@ window.__ModuleLoader__.load({
|
|
|
338
346
|
});
|
|
339
347
|
});
|
|
340
348
|
//#endregion
|
|
349
|
+
//#region src/prompt-control-url.ts
|
|
350
|
+
/** Same-origin host route used by the composer toggle button. */
|
|
351
|
+
const GENUI_PROMPT_CONTROL_URL = "/api/dsh-genui/prompt";
|
|
352
|
+
//#endregion
|
|
353
|
+
//#region \0dsh-css:module:src/client/prompt-toggle-button.module.css.mjs
|
|
354
|
+
const css = ".aG4Rta_toggle{width:28px;height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:14px;justify-content:center;align-items:center;display:inline-flex}.aG4Rta_toggle:hover:not(:disabled),.aG4Rta_toggle:focus-visible{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);outline:none}.aG4Rta_toggle[aria-disabled=true]{cursor:not-allowed;opacity:.4}.aG4Rta_toggle[data-active]{background:var(--dsw-alias-button-ghost-active-fill);color:var(--dsw-alias-label-primary);box-shadow:inset 0 0 0 1px var(--dsw-alias-button-ghost-active-border)}.aG4Rta_tooltip{z-index:1000;background:var(--dsw-alias-tooltip-bg);width:max-content;max-width:50vw;color:var(--dsw-static-neutral-bluish-00);white-space:pre-line;overflow-wrap:break-word;pointer-events:none;border-radius:8px;padding:3px 7px;font-size:13px;line-height:20px;position:fixed;transform:translate(-50%,-100%)}";
|
|
355
|
+
const tagId = "dsh-genui/src/client/prompt-toggle-button.module.css";
|
|
356
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
357
|
+
const tag = document.createElement("style");
|
|
358
|
+
tag.dataset.plugin = "dsh-genui";
|
|
359
|
+
tag.dataset.pluginCss = tagId;
|
|
360
|
+
tag.textContent = css;
|
|
361
|
+
document.head.appendChild(tag);
|
|
362
|
+
}
|
|
363
|
+
var prompt_toggle_button_module_css_default = {
|
|
364
|
+
"toggle": "aG4Rta_toggle",
|
|
365
|
+
"tooltip": "aG4Rta_tooltip"
|
|
366
|
+
};
|
|
367
|
+
//#endregion
|
|
368
|
+
//#region src/client/prompt-toggle-button.tsx
|
|
369
|
+
/**
|
|
370
|
+
* Turn the GenUI authoring prompt on or off from the composer tool row.
|
|
371
|
+
* Rendering stays installed either way; only the prompt section changes.
|
|
372
|
+
*/
|
|
373
|
+
function PromptToggleButton(_props) {
|
|
374
|
+
const [enabled, setEnabled] = (0, react.useState)(false);
|
|
375
|
+
const [available, setAvailable] = (0, react.useState)(false);
|
|
376
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
377
|
+
const [tooltipPoint, setTooltipPoint] = (0, react.useState)(null);
|
|
378
|
+
const buttonRef = (0, react.useRef)(null);
|
|
379
|
+
const tooltipTimer = (0, react.useRef)(null);
|
|
380
|
+
(0, react.useEffect)(() => {
|
|
381
|
+
let active = true;
|
|
382
|
+
const controller = new AbortController();
|
|
383
|
+
fetch(GENUI_PROMPT_CONTROL_URL, { signal: controller.signal }).then(async (response) => {
|
|
384
|
+
if (!response.ok) throw new Error(String(response.status));
|
|
385
|
+
const state = await response.json();
|
|
386
|
+
if (active) {
|
|
387
|
+
setEnabled(state.enabled === true);
|
|
388
|
+
setAvailable(true);
|
|
389
|
+
}
|
|
390
|
+
}).catch(() => {
|
|
391
|
+
if (active) setAvailable(false);
|
|
392
|
+
});
|
|
393
|
+
return () => {
|
|
394
|
+
active = false;
|
|
395
|
+
controller.abort();
|
|
396
|
+
};
|
|
397
|
+
}, []);
|
|
398
|
+
const toggle = (0, react.useCallback)(async () => {
|
|
399
|
+
if (busy) return;
|
|
400
|
+
const next = !enabled;
|
|
401
|
+
setBusy(true);
|
|
402
|
+
setEnabled(next);
|
|
403
|
+
try {
|
|
404
|
+
const response = await fetch(GENUI_PROMPT_CONTROL_URL, {
|
|
405
|
+
method: "POST",
|
|
406
|
+
headers: { "content-type": "application/json" },
|
|
407
|
+
body: JSON.stringify({ enabled: next })
|
|
408
|
+
});
|
|
409
|
+
if (!response.ok) throw new Error(String(response.status));
|
|
410
|
+
const state = await response.json();
|
|
411
|
+
setEnabled(state.enabled === true);
|
|
412
|
+
setAvailable(true);
|
|
413
|
+
} catch {
|
|
414
|
+
setEnabled(!next);
|
|
415
|
+
setAvailable(false);
|
|
416
|
+
} finally {
|
|
417
|
+
setBusy(false);
|
|
418
|
+
}
|
|
419
|
+
}, [busy, enabled]);
|
|
420
|
+
const showTooltip = (0, react.useCallback)(() => {
|
|
421
|
+
if (tooltipTimer.current !== null) return;
|
|
422
|
+
tooltipTimer.current = setTimeout(() => {
|
|
423
|
+
tooltipTimer.current = null;
|
|
424
|
+
const button = buttonRef.current;
|
|
425
|
+
if (button === null) return;
|
|
426
|
+
const rect = button.getBoundingClientRect();
|
|
427
|
+
setTooltipPoint({
|
|
428
|
+
left: rect.left + rect.width / 2,
|
|
429
|
+
top: rect.top - 8
|
|
430
|
+
});
|
|
431
|
+
}, 120);
|
|
432
|
+
}, []);
|
|
433
|
+
const hideTooltip = (0, react.useCallback)(() => {
|
|
434
|
+
if (tooltipTimer.current !== null) {
|
|
435
|
+
clearTimeout(tooltipTimer.current);
|
|
436
|
+
tooltipTimer.current = null;
|
|
437
|
+
}
|
|
438
|
+
setTooltipPoint(null);
|
|
439
|
+
}, []);
|
|
440
|
+
const tooltipOpen = tooltipPoint !== null;
|
|
441
|
+
(0, react.useLayoutEffect)(() => {
|
|
442
|
+
if (!tooltipOpen) return;
|
|
443
|
+
const update = () => {
|
|
444
|
+
const button = buttonRef.current;
|
|
445
|
+
if (button === null) return;
|
|
446
|
+
const rect = button.getBoundingClientRect();
|
|
447
|
+
setTooltipPoint({
|
|
448
|
+
left: rect.left + rect.width / 2,
|
|
449
|
+
top: rect.top - 8
|
|
450
|
+
});
|
|
451
|
+
};
|
|
452
|
+
update();
|
|
453
|
+
window.addEventListener("resize", update);
|
|
454
|
+
window.addEventListener("scroll", update, true);
|
|
455
|
+
return () => {
|
|
456
|
+
window.removeEventListener("resize", update);
|
|
457
|
+
window.removeEventListener("scroll", update, true);
|
|
458
|
+
};
|
|
459
|
+
}, [tooltipOpen]);
|
|
460
|
+
(0, react.useEffect)(() => hideTooltip, [hideTooltip]);
|
|
461
|
+
const label = available ? enabled ? "GenUI prompt on" : "GenUI prompt off" : "GenUI prompt unavailable";
|
|
462
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
463
|
+
ref: buttonRef,
|
|
464
|
+
type: "button",
|
|
465
|
+
className: prompt_toggle_button_module_css_default.toggle,
|
|
466
|
+
"aria-label": "GenUI prompt",
|
|
467
|
+
"aria-pressed": enabled,
|
|
468
|
+
"aria-disabled": !available,
|
|
469
|
+
"data-active": enabled || void 0,
|
|
470
|
+
onClick: () => {
|
|
471
|
+
if (available && !busy) toggle();
|
|
472
|
+
},
|
|
473
|
+
onMouseDown: (event) => {
|
|
474
|
+
event.preventDefault();
|
|
475
|
+
},
|
|
476
|
+
onMouseEnter: showTooltip,
|
|
477
|
+
onMouseLeave: hideTooltip,
|
|
478
|
+
onFocus: showTooltip,
|
|
479
|
+
onBlur: hideTooltip,
|
|
480
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSparkle16, { size: 14 })
|
|
481
|
+
}), tooltipPoint !== null && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
482
|
+
className: prompt_toggle_button_module_css_default.tooltip,
|
|
483
|
+
role: "tooltip",
|
|
484
|
+
style: {
|
|
485
|
+
left: tooltipPoint.left,
|
|
486
|
+
top: tooltipPoint.top
|
|
487
|
+
},
|
|
488
|
+
children: label
|
|
489
|
+
}), document.body)] });
|
|
490
|
+
}
|
|
491
|
+
//#endregion
|
|
341
492
|
//#region src/client/index.ts
|
|
342
493
|
const inject = ["slots"];
|
|
343
494
|
/**
|
|
@@ -346,6 +497,11 @@ window.__ModuleLoader__.load({
|
|
|
346
497
|
*/
|
|
347
498
|
function apply(ctx) {
|
|
348
499
|
prefetchGenuiRuntime();
|
|
500
|
+
ctx.slots.inject("conversation.input.left", () => ctx.slots.register({
|
|
501
|
+
name: "conversation.input.left",
|
|
502
|
+
id: "genui-prompt-toggle",
|
|
503
|
+
order: 0
|
|
504
|
+
}, PromptToggleButton));
|
|
349
505
|
ctx.slots.inject("conversation.chat.node", () => ctx.slots.register({
|
|
350
506
|
name: "conversation.chat.node",
|
|
351
507
|
key: "assistant-step",
|