@genkaku/dsh-worker-plugin 0.1.22 → 0.1.23

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.
Files changed (2) hide show
  1. package/lib/client.js +116 -56
  2. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -92,26 +92,36 @@ var PANEL_CSS = `
92
92
  .gk-pull { width: 260px; max-width: 100%; }
93
93
  .gk-muted { color: var(--dsw-alias-label-tertiary); font-size: 12px; }
94
94
  /* The panel can hold every model Ollama has (two dozen is normal). A bounded
95
- list with its own scrollbar keeps the download box, Save and the Worker
96
- section reachable instead of pushing them off-screen. */
95
+ list keeps the download box, Save and the Worker section reachable.
96
+ macOS auto-hides native scrollbars (and Chromium screenshots never paint
97
+ them), so the bar is drawn by us: always visible, and click-to-jump. */
98
+ .gk-models-wrap { position: relative; padding-right: 12px; }
97
99
  .gk-models {
98
100
  max-height: min(420px, 45vh);
99
101
  overflow-y: auto;
100
102
  overscroll-behavior: contain;
101
- padding-right: 6px;
102
- scrollbar-width: thin;
103
+ scrollbar-width: none;
103
104
  }
104
- .gk-models::-webkit-scrollbar { width: 10px; }
105
- .gk-models::-webkit-scrollbar-track { background: transparent; }
106
- .gk-models::-webkit-scrollbar-thumb {
107
- background: var(--dsw-alias-border-l3, #9aa3b2);
108
- border: 2px solid transparent;
109
- border-radius: 6px;
110
- background-clip: content-box;
105
+ .gk-models::-webkit-scrollbar { display: none; }
106
+ .gk-rail {
107
+ position: absolute;
108
+ top: 2px;
109
+ bottom: 2px;
110
+ right: 2px;
111
+ width: 8px;
112
+ border-radius: 4px;
113
+ background: rgba(127,137,153,.16);
114
+ cursor: pointer;
111
115
  }
112
- .gk-models:hover::-webkit-scrollbar-thumb {
113
- background-color: var(--dsw-alias-label-tertiary, #6f7887);
116
+ .gk-thumb {
117
+ position: absolute;
118
+ left: 0;
119
+ width: 8px;
120
+ min-height: 16px;
121
+ border-radius: 4px;
122
+ background: rgba(127,137,153,.7);
114
123
  }
124
+ .gk-rail:hover .gk-thumb { background: rgba(127,137,153,.9); }
115
125
  `;
116
126
  function Icon({ size = 16 }) {
117
127
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { display: "inline-flex", width: size, height: size }, "aria-hidden": "true", children: [
@@ -156,6 +166,44 @@ function seedDraft(status) {
156
166
  function draftSignature(draft) {
157
167
  return Object.entries(draft).map(([model, price]) => `${model}=${price}`).sort().join("|");
158
168
  }
169
+ function useScrollThumb() {
170
+ const [node, setNode] = (0, import_react.useState)(null);
171
+ const nodeRef = (0, import_react.useRef)(null);
172
+ const [thumb, setThumb] = (0, import_react.useState)(null);
173
+ const attach = (0, import_react.useCallback)((next) => {
174
+ nodeRef.current = next;
175
+ setNode(next);
176
+ }, []);
177
+ const update = (0, import_react.useCallback)(() => {
178
+ const el = nodeRef.current;
179
+ if (el == null) return;
180
+ const ratio = el.scrollHeight > el.clientHeight + 1 ? el.clientHeight / el.scrollHeight : 1;
181
+ setThumb(ratio >= 1 ? null : { top: el.scrollTop / el.scrollHeight * 100, height: Math.max(ratio * 100, 8) });
182
+ }, []);
183
+ (0, import_react.useEffect)(() => {
184
+ if (node == null) return;
185
+ update();
186
+ node.addEventListener("scroll", update, { passive: true });
187
+ const ro = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(update);
188
+ ro?.observe(node);
189
+ const mo = typeof MutationObserver === "undefined" ? null : new MutationObserver(update);
190
+ mo?.observe(node, { childList: true, subtree: true });
191
+ return () => {
192
+ node.removeEventListener("scroll", update);
193
+ ro?.disconnect();
194
+ mo?.disconnect();
195
+ };
196
+ }, [node, update]);
197
+ const jump = (0, import_react.useCallback)((event) => {
198
+ const el = nodeRef.current;
199
+ const rail = event.currentTarget;
200
+ if (el == null || rail == null) return;
201
+ const rect = rail.getBoundingClientRect();
202
+ if (rect.height <= 0) return;
203
+ el.scrollTop = Math.max(0, (event.clientY - rect.top) / rect.height) * el.scrollHeight;
204
+ }, []);
205
+ return { attach, thumb, update, jump };
206
+ }
159
207
  function Section({ title, children }) {
160
208
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: box, children: [
161
209
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: caption, children: title }),
@@ -173,6 +221,7 @@ function Page() {
173
221
  const [pullModel, setPullModel] = (0, import_react.useState)("");
174
222
  const [pull, setPull] = (0, import_react.useState)(null);
175
223
  const seeded = (0, import_react.useRef)(false);
224
+ const models = useScrollThumb();
176
225
  const [origin, setOrigin] = (0, import_react.useState)(originOf(CANDIDATE_PORTS[0]));
177
226
  const originRef = (0, import_react.useRef)(origin);
178
227
  originRef.current = origin;
@@ -472,53 +521,64 @@ function Page() {
472
521
  "\u672A\u52FE\u9078\u7684\u6A21\u578B\u4ECD\u7559\u5728\u672C\u6A5F\uFF0C\u4F46\u4E0D\u6703\u4E0A\u67B6\u3002\u50F9\u683C\u70BA\u6BCF\u5C0F\u6642 USDC\uFF1B0 \u8868\u793A\u514D\u8CBB\u3002"
473
522
  ) }),
474
523
  names.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: dim, children: t("No local models yet. Download one below.", "\u672C\u6A5F\u9084\u6C92\u6709\u6A21\u578B\uFF0C\u53EF\u5728\u4E0B\u65B9\u4E0B\u8F09\u3002") }),
475
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "gk-models", children: names.map((name) => {
476
- const on = name in offered;
477
- const tier = tierOf.get(name);
478
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "gk-row", children: [
479
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
480
- "input",
481
- {
482
- type: "checkbox",
483
- checked: on,
484
- "aria-label": name,
485
- onChange: () => {
486
- toggleOffer(name);
487
- }
488
- }
489
- ),
490
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: [
491
- name,
492
- tier !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { ...dim, fontSize: 11, marginLeft: 8 }, children: tier === "chat" ? t("not for agents", "\u4E0D\u9069\u7528\u65BC agent") : tier })
493
- ] }),
494
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "gk-muted", style: { textAlign: "right" }, children: gb(sizeOf.get(name)) }),
495
- on ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { display: "inline-flex", alignItems: "center", gap: 4 }, children: [
524
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "gk-models-wrap", children: [
525
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "gk-models", ref: models.attach, onScroll: models.update, children: names.map((name) => {
526
+ const on = name in offered;
527
+ const tier = tierOf.get(name);
528
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "gk-row", children: [
496
529
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
497
- import_dsh_client_ui_primitives.Input,
530
+ "input",
498
531
  {
499
- className: "gk-price",
500
- value: offered[name] ?? "",
501
- inputMode: "decimal",
502
- "aria-label": t(`Price per hour for ${name}`, `${name} \u6BCF\u5C0F\u6642\u50F9\u683C`),
503
- onChange: (e) => setDraft({ ...draft ?? {}, [name]: e.target.value })
532
+ type: "checkbox",
533
+ checked: on,
534
+ "aria-label": name,
535
+ onChange: () => {
536
+ toggleOffer(name);
537
+ }
504
538
  }
505
539
  ),
506
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "gk-muted", children: "/h" })
507
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {}),
508
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
509
- import_dsh_client_ui_primitives.Button,
510
- {
511
- variant: "ghost",
512
- size: "sm",
513
- disabled: busy === `remove:${name}`,
514
- onClick: () => {
515
- void removeModel(name);
516
- },
517
- children: t("delete", "\u522A\u9664")
518
- }
519
- )
520
- ] }, name);
521
- }) }),
540
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: [
541
+ name,
542
+ tier !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { ...dim, fontSize: 11, marginLeft: 8 }, children: tier === "chat" ? t("not for agents", "\u4E0D\u9069\u7528\u65BC agent") : tier })
543
+ ] }),
544
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "gk-muted", style: { textAlign: "right" }, children: gb(sizeOf.get(name)) }),
545
+ on ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { display: "inline-flex", alignItems: "center", gap: 4 }, children: [
546
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
547
+ import_dsh_client_ui_primitives.Input,
548
+ {
549
+ className: "gk-price",
550
+ value: offered[name] ?? "",
551
+ inputMode: "decimal",
552
+ "aria-label": t(`Price per hour for ${name}`, `${name} \u6BCF\u5C0F\u6642\u50F9\u683C`),
553
+ onChange: (e) => setDraft({ ...draft ?? {}, [name]: e.target.value })
554
+ }
555
+ ),
556
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "gk-muted", children: "/h" })
557
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {}),
558
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
559
+ import_dsh_client_ui_primitives.Button,
560
+ {
561
+ variant: "ghost",
562
+ size: "sm",
563
+ disabled: busy === `remove:${name}`,
564
+ onClick: () => {
565
+ void removeModel(name);
566
+ },
567
+ children: t("delete", "\u522A\u9664")
568
+ }
569
+ )
570
+ ] }, name);
571
+ }) }),
572
+ models.thumb != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
573
+ "div",
574
+ {
575
+ className: "gk-rail",
576
+ "aria-hidden": "true",
577
+ onPointerDown: (e) => models.jump(e),
578
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "gk-thumb", style: { top: `${models.thumb.top}%`, height: `${models.thumb.height}%` } })
579
+ }
580
+ )
581
+ ] }),
522
582
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { ...row, justifyContent: "flex-start", gap: 8, marginTop: 12 }, children: [
523
583
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
524
584
  import_dsh_client_ui_primitives.Input,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genkaku/dsh-worker-plugin",
3
- "version": "0.1.22",
3
+ "version": "0.1.23",
4
4
  "description": "Share this machine's Ollama models on the GenKaKu network",
5
5
  "license": "MIT",
6
6
  "type": "module",