@sleetdrop/dsh-plugin-topology 0.1.0 → 0.2.0

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 CHANGED
@@ -47,6 +47,16 @@ graph as one document:
47
47
  For `dot` or `svg`, write the returned content to a file with the write tool to
48
48
  produce a shareable artifact. Use `json` to inspect or analyze the assembly.
49
49
 
50
+ ## Screenshots
51
+
52
+ The panel opens maximized by default and renders the live plugin/service
53
+ topology as a zoomable Graphviz graph, with metrics, a color legend, and an
54
+ unresolved-dependency table. The same panel is localized through the harness
55
+ locale dictionaries:
56
+
57
+ - [Chinese (zh_CN)](docs/screenshots/panel-zh_CN.png)
58
+ - [English (en_US)](docs/screenshots/panel-en_US.png)
59
+
50
60
  ## Browser panel
51
61
 
52
62
  The trigger sits in `sidebar.footer.action` (a root-scope list slot, above
@@ -63,12 +73,31 @@ the host assembly's contribution list.
63
73
 
64
74
  ## Compatibility
65
75
 
66
- Targets DeepSeek Harness `0.1.1-rc.2`; the `peerDependencies` pin the client
67
- packages and `@deepseek-ai/cordis@^4.0.1` the snapshot reads through. The
68
- service reads Cordis internals (`root.registry`, `root.reflect.store`, fiber
69
- fields) that are not part of the stable public API — verify the installed
76
+ The plugin uses its **own independent semantic version** — it does not mirror
77
+ the DeepSeek Harness version. New plugin features and bugfixes bump the plugin
78
+ version on their own schedule, independent of which DSH release it targets.
79
+ The table below maps each plugin version to the DSH release it was validated
80
+ against, so pick the plugin version whose target DSH matches your harness.
81
+
82
+ Targets DeepSeek Harness `0.1.2-rc.1`; the `peerDependencies` pin the client
83
+ packages and `@deepseek-ai/cordis@^4.0.2` the snapshot reads through. The
84
+ 0.1.2 rc line removed the old `dsh-client-runtime` browser runtime: the client
85
+ now runs on the Cordis `Context` augmented by the shell baseline renderer
86
+ (`dsh-client-ui-renderer` → `ctx.slots`), `dsh-client-store` (`defineStore`),
87
+ `dsh-client-locale` (`ctx.locale`), and `dsh-api-remotes` (`ctx.remote`).
88
+ The service reads Cordis internals (`root.registry`, `root.reflect.store`,
89
+ fiber fields) that are not part of the stable public API — verify the installed
70
90
  harness satisfies the peer ranges before enabling the tool or panel.
71
91
 
92
+ DSH itself has no beta channel — it publishes `alpha` then `rc` (its `latest`
93
+ dist-tag is stale, follow its `next`). This plugin adapts to stable DSH `rc`
94
+ releases only and skips the fast-moving `alpha` line.
95
+
96
+ | Plugin version (Git + npm) | Targets DSH harness | Notes |
97
+ | --- | --- | --- |
98
+ | `0.1.0` | `0.1.1-rc.2` | Old `dsh-client-runtime` browser model (frozen). |
99
+ | `0.2.0` | `0.1.2-rc.1` | Cordis-Context browser model; client-runtime removed. |
100
+
72
101
  ## Known Limitations
73
102
 
74
103
  - A future Cordis that reshapes the internal registry/reflect surfaces breaks
@@ -87,23 +116,25 @@ pnpm test # node:test over compiled specs
87
116
  pnpm run typecheck # noEmit check
88
117
  ```
89
118
 
90
- The `dsh.client` browser bundle inlines everything except `react` and
91
- `@deepseek-ai/dsh-client-runtime`, the two rows every harness shell serves.
119
+ The `dsh.client` browser bundle inlines everything except the shell's frozen
120
+ platform-module rows (`react`, `@deepseek-ai/cordis`, `dsh-client-store`, and
121
+ `dsh-client-ui-slots`), which every 0.1.2-rc.1 harness shell serves.
92
122
 
93
123
  See [NEXT-STEPS.md](NEXT-STEPS.md) for planned renderer improvements.
94
124
 
95
125
  ## Publishing
96
126
 
97
- `prepublishOnly` runs the build and tests. `publishConfig` pins `access:
98
- public` and the `registry.npmjs.org` target (machine-local pnpm may default to
99
- a read-only mirror), so:
127
+ The full end-to-end release checklist (version bump → build/test → commit/tag →
128
+ headless + browser smoke → publish) is in [`docs/RELEASING.md`](docs/RELEASING.md).
129
+ The final publish requires an interactive OTP, so it is run by hand:
100
130
 
101
131
  ```sh
102
- npm login # once, with the sleetdrop account
103
- npm publish # builds, tests, and publishes the tarball
132
+ pnpm publish --access public # prompts for your OTP
104
133
  ```
105
134
 
106
- If `npm publish` fails with an `EPERM` from the npm cache
135
+ `prepublishOnly` runs the build and tests. `publishConfig` pins `access:
136
+ public` and the `registry.npmjs.org` target (machine-local pnpm may default to
137
+ a read-only mirror). If `npm publish` fails with an `EPERM` from the npm cache
107
138
  (`root-owned files`), either fix the cache once
108
139
  (`sudo chown -R $(id -u):$(id -g) ~/.npm`) or publish through pnpm, whose
109
140
  store avoids the npm cache entirely:
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Local icon set for the topology viewer.
3
3
  *
4
- * The 0.1.1-rc.2 harness has no shared `dsh-client-ui-primitives` package, so
5
- * the plugin carries its own icons and controls. Each glyph is a self-contained
4
+ * The plugin carries its own icons and controls rather than depending on the
5
+ * shell's `dsh-client-ui-primitives` glyph set. Each glyph is a self-contained
6
6
  * SVG in the `currentColor` convention.
7
7
  * @module @sleetdrop/dsh-plugin-topology/client/icons
8
8
  */
@@ -25,7 +25,7 @@ export declare function IconDownloadOutline16({ size, className }: IconProps): R
25
25
  export declare function IconFullscreenOutline16({ size, className }: IconProps): ReactNode;
26
26
  /** Plugin-mark glyph for the footer trigger. */
27
27
  export declare function IconPluginOutline16({ size, className }: IconProps): ReactNode;
28
- /** Minimal outline button (the rc.2 harness ships no shared Button). */
28
+ /** Minimal outline button (the plugin keeps its own control set). */
29
29
  export declare function OutlineButton({ children, onClick, title, icon, }: {
30
30
  children: ReactNode;
31
31
  onClick: () => void;
@@ -27,7 +27,7 @@ export function IconFullscreenOutline16({ size = 16, className }) {
27
27
  export function IconPluginOutline16({ size = 16, className }) {
28
28
  return (_jsxs("svg", { width: size, height: size, className: className, viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [_jsx("circle", { cx: "8", cy: "8", r: "6.5", stroke: "currentColor", strokeWidth: "1.3" }), _jsx("circle", { cx: "5.6", cy: "5.6", r: "1.1", fill: "currentColor" }), _jsx("circle", { cx: "10.4", cy: "5.6", r: "1.1", fill: "currentColor" }), _jsx("circle", { cx: "8", cy: "9.4", r: "1.1", fill: "currentColor" }), _jsx("path", { d: "M5.6 5.6L8 9.4M10.4 5.6L8 9.4", stroke: "currentColor", strokeWidth: "1.1" })] }));
29
29
  }
30
- /** Minimal outline button (the rc.2 harness ships no shared Button). */
30
+ /** Minimal outline button (the plugin keeps its own control set). */
31
31
  export function OutlineButton({ children, onClick, title, icon, }) {
32
32
  const handle = (event) => {
33
33
  event.preventDefault();
@@ -4,14 +4,15 @@
4
4
  * fixed, centered panel that shows the live Host plugin dependency graph
5
5
  * (metrics, legend, unresolved-dependency log, zoom/pan and format downloads).
6
6
  *
7
- * The Host service is consumed through the generated `remote` namespace, whose
8
- * contribution this plugin mounts itself (`ctx.remote.$mount`). The mounted
9
- * namespace service is read through `ctx.get('remote.pluginTopology')` — the
10
- * Cordis no-inject read — because declaring it in `inject` would deadlock:
11
- * the service does not exist until this plugin's own apply mounts it.
7
+ * The Host service is consumed through the generated `remote` namespace. This
8
+ * package self-mounts its `pluginTopology` Remote contribution on `ctx.remote`
9
+ * (`$mount`), so it does not require a hand-edited host assembly contribution
10
+ * list. The namespace is read lazily inside the panel callbacks through the
11
+ * `ctx.get('remote.pluginTopology')` no-inject read — those closures run only
12
+ * after the user opens the panel, long after the mount settles.
12
13
  * @module @sleetdrop/dsh-plugin-topology/client
13
14
  */
14
- import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
15
+ import type { Context } from '@deepseek-ai/cordis';
15
16
  import { type PluginTopologyLocaleKey } from './locales.ts';
16
17
  declare module '@deepseek-ai/dsh-client-ui-slots' {
17
18
  interface LocaleNamespaceMap {
@@ -22,8 +23,8 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
22
23
  export type { TopologyViewInjected, TopologyViewProps } from './TopologyView.tsx';
23
24
  export type { PluginTopologyLocaleKey } from './locales.ts';
24
25
  export { TopologyPanel, type TopologyPanelProps } from './panel.tsx';
25
- /** Services required by the panel. The topology namespace is self-mounted and
26
- * read through ctx.get — see the module doc for the deadlock rationale. */
26
+ export { createTopologyViewStore, type ViewerHandle } from './stores.ts';
27
+ /** Required services: the slot registry, the locale runtime, and the Remote gateway. */
27
28
  export declare const inject: string[];
28
29
  /** Contribute the global topology panel trigger + panel to the sidebar footer. */
29
- export declare function apply(ctx: ClientContext): void;
30
+ export declare function apply(ctx: Context): void;
@@ -4,11 +4,12 @@
4
4
  * fixed, centered panel that shows the live Host plugin dependency graph
5
5
  * (metrics, legend, unresolved-dependency log, zoom/pan and format downloads).
6
6
  *
7
- * The Host service is consumed through the generated `remote` namespace, whose
8
- * contribution this plugin mounts itself (`ctx.remote.$mount`). The mounted
9
- * namespace service is read through `ctx.get('remote.pluginTopology')` — the
10
- * Cordis no-inject read — because declaring it in `inject` would deadlock:
11
- * the service does not exist until this plugin's own apply mounts it.
7
+ * The Host service is consumed through the generated `remote` namespace. This
8
+ * package self-mounts its `pluginTopology` Remote contribution on `ctx.remote`
9
+ * (`$mount`), so it does not require a hand-edited host assembly contribution
10
+ * list. The namespace is read lazily inside the panel callbacks through the
11
+ * `ctx.get('remote.pluginTopology')` no-inject read — those closures run only
12
+ * after the user opens the panel, long after the mount settles.
12
13
  * @module @sleetdrop/dsh-plugin-topology/client
13
14
  */
14
15
  // The generated Host Remote-descriptor contribution for this package.
@@ -17,32 +18,32 @@ import { createTopologyViewStore } from "./stores.js";
17
18
  import { TopologyPanel } from "./panel.js";
18
19
  import { en, NS, zh } from "./locales.js";
19
20
  export { TopologyPanel } from "./panel.js";
20
- /** Services required by the panel. The topology namespace is self-mounted and
21
- * read through ctx.get — see the module doc for the deadlock rationale. */
21
+ export { createTopologyViewStore } from "./stores.js";
22
+ /** Required services: the slot registry, the locale runtime, and the Remote gateway. */
22
23
  export const inject = ['slots', 'locale', 'remote'];
23
24
  /** Contribute the global topology panel trigger + panel to the sidebar footer. */
24
25
  export function apply(ctx) {
25
- // rc.2 locale API: per-locale registration (the typed dicts form needs a
26
- // LocaleNamespaceMap merge this package deliberately avoids).
27
- ctx.effect(() => {
28
- const unregisterEn = ctx.locale.register(NS, 'en', en);
29
- const unregisterZh = ctx.locale.register(NS, 'zh', zh);
30
- return () => {
31
- unregisterEn();
32
- unregisterZh();
33
- };
34
- }, 'ui-plugin-topology: dictionaries');
26
+ // rc.1 locale API: register the dictionary map object keyed by built-in
27
+ // locale id (the typed dicts form needs a LocaleNamespaceMap merge — the
28
+ // declare module above provides the `pluginTopology` namespace).
29
+ ctx.effect(() => ctx.locale.register(NS, { en, zh }), 'ui-plugin-topology: dictionaries');
35
30
  // Mount the package's own Remote contribution so `remote.pluginTopology`
36
- // becomes a live namespace service. The mounted service is read via
37
- // ctx.get (no-inject) inside the panel callbacks, which run only after the
38
- // user opens the panel — long after this mount has settled.
39
- ctx.effect(() => ctx.remote.$mount(topologyRemote).then(dispose => dispose, (error) => {
31
+ // becomes a live namespace service. The namespace is read via the
32
+ // `ctx.get('remote.pluginTopology')` no-inject read inside the panel
33
+ // callbacks, which run only after the user opens the panel — long after this
34
+ // mount has settled.
35
+ ctx.effect(() => ctx.remote.$mount(topologyRemote).then((dispose) => dispose, (error) => {
40
36
  console.error('[plugin-topology] remote contribution mount failed:', error);
41
37
  return () => { };
42
38
  }), 'ui-plugin-topology: mount remote contribution');
43
- const t = ctx.locale.bind(NS);
44
39
  const viewerStore = createTopologyViewStore();
45
40
  const namespaceOf = () => {
41
+ // No-inject read: the `remote.pluginTopology` namespace service is mounted
42
+ // by this plugin's own `$mount` above, so it cannot appear in `inject`
43
+ // (injecting a self-mounted dynamic service would deadlock — the service
44
+ // does not exist until this plugin's apply mounts it). `ctx.get` reads the
45
+ // store without the inject requirement; it resolves only once the mount's
46
+ // namespace fiber is ACTIVE, which the panel callbacks guarantee.
46
47
  const namespace = ctx.get('remote.pluginTopology');
47
48
  if (namespace === undefined) {
48
49
  throw new Error('pluginTopology remote namespace is not mounted yet — reopen the panel');
@@ -54,7 +55,10 @@ export function apply(ctx) {
54
55
  id: 'plugin-topology',
55
56
  order: 100,
56
57
  locale: NS,
57
- label: () => t('title'),
58
+ // Store seat persisted across panel remounts (the transform is written
59
+ // here so it survives closing/reopening the global panel). Declaring it
60
+ // makes the occupant receive `useStore` + bound `actions` props, and the
61
+ // `inject` factory below receives the same baked actions as its arg.
58
62
  store: viewerStore,
59
63
  inject: () => ({
60
64
  analyze: async () => {
@@ -1,4 +1,4 @@
1
- import { type EngineStoreHandle } from '@deepseek-ai/dsh-client-runtime/client';
1
+ import { type EngineStoreHandle } from '@deepseek-ai/dsh-client-store';
2
2
  /** The pan/zoom transform of the graph viewport. */
3
3
  export interface TopologyTransform {
4
4
  /** Horizontal translate in px. */
@@ -9,17 +9,18 @@ export interface TopologyTransform {
9
9
  k: number;
10
10
  }
11
11
  /** Viewer state: the remembered transform, or null until the first fit lands. */
12
- type ViewerState = {
12
+ export type ViewerState = {
13
13
  transform: TopologyTransform | null;
14
14
  };
15
- type ViewerActions = {
15
+ export type ViewerActions = {
16
16
  setTransform: (draft: ViewerState, transform: TopologyTransform | null) => void;
17
17
  };
18
+ /** The engine-backed viewer handle (spec + actions in one). */
19
+ export type ViewerHandle = EngineStoreHandle<ViewerState, ViewerActions>;
18
20
  /**
19
21
  * Create the shared root-scope viewer handle. The transform is written here so
20
22
  * it survives the panel's unmount/remount when the user closes and reopens the
21
23
  * global panel.
22
24
  * @returns the store handle (spec + type + identity + factory in one).
23
25
  */
24
- export declare function createTopologyViewStore(): EngineStoreHandle<ViewerState, ViewerActions>;
25
- export {};
26
+ export declare function createTopologyViewStore(): ViewerHandle;
@@ -1,4 +1,4 @@
1
- import { defineStore } from '@deepseek-ai/dsh-client-runtime/client';
1
+ import { defineStore } from '@deepseek-ai/dsh-client-store';
2
2
  /**
3
3
  * Create the shared root-scope viewer handle. The transform is written here so
4
4
  * it survives the panel's unmount/remount when the user closes and reopens the
@@ -1,7 +1,7 @@
1
1
  import type { RefObject } from 'react';
2
2
  /**
3
3
  * Close a popover when the pointer presses outside its root element.
4
- * Local implementation: the 0.1.1-rc.2 harness ships no shared dismiss hook.
4
+ * Local implementation: the plugin keeps its own dismiss hook.
5
5
  * @param ref - root element the interaction belongs to.
6
6
  * @param open - whether the popover is currently open.
7
7
  * @param setOpen - close state setter.
@@ -1,7 +1,7 @@
1
1
  import { useEffect } from 'react';
2
2
  /**
3
3
  * Close a popover when the pointer presses outside its root element.
4
- * Local implementation: the 0.1.1-rc.2 harness ships no shared dismiss hook.
4
+ * Local implementation: the plugin keeps its own dismiss hook.
5
5
  * @param ref - root element the interaction belongs to.
6
6
  * @param open - whether the popover is currently open.
7
7
  * @param setOpen - close state setter.
package/lib/client.js CHANGED
@@ -23,8 +23,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  }) : target, mod));
24
24
 
25
25
  //#endregion
26
- let __deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
27
- __deepseek_ai_dsh_client_runtime_client = __toESM(__deepseek_ai_dsh_client_runtime_client);
26
+ let __deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
27
+ __deepseek_ai_dsh_client_store = __toESM(__deepseek_ai_dsh_client_store);
28
28
  let react = require("react");
29
29
  react = __toESM(react);
30
30
  let react_jsx_runtime = require("react/jsx-runtime");
@@ -4450,7 +4450,7 @@ var remote_client_default = TYPERT_REMOTE;
4450
4450
  * @returns the store handle (spec + type + identity + factory in one).
4451
4451
  */
4452
4452
  function createTopologyViewStore() {
4453
- return (0, __deepseek_ai_dsh_client_runtime_client.defineStore)({
4453
+ return (0, __deepseek_ai_dsh_client_store.defineStore)({
4454
4454
  init: () => ({ transform: null }),
4455
4455
  actions: { setTransform: (d, transform$1) => {
4456
4456
  d.transform = transform$1;
@@ -4610,7 +4610,7 @@ function IconPluginOutline16({ size = 16, className }) {
4610
4610
  ]
4611
4611
  });
4612
4612
  }
4613
- /** Minimal outline button (the rc.2 harness ships no shared Button). */
4613
+ /** Minimal outline button (the plugin keeps its own control set). */
4614
4614
  function OutlineButton({ children, onClick, title, icon }) {
4615
4615
  const handle = (event) => {
4616
4616
  event.preventDefault();
@@ -5508,8 +5508,7 @@ const zh = {
5508
5508
 
5509
5509
  //#endregion
5510
5510
  //#region src/client/index.ts
5511
- /** Services required by the panel. The topology namespace is self-mounted and
5512
- * read through ctx.get — see the module doc for the deadlock rationale. */
5511
+ /** Required services: the slot registry, the locale runtime, and the Remote gateway. */
5513
5512
  const inject = [
5514
5513
  "slots",
5515
5514
  "locale",
@@ -5517,19 +5516,14 @@ const inject = [
5517
5516
  ];
5518
5517
  /** Contribute the global topology panel trigger + panel to the sidebar footer. */
5519
5518
  function apply(ctx) {
5520
- ctx.effect(() => {
5521
- const unregisterEn = ctx.locale.register(NS, "en", en);
5522
- const unregisterZh = ctx.locale.register(NS, "zh", zh);
5523
- return () => {
5524
- unregisterEn();
5525
- unregisterZh();
5526
- };
5527
- }, "ui-plugin-topology: dictionaries");
5519
+ ctx.effect(() => ctx.locale.register(NS, {
5520
+ en,
5521
+ zh
5522
+ }), "ui-plugin-topology: dictionaries");
5528
5523
  ctx.effect(() => ctx.remote.$mount(remote_client_default).then((dispose) => dispose, (error) => {
5529
5524
  console.error("[plugin-topology] remote contribution mount failed:", error);
5530
5525
  return () => {};
5531
5526
  }), "ui-plugin-topology: mount remote contribution");
5532
- const t = ctx.locale.bind(NS);
5533
5527
  const viewerStore = createTopologyViewStore();
5534
5528
  const namespaceOf = () => {
5535
5529
  const namespace = ctx.get("remote.pluginTopology");
@@ -5541,7 +5535,6 @@ function apply(ctx) {
5541
5535
  id: "plugin-topology",
5542
5536
  order: 100,
5543
5537
  locale: NS,
5544
- label: () => t("title"),
5545
5538
  store: viewerStore,
5546
5539
  inject: () => ({
5547
5540
  analyze: async () => {
@@ -5561,6 +5554,7 @@ function apply(ctx) {
5561
5554
  //#endregion
5562
5555
  exports.TopologyPanel = TopologyPanel;
5563
5556
  exports.apply = apply;
5557
+ exports.createTopologyViewStore = createTopologyViewStore;
5564
5558
  exports.inject = inject;
5565
5559
  return module.exports; } });
5566
5560
  //# sourceMappingURL=client.js.map