dsh-model-arena 0.1.2 → 0.1.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.4
4
+
5
+ - **Fix the client half never activating.** The browser half declared package names
6
+ (`@deepseek-ai/dsh-client-ui-settings`) as cordis services, but services are named by
7
+ their runtime identity — the settings shell is `slots`, the host bridge is
8
+ `connection`. The fiber therefore never resolved and the plugin stayed pending, which
9
+ the loader reported as "did not activate". It now injects `slots` and registers into
10
+ the `settings.section` slot the way the official client artifacts do.
11
+
12
+ ## 0.1.3
13
+
14
+ - **Fix the browser half never registering.** The client bundle was emitted as plain ESM
15
+ with `react` external, which prints a top-level `import` — invalid inside the loader's
16
+ concatenated script bundle, so evaluation aborted and every module after it failed with
17
+ "...loaded without registering ... via __ModuleLoader__.load". The bundle is now emitted
18
+ in the loader's lazy-CJS factory form (`window.__ModuleLoader__.load({ id, factory })`).
19
+ - Point the `./client` export's `types` at `lib/client/index.d.ts`, the file the client
20
+ half actually compiles to.
21
+
3
22
  ## 0.1.0 (2026-09-18)
4
23
 
5
24
  Initial release.
package/lib/arena.web.js CHANGED
@@ -1,3 +1,33 @@
1
+ window.__ModuleLoader__.load({ id: "dsh-model-arena", factory: (require) => {
2
+ var module = { exports: {} }; var exports = module.exports;
3
+ "use strict";
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+
22
+ // src/client/index.tsx
23
+ var index_exports = {};
24
+ __export(index_exports, {
25
+ apply: () => apply,
26
+ inject: () => inject
27
+ });
28
+ module.exports = __toCommonJS(index_exports);
29
+ var import_react = require("react");
30
+
1
31
  // src/client/view.tsx
2
32
  function renderPanel(payload) {
3
33
  const runRows = payload.recentRuns.map((r) => `<tr><td>${r.id}</td><td>${r.modelCount}</td><td>${new Date(r.timestamp).toLocaleString()}</td><td>${r.prompt.slice(0, 60)}...</td></tr>`).join("");
@@ -8,22 +38,54 @@ function renderPanel(payload) {
8
38
  }
9
39
 
10
40
  // src/client/index.tsx
11
- var inject = ["@deepseek-ai/dsh-client-ui-settings", "@deepseek-ai/dsh-client-connection"];
12
- function apply(ctx) {
13
- ctx.inject(inject, (settings, connection) => {
14
- const s = settings;
15
- const c = connection;
16
- s.section("arena", {
17
- title: "Model Arena",
18
- render: async () => {
19
- const res = await c.fetch("/api/arena.panel");
20
- const payload = await res.json();
21
- return renderPanel(payload);
22
- }
41
+ var import_jsx_runtime = require("react/jsx-runtime");
42
+ var PANEL_PATH = "/api/arena.panel";
43
+ function usePanel() {
44
+ const [html, setHtml] = (0, import_react.useState)(null);
45
+ const [error, setError] = (0, import_react.useState)(null);
46
+ const [tick, setTick] = (0, import_react.useState)(0);
47
+ const reload = (0, import_react.useCallback)(() => setTick((t) => t + 1), []);
48
+ (0, import_react.useEffect)(() => {
49
+ const controller = new AbortController();
50
+ setError(null);
51
+ fetch(PANEL_PATH, { signal: controller.signal }).then(async (response) => {
52
+ if (!response.ok) throw new Error(`panel request failed with ${response.status}`);
53
+ return response.json();
54
+ }).then((payload) => {
55
+ if (!controller.signal.aborted) setHtml(renderPanel(payload));
56
+ }).catch((cause) => {
57
+ if (controller.signal.aborted) return;
58
+ setError(cause instanceof Error ? cause.message : String(cause));
23
59
  });
24
- });
60
+ return () => controller.abort();
61
+ }, [tick]);
62
+ return { html, error, reload };
25
63
  }
26
- export {
27
- apply,
28
- inject
29
- };
64
+ function ModelArenaPage() {
65
+ const { html, error, reload } = usePanel();
66
+ if (error !== null) {
67
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { children: [
68
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { role: "alert", children: [
69
+ "Model Arena panel failed to load: ",
70
+ error
71
+ ] }),
72
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: reload, children: "Retry" })
73
+ ] });
74
+ }
75
+ if (html === null) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { "aria-live": "polite", children: "Loading the Model Arena panel\u2026" });
76
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { dangerouslySetInnerHTML: { __html: html } });
77
+ }
78
+ var inject = ["slots"];
79
+ function apply(ctx) {
80
+ ctx.slots.inject("settings.section", () => ctx.slots.register(
81
+ {
82
+ name: "settings.section",
83
+ id: "model-arena",
84
+ order: 42,
85
+ label: () => "Model Arena"
86
+ },
87
+ ModelArenaPage
88
+ ));
89
+ }
90
+ return module.exports; } });
91
+ //# sourceMappingURL=arena.web.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/client/index.tsx", "../src/client/view.tsx"],
4
+ "sourcesContent": ["/**\n * Browser half of dsh-model-arena: the Model Arena page inside Settings.\n *\n * Services are named by their runtime identity, not by the package that provides them:\n * the settings shell is the `slots` service and the host bridge is `connection`. A\n * fiber that injects package names never resolves and the plugin stays pending, which\n * the loader reports as \"did not activate\".\n *\n * @module client\n */\n\nimport { useCallback, useEffect, useState } from 'react'\nimport type { ComponentType } from 'react'\nimport type { PanelPayload } from '../types.js'\nimport { renderPanel } from './view.js'\n\n/** Path the host half registers on the web connection. */\nconst PANEL_PATH = '/api/arena.panel'\n\ninterface SlotsService {\n inject(name: string, register: () => void): void\n register(options: { name: string; id: string; order: number; label: () => string }, component: ComponentType): unknown\n}\n\n/** Fetch the panel payload; `reload` re-runs the request. */\nfunction usePanel() {\n const [html, setHtml] = useState<string | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [tick, setTick] = useState(0)\n const reload = useCallback(() => setTick((t) => t + 1), [])\n\n useEffect(() => {\n const controller = new AbortController()\n setError(null)\n fetch(PANEL_PATH, { signal: controller.signal })\n .then(async (response) => {\n if (!response.ok) throw new Error(`panel request failed with ${response.status}`)\n return response.json() as Promise<PanelPayload>\n })\n .then((payload) => {\n if (!controller.signal.aborted) setHtml(renderPanel(payload))\n })\n .catch((cause: unknown) => {\n if (controller.signal.aborted) return\n // Say what failed rather than rendering an empty panel, which would read as\n // \"nothing recorded\" \u2014 a different and wrong answer.\n setError(cause instanceof Error ? cause.message : String(cause))\n })\n return () => controller.abort()\n }, [tick])\n\n return { html, error, reload }\n}\n\nfunction ModelArenaPage() {\n const { html, error, reload } = usePanel()\n if (error !== null) {\n return (\n <section>\n <p role=\"alert\">Model Arena panel failed to load: {error}</p>\n <button type=\"button\" onClick={reload}>Retry</button>\n </section>\n )\n }\n if (html === null) return <p aria-live=\"polite\">Loading the Model Arena panel\u2026</p>\n // The markup is produced by `renderPanel`, which escapes every interpolated value.\n return <div dangerouslySetInnerHTML={{ __html: html }} />\n}\n\n/** The slots service is what the settings shell exposes. */\nexport const inject = ['slots']\n\nexport function apply(ctx: { slots: SlotsService }): void {\n ctx.slots.inject('settings.section', () => ctx.slots.register(\n {\n name: 'settings.section',\n id: 'model-arena',\n order: 42,\n label: () => 'Model Arena',\n },\n ModelArenaPage,\n ))\n}\n", "import type { PanelPayload } from '../types.js';\n\nexport function renderPanel(payload: PanelPayload): string {\n const runRows = payload.recentRuns\n .map((r) => `<tr><td>${r.id}</td><td>${r.modelCount}</td><td>${new Date(r.timestamp).toLocaleString()}</td><td>${r.prompt.slice(0, 60)}...</td></tr>`)\n .join('');\n\n return `<div class=\"arena-panel\">\n <h2>Model Arena</h2>\n ${runRows ? `<table class=\"arena-table\"><thead><tr><th>Run</th><th>Models</th><th>Date</th><th>Prompt</th></tr></thead><tbody>${runRows}</tbody></table>` : '<p>No runs yet.</p>'}\n </div>`;\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWA,mBAAiD;;;ACT1C,SAAS,YAAY,SAA+B;AACzD,QAAM,UAAU,QAAQ,WACrB,IAAI,CAAC,MAAM,WAAW,EAAE,EAAE,YAAY,EAAE,UAAU,YAAY,IAAI,KAAK,EAAE,SAAS,EAAE,eAAe,CAAC,YAAY,EAAE,OAAO,MAAM,GAAG,EAAE,CAAC,eAAe,EACpJ,KAAK,EAAE;AAEV,SAAO;AAAA;AAAA,MAEH,UAAU,oHAAoH,OAAO,qBAAqB,qBAAqB;AAAA;AAErL;;;ADgDQ;AA1CR,IAAM,aAAa;AAQnB,SAAS,WAAW;AAClB,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAwB,IAAI;AACpD,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAwB,IAAI;AACtD,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAS,CAAC;AAClC,QAAM,aAAS,0BAAY,MAAM,QAAQ,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;AAE1D,8BAAU,MAAM;AACd,UAAM,aAAa,IAAI,gBAAgB;AACvC,aAAS,IAAI;AACb,UAAM,YAAY,EAAE,QAAQ,WAAW,OAAO,CAAC,EAC5C,KAAK,OAAO,aAAa;AACxB,UAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,6BAA6B,SAAS,MAAM,EAAE;AAChF,aAAO,SAAS,KAAK;AAAA,IACvB,CAAC,EACA,KAAK,CAAC,YAAY;AACjB,UAAI,CAAC,WAAW,OAAO,QAAS,SAAQ,YAAY,OAAO,CAAC;AAAA,IAC9D,CAAC,EACA,MAAM,CAAC,UAAmB;AACzB,UAAI,WAAW,OAAO,QAAS;AAG/B,eAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,IACjE,CAAC;AACH,WAAO,MAAM,WAAW,MAAM;AAAA,EAChC,GAAG,CAAC,IAAI,CAAC;AAET,SAAO,EAAE,MAAM,OAAO,OAAO;AAC/B;AAEA,SAAS,iBAAiB;AACxB,QAAM,EAAE,MAAM,OAAO,OAAO,IAAI,SAAS;AACzC,MAAI,UAAU,MAAM;AAClB,WACE,6CAAC,aACC;AAAA,mDAAC,OAAE,MAAK,SAAQ;AAAA;AAAA,QAAmC;AAAA,SAAM;AAAA,MACzD,4CAAC,YAAO,MAAK,UAAS,SAAS,QAAQ,mBAAK;AAAA,OAC9C;AAAA,EAEJ;AACA,MAAI,SAAS,KAAM,QAAO,4CAAC,OAAE,aAAU,UAAS,iDAA8B;AAE9E,SAAO,4CAAC,SAAI,yBAAyB,EAAE,QAAQ,KAAK,GAAG;AACzD;AAGO,IAAM,SAAS,CAAC,OAAO;AAEvB,SAAS,MAAM,KAAoC;AACxD,MAAI,MAAM,OAAO,oBAAoB,MAAM,IAAI,MAAM;AAAA,IACnD;AAAA,MACE,MAAM;AAAA,MACN,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,OAAO,MAAM;AAAA,IACf;AAAA,IACA;AAAA,EACF,CAAC;AACH;",
6
+ "names": []
7
+ }
@@ -1,4 +1,26 @@
1
+ /**
2
+ * Browser half of dsh-model-arena: the Model Arena page inside Settings.
3
+ *
4
+ * Services are named by their runtime identity, not by the package that provides them:
5
+ * the settings shell is the `slots` service and the host bridge is `connection`. A
6
+ * fiber that injects package names never resolves and the plugin stays pending, which
7
+ * the loader reports as "did not activate".
8
+ *
9
+ * @module client
10
+ */
11
+ import type { ComponentType } from 'react';
12
+ interface SlotsService {
13
+ inject(name: string, register: () => void): void;
14
+ register(options: {
15
+ name: string;
16
+ id: string;
17
+ order: number;
18
+ label: () => string;
19
+ }, component: ComponentType): unknown;
20
+ }
21
+ /** The slots service is what the settings shell exposes. */
1
22
  export declare const inject: string[];
2
23
  export declare function apply(ctx: {
3
- inject: (deps: string[], fn: (...services: unknown[]) => void) => void;
24
+ slots: SlotsService;
4
25
  }): void;
26
+ export {};
@@ -1,16 +1,65 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Browser half of dsh-model-arena: the Model Arena page inside Settings.
4
+ *
5
+ * Services are named by their runtime identity, not by the package that provides them:
6
+ * the settings shell is the `slots` service and the host bridge is `connection`. A
7
+ * fiber that injects package names never resolves and the plugin stays pending, which
8
+ * the loader reports as "did not activate".
9
+ *
10
+ * @module client
11
+ */
12
+ import { useCallback, useEffect, useState } from 'react';
1
13
  import { renderPanel } from './view.js';
2
- export const inject = ['@deepseek-ai/dsh-client-ui-settings', '@deepseek-ai/dsh-client-connection'];
3
- export function apply(ctx) {
4
- ctx.inject(inject, (settings, connection) => {
5
- const s = settings;
6
- const c = connection;
7
- s.section('arena', {
8
- title: 'Model Arena',
9
- render: async () => {
10
- const res = await c.fetch('/api/arena.panel');
11
- const payload = await res.json();
12
- return renderPanel(payload);
13
- },
14
+ /** Path the host half registers on the web connection. */
15
+ const PANEL_PATH = '/api/arena.panel';
16
+ /** Fetch the panel payload; `reload` re-runs the request. */
17
+ function usePanel() {
18
+ const [html, setHtml] = useState(null);
19
+ const [error, setError] = useState(null);
20
+ const [tick, setTick] = useState(0);
21
+ const reload = useCallback(() => setTick((t) => t + 1), []);
22
+ useEffect(() => {
23
+ const controller = new AbortController();
24
+ setError(null);
25
+ fetch(PANEL_PATH, { signal: controller.signal })
26
+ .then(async (response) => {
27
+ if (!response.ok)
28
+ throw new Error(`panel request failed with ${response.status}`);
29
+ return response.json();
30
+ })
31
+ .then((payload) => {
32
+ if (!controller.signal.aborted)
33
+ setHtml(renderPanel(payload));
34
+ })
35
+ .catch((cause) => {
36
+ if (controller.signal.aborted)
37
+ return;
38
+ // Say what failed rather than rendering an empty panel, which would read as
39
+ // "nothing recorded" — a different and wrong answer.
40
+ setError(cause instanceof Error ? cause.message : String(cause));
14
41
  });
15
- });
42
+ return () => controller.abort();
43
+ }, [tick]);
44
+ return { html, error, reload };
45
+ }
46
+ function ModelArenaPage() {
47
+ const { html, error, reload } = usePanel();
48
+ if (error !== null) {
49
+ return (_jsxs("section", { children: [_jsxs("p", { role: "alert", children: ["Model Arena panel failed to load: ", error] }), _jsx("button", { type: "button", onClick: reload, children: "Retry" })] }));
50
+ }
51
+ if (html === null)
52
+ return _jsx("p", { "aria-live": "polite", children: "Loading the Model Arena panel\u2026" });
53
+ // The markup is produced by `renderPanel`, which escapes every interpolated value.
54
+ return _jsx("div", { dangerouslySetInnerHTML: { __html: html } });
55
+ }
56
+ /** The slots service is what the settings shell exposes. */
57
+ export const inject = ['slots'];
58
+ export function apply(ctx) {
59
+ ctx.slots.inject('settings.section', () => ctx.slots.register({
60
+ name: 'settings.section',
61
+ id: 'model-arena',
62
+ order: 42,
63
+ label: () => 'Model Arena',
64
+ }, ModelArenaPage));
16
65
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-model-arena",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Run the same prompt through every model you have, and see the difference without squinting.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -14,7 +14,7 @@
14
14
  "default": "./lib/index.js"
15
15
  },
16
16
  "./client": {
17
- "types": "./lib/client.d.ts",
17
+ "types": "./lib/client/index.d.ts",
18
18
  "default": "./lib/arena.web.js"
19
19
  },
20
20
  "./package.json": "./package.json"
@@ -41,8 +41,8 @@
41
41
  "client": {
42
42
  "platform": "web",
43
43
  "inject": [
44
- "@deepseek-ai/dsh-client-ui-settings",
45
- "@deepseek-ai/dsh-client-connection"
44
+ "slots",
45
+ "connection"
46
46
  ]
47
47
  }
48
48
  },