@devframes/hub 0.8.2 → 0.9.0-beta.10

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.
@@ -1,4 +1,4 @@
1
- import { DEFAULT_CATEGORIES_ORDER, DEFAULT_STATE_USER_SETTINGS } from "../constants.mjs";
1
+ import { DEFAULT_CATEGORIES_ORDER, DEFAULT_STATE_USER_SETTINGS, DOCK_RENDERERS_STATE_KEY } from "../constants.mjs";
2
2
  import { n as stripRemoteConnectionFromUrl, t as buildRemoteConnectionUrl } from "../remote-url-KVXtKP47.mjs";
3
3
  import { REMOTE_CONNECTION_KEY } from "devframe/constants";
4
4
  import { connectDevframe, getDevframeRpcClient, resolveWsUrl } from "devframe/client";
@@ -69,6 +69,88 @@ function resolveDockIcon(icon, connection) {
69
69
  };
70
70
  }
71
71
  //#endregion
72
+ //#region src/client/frame-location.ts
73
+ /**
74
+ * Track a frame's location until the returned disposer is called. Disposing
75
+ * detaches every listener and restores any wrapped `history` method, leaving the
76
+ * embedded page as it was found.
77
+ */
78
+ function watchFrameLocation(options) {
79
+ const { iframe, onChange } = options;
80
+ let lastReported = options.initial;
81
+ let detach;
82
+ let disposed = false;
83
+ /**
84
+ * The frame's window, or `null` when its location can't be read. Touching
85
+ * `location.href` is the cross-origin probe — it throws for a foreign
86
+ * document, where there is nothing to observe.
87
+ */
88
+ function readableWindow() {
89
+ try {
90
+ const win = iframe.contentWindow;
91
+ return win && typeof win.location.href === "string" ? win : null;
92
+ } catch {
93
+ return null;
94
+ }
95
+ }
96
+ function report() {
97
+ const href = readableWindow()?.location.href;
98
+ if (!href || href === "about:blank" || href === lastReported) return;
99
+ lastReported = href;
100
+ onChange(href);
101
+ }
102
+ function subscribe() {
103
+ const win = readableWindow();
104
+ if (!win) return;
105
+ const listeners = [];
106
+ win.addEventListener("popstate", report);
107
+ win.addEventListener("hashchange", report);
108
+ listeners.push(() => {
109
+ win.removeEventListener("popstate", report);
110
+ win.removeEventListener("hashchange", report);
111
+ });
112
+ const { navigation, history } = win;
113
+ if (navigation) {
114
+ navigation.addEventListener("currententrychange", report);
115
+ listeners.push(() => navigation.removeEventListener("currententrychange", report));
116
+ }
117
+ if (history) {
118
+ const originals = {
119
+ pushState: history.pushState,
120
+ replaceState: history.replaceState
121
+ };
122
+ for (const method of ["pushState", "replaceState"]) history[method] = function(...args) {
123
+ originals[method].apply(this, args);
124
+ report();
125
+ };
126
+ listeners.push(() => {
127
+ history.pushState = originals.pushState;
128
+ history.replaceState = originals.replaceState;
129
+ });
130
+ }
131
+ detach = () => {
132
+ for (const off of listeners) off();
133
+ };
134
+ }
135
+ function onLoad() {
136
+ if (disposed) return;
137
+ detach?.();
138
+ detach = void 0;
139
+ subscribe();
140
+ report();
141
+ }
142
+ iframe.addEventListener("load", onLoad);
143
+ subscribe();
144
+ report();
145
+ return () => {
146
+ if (disposed) return;
147
+ disposed = true;
148
+ iframe.removeEventListener("load", onLoad);
149
+ detach?.();
150
+ detach = void 0;
151
+ };
152
+ }
153
+ //#endregion
72
154
  //#region src/client/frame-nav.ts
73
155
  /**
74
156
  * Shared-iframe soft navigation — the viewer-side half of a host↔iframe
@@ -324,6 +406,97 @@ function createMessagesClient(rpc, options = {}) {
324
406
  };
325
407
  }
326
408
  //#endregion
409
+ //#region src/client/renderers.ts
410
+ /**
411
+ * Build the {@link DockRenderersContext} shared by every hub-aware client —
412
+ * `createDevframeClientHost` and viewers that assemble their own context
413
+ * (`@devframes/hub-ui`) both delegate here so local-first resolution, lazy
414
+ * manifest imports, and the typed mount result behave identically everywhere.
415
+ */
416
+ function createDockRenderersContext(options) {
417
+ const rendererMap = /* @__PURE__ */ new Map();
418
+ for (const [type, renderer] of Object.entries(options.local ?? {})) rendererMap.set(type, renderer);
419
+ const manifestImports = /* @__PURE__ */ new Map();
420
+ const manifestEntry = (type) => options.manifest?.()[type];
421
+ async function importManifestRenderer(type, script) {
422
+ const renderer = (await import(
423
+ /* @vite-ignore */
424
+ /* webpackIgnore: true */
425
+ /* turbopackIgnore: true */
426
+ script.importFrom
427
+ ))[script.importName ?? "default"];
428
+ return typeof renderer === "function" ? renderer : void 0;
429
+ }
430
+ async function resolveRenderer(type) {
431
+ const local = rendererMap.get(type);
432
+ if (local) return local;
433
+ const script = manifestEntry(type);
434
+ if (!script) return void 0;
435
+ let pending = manifestImports.get(type);
436
+ if (!pending) {
437
+ pending = importManifestRenderer(type, script);
438
+ manifestImports.set(type, pending);
439
+ pending.catch(() => manifestImports.delete(type));
440
+ }
441
+ const renderer = await pending;
442
+ if (renderer && !rendererMap.has(type)) rendererMap.set(type, renderer);
443
+ return renderer;
444
+ }
445
+ return {
446
+ register(type, renderer) {
447
+ rendererMap.set(type, renderer);
448
+ return () => {
449
+ if (rendererMap.get(type) === renderer) rendererMap.delete(type);
450
+ };
451
+ },
452
+ get: (type) => rendererMap.get(type),
453
+ has: (type) => rendererMap.has(type) || manifestEntry(type) !== void 0,
454
+ async mount(entry, container) {
455
+ let renderer;
456
+ try {
457
+ renderer = await resolveRenderer(entry.type);
458
+ } catch (error) {
459
+ console.error(`[@devframes/hub] failed to load the renderer module for dock type "${entry.type}"`, error);
460
+ return {
461
+ status: "load-error",
462
+ error
463
+ };
464
+ }
465
+ if (!renderer) {
466
+ console.warn(`[@devframes/hub] no renderer registered for dock type "${entry.type}" (entry "${entry.id}")`);
467
+ return { status: "missing-renderer" };
468
+ }
469
+ let instance;
470
+ try {
471
+ instance = await renderer({
472
+ entry,
473
+ container,
474
+ context: options.context()
475
+ });
476
+ } catch (error) {
477
+ console.error(`[@devframes/hub] renderer for dock type "${entry.type}" failed to mount (entry "${entry.id}")`, error);
478
+ return {
479
+ status: "load-error",
480
+ error
481
+ };
482
+ }
483
+ let disposed = false;
484
+ let cleanup;
485
+ const dispose = () => {
486
+ if (disposed) return;
487
+ disposed = true;
488
+ cleanup?.();
489
+ instance.dispose?.();
490
+ };
491
+ cleanup = options.onMounted?.(dispose, entry);
492
+ return {
493
+ status: "mounted",
494
+ dispose
495
+ };
496
+ }
497
+ };
498
+ }
499
+ //#endregion
327
500
  //#region src/client/host.ts
328
501
  const DOCKS_STATE_KEY = "devframe:docks";
329
502
  const COMMANDS_STATE_KEY = "devframe:commands";
@@ -343,16 +516,21 @@ async function createDevframeClientHost(options = {}) {
343
516
  const clientType = options.clientType ?? "standalone";
344
517
  const rpc = options.rpc ?? await connectDevframe(options.connect);
345
518
  let mountedRenderers;
346
- const [docksState, commandsState, settings] = await Promise.all([
519
+ const [docksState, commandsState, settings, renderersManifestState] = await Promise.all([
347
520
  rpc.sharedState.get(DOCKS_STATE_KEY, { initialValue: [] }),
348
521
  rpc.sharedState.get(COMMANDS_STATE_KEY, { initialValue: [] }),
349
- rpc.sharedState.get(USER_SETTINGS_STATE_KEY, { initialValue: DEFAULT_STATE_USER_SETTINGS() })
522
+ rpc.sharedState.get(USER_SETTINGS_STATE_KEY, { initialValue: DEFAULT_STATE_USER_SETTINGS() }),
523
+ rpc.sharedState.get(DOCK_RENDERERS_STATE_KEY, { initialValue: {} })
350
524
  ]);
351
525
  let selectedId = null;
352
526
  const entryToStateMap = /* @__PURE__ */ new Map();
353
527
  const clientDocks = /* @__PURE__ */ new Map();
354
528
  const frameNavAdapters = /* @__PURE__ */ new Map();
355
529
  const loadScriptsEnabled = options.loadClientScripts ?? true;
530
+ const categoryOrder = {
531
+ ...DEFAULT_CATEGORIES_ORDER,
532
+ ...options.categoryOrder
533
+ };
356
534
  const panel = createPanelContext(clientType);
357
535
  const docks = createDocksContext();
358
536
  const commands = createCommandsContext();
@@ -366,7 +544,7 @@ async function createDevframeClientHost(options = {}) {
366
544
  when: { get context() {
367
545
  return {
368
546
  clientType,
369
- dockOpen: panel.store.open,
547
+ dockOpen: panel.session.open,
370
548
  paletteOpen: commands.paletteOpen,
371
549
  dockSelectedId: selectedId ?? ""
372
550
  };
@@ -487,8 +665,12 @@ async function createDevframeClientHost(options = {}) {
487
665
  }
488
666
  }
489
667
  docks.entries = entries;
490
- docks.groupedEntries = groupByCategory(entries);
491
- if (selectedId && !entryToStateMap.has(selectedId)) selectedId = null;
668
+ docks.groupedEntries = groupByCategory(entries, categoryOrder);
669
+ if (selectedId && !entryToStateMap.has(selectedId)) {
670
+ selectedId = null;
671
+ panel.session.selectedDockId = null;
672
+ panel.session.selectedDockRoute = null;
673
+ }
492
674
  }
493
675
  function createDocksContext() {
494
676
  return {
@@ -498,12 +680,19 @@ async function createDevframeClientHost(options = {}) {
498
680
  set selectedId(id) {
499
681
  switchEntry(id);
500
682
  },
683
+ get selectedRoute() {
684
+ return panel.session.selectedDockRoute;
685
+ },
686
+ set selectedRoute(route) {
687
+ panel.session.selectedDockRoute = route;
688
+ },
501
689
  get selected() {
502
690
  return selectedId && entryToStateMap.get(selectedId)?.entryMeta || null;
503
691
  },
504
692
  entries: [],
505
693
  entryToStateMap,
506
694
  groupedEntries: [],
695
+ categoryOrder,
507
696
  settings,
508
697
  getStateById: (id) => entryToStateMap.get(id),
509
698
  switchEntry,
@@ -541,6 +730,8 @@ async function createDevframeClientHost(options = {}) {
541
730
  if (next !== null && !entryToStateMap.has(next)) return false;
542
731
  const previous = selectedId;
543
732
  selectedId = next;
733
+ panel.session.selectedDockId = next;
734
+ if (next === null || entryToStateMap.get(next)?.entryMeta.type !== "iframe") panel.session.selectedDockRoute = null;
544
735
  if (previous) entryToStateMap.get(previous)?.events.emit("entry:deactivated");
545
736
  if (next) entryToStateMap.get(next)?.events.emit("entry:activated");
546
737
  return true;
@@ -579,44 +770,21 @@ async function createDevframeClientHost(options = {}) {
579
770
  };
580
771
  }
581
772
  function createRenderersContext() {
582
- const rendererMap = /* @__PURE__ */ new Map();
583
- for (const [type, renderer] of Object.entries(options.renderers ?? {})) rendererMap.set(type, renderer);
584
773
  const mountedDisposers = /* @__PURE__ */ new Set();
585
774
  mountedRenderers = mountedDisposers;
586
- return {
587
- register(type, renderer) {
588
- rendererMap.set(type, renderer);
775
+ return createDockRenderersContext({
776
+ context: () => context,
777
+ ...options.renderers ? { local: options.renderers } : {},
778
+ manifest: () => renderersManifestState.value(),
779
+ onMounted(dispose, entry) {
780
+ mountedDisposers.add(dispose);
781
+ const offDeactivate = entryToStateMap.get(entry.id)?.events.on("entry:deactivated", dispose);
589
782
  return () => {
590
- if (rendererMap.get(type) === renderer) rendererMap.delete(type);
591
- };
592
- },
593
- get: (type) => rendererMap.get(type),
594
- has: (type) => rendererMap.has(type),
595
- async mount(entry, container) {
596
- const renderer = rendererMap.get(entry.type);
597
- if (!renderer) {
598
- console.warn(`[@devframes/hub] no renderer registered for dock type "${entry.type}" (entry "${entry.id}")`);
599
- return () => {};
600
- }
601
- const instance = await renderer({
602
- entry,
603
- container,
604
- context
605
- });
606
- let disposed = false;
607
- let offDeactivate;
608
- const dispose = () => {
609
- if (disposed) return;
610
- disposed = true;
611
783
  mountedDisposers.delete(dispose);
612
784
  offDeactivate?.();
613
- instance.dispose?.();
614
785
  };
615
- mountedDisposers.add(dispose);
616
- offDeactivate = entryToStateMap.get(entry.id)?.events.on("entry:deactivated", dispose);
617
- return dispose;
618
786
  }
619
- };
787
+ });
620
788
  }
621
789
  function clientScriptOf(entry) {
622
790
  return entry.action ?? entry.renderer ?? entry.clientScript;
@@ -660,11 +828,15 @@ function createPanelContext(clientType) {
660
828
  top: 0,
661
829
  left: 0,
662
830
  position: "right",
663
- open: clientType === "standalone",
664
831
  inactiveTimeout: 0
665
832
  };
666
833
  return {
667
834
  store,
835
+ session: {
836
+ open: clientType === "standalone",
837
+ selectedDockId: null,
838
+ selectedDockRoute: null
839
+ },
668
840
  isDragging: false,
669
841
  isResizing: false,
670
842
  get isVertical() {
@@ -672,7 +844,7 @@ function createPanelContext(clientType) {
672
844
  }
673
845
  };
674
846
  }
675
- function groupByCategory(entries) {
847
+ function groupByCategory(entries, categoryOrder) {
676
848
  const groupsById = /* @__PURE__ */ new Map();
677
849
  for (const entry of entries) if (entry.type === "group") groupsById.set(entry.id, entry);
678
850
  const groups = /* @__PURE__ */ new Map();
@@ -686,7 +858,7 @@ function groupByCategory(entries) {
686
858
  }
687
859
  list.push(entry);
688
860
  }
689
- return [...groups.entries()].sort(([a], [b]) => (DEFAULT_CATEGORIES_ORDER[a] ?? 0) - (DEFAULT_CATEGORIES_ORDER[b] ?? 0));
861
+ return [...groups.entries()].sort(([a], [b]) => (categoryOrder[a] ?? 0) - (categoryOrder[b] ?? 0));
690
862
  }
691
863
  //#endregion
692
864
  //#region src/client/remote.ts
@@ -795,4 +967,4 @@ async function connectRemoteDevframe(options = {}) {
795
967
  });
796
968
  }
797
969
  //#endregion
798
- export { CLIENT_CONTEXT_KEY, DEFAULT_CATEGORIES_ORDER, FRAME_NAV_CHANNEL, FRAME_NAV_VERSION, attachFrameNavClient, buildRemoteDevframeUrl, connectRemoteDevframe, createDevframeClientHost, createMessagesClient, getDevframeClientContext, parseRemoteConnection, resolveDockIcon, resolveDockUrl, setDevframeClientContext, stripRemoteConnectionFromUrl };
970
+ export { CLIENT_CONTEXT_KEY, FRAME_NAV_CHANNEL, FRAME_NAV_VERSION, attachFrameNavClient, buildRemoteDevframeUrl, connectRemoteDevframe, createDevframeClientHost, createDockRenderersContext, createMessagesClient, getDevframeClientContext, parseRemoteConnection, resolveDockIcon, resolveDockUrl, setDevframeClientContext, stripRemoteConnectionFromUrl, watchFrameLocation };
@@ -1,6 +1,16 @@
1
- import { t as DevframeDocksUserSettings } from "./settings-Byh48aCd.mjs";
1
+ import { t as DevframeDocksUserSettings } from "./settings-DfrRa9Wf.mjs";
2
2
  export * from "devframe/constants";
3
3
  //#region src/constants.d.ts
4
+ /** Default mount base for a hub instance — one namespace, one catch-all. */
5
+ declare const DEVFRAMES_HUB_BASE = "/__devframes/";
6
+ /**
7
+ * Normalize a hub mount base to an absolute path with leading and trailing
8
+ * slashes (e.g. `devframes` → `/devframes/`), collapsing any doubled
9
+ * slashes the input introduced. The one implementation every hub-aware
10
+ * host (`@devframes/hub` itself, and the Vite/Nuxt/Next adapters) resolves
11
+ * `options.base` through.
12
+ */
13
+ declare function normalizeHubBase(base: string): string;
4
14
  /**
5
15
  * The default ordering weight for each known dock category — lower sorts
6
16
  * earlier. Downstream viewers (e.g. `@vitejs/devtools-kit`) import this as the
@@ -12,6 +22,13 @@ export * from "devframe/constants";
12
22
  * its own categories (or override these) without editing this table.
13
23
  */
14
24
  declare const DEFAULT_CATEGORIES_ORDER: Record<string, number>;
25
+ /**
26
+ * Shared-state slot carrying the hub's renderer manifest — one
27
+ * {@link import('./client/renderers').DockRendererManifest} entry per dock
28
+ * `type`, published by `initHub({ renderers })` and consumed by every
29
+ * hub-aware client (the headless client host and viewers alike).
30
+ */
31
+ declare const DOCK_RENDERERS_STATE_KEY = "devframe:dock-renderers";
15
32
  declare const DEFAULT_STATE_USER_SETTINGS: () => DevframeDocksUserSettings;
16
33
  //#endregion
17
- export { DEFAULT_CATEGORIES_ORDER, DEFAULT_STATE_USER_SETTINGS };
34
+ export { DEFAULT_CATEGORIES_ORDER, DEFAULT_STATE_USER_SETTINGS, DEVFRAMES_HUB_BASE, DOCK_RENDERERS_STATE_KEY, normalizeHubBase };
@@ -1,5 +1,18 @@
1
+ import { cleanDoubleSlashes, withLeadingSlash, withTrailingSlash } from "ufo";
1
2
  export * from "devframe/constants";
2
3
  //#region src/constants.ts
4
+ /** Default mount base for a hub instance — one namespace, one catch-all. */
5
+ const DEVFRAMES_HUB_BASE = "/__devframes/";
6
+ /**
7
+ * Normalize a hub mount base to an absolute path with leading and trailing
8
+ * slashes (e.g. `devframes` → `/devframes/`), collapsing any doubled
9
+ * slashes the input introduced. The one implementation every hub-aware
10
+ * host (`@devframes/hub` itself, and the Vite/Nuxt/Next adapters) resolves
11
+ * `options.base` through.
12
+ */
13
+ function normalizeHubBase(base) {
14
+ return cleanDoubleSlashes(withTrailingSlash(withLeadingSlash(base)));
15
+ }
3
16
  /**
4
17
  * The default ordering weight for each known dock category — lower sorts
5
18
  * earlier. Downstream viewers (e.g. `@vitejs/devtools-kit`) import this as the
@@ -22,14 +35,19 @@ const DEFAULT_CATEGORIES_ORDER = {
22
35
  "docs": 500,
23
36
  "~builtin": 1e3
24
37
  };
38
+ /**
39
+ * Shared-state slot carrying the hub's renderer manifest — one
40
+ * {@link import('./client/renderers').DockRendererManifest} entry per dock
41
+ * `type`, published by `initHub({ renderers })` and consumed by every
42
+ * hub-aware client (the headless client host and viewers alike).
43
+ */
44
+ const DOCK_RENDERERS_STATE_KEY = "devframe:dock-renderers";
25
45
  const DEFAULT_STATE_USER_SETTINGS = () => ({
26
46
  docksHidden: [],
27
47
  docksCategoriesHidden: [],
28
48
  docksPinned: [],
29
49
  docksCustomOrder: {},
30
- showIframeAddressBar: false,
31
- closeOnOutsideClick: false,
32
50
  commandShortcuts: {}
33
51
  });
34
52
  //#endregion
35
- export { DEFAULT_CATEGORIES_ORDER, DEFAULT_STATE_USER_SETTINGS };
53
+ export { DEFAULT_CATEGORIES_ORDER, DEFAULT_STATE_USER_SETTINGS, DEVFRAMES_HUB_BASE, DOCK_RENDERERS_STATE_KEY, normalizeHubBase };
@@ -1,6 +1,6 @@
1
- import { C as DevframeDocksHost, L as JsonRenderSpec, R as JsonRenderer, l as DevframeCommandsHost, m as DevframeDockActivation, y as DevframeDockEntryIcon } from "./settings-Byh48aCd.mjs";
1
+ import { b as DevframeDockEntryIcon, k as DevframeViewIframe, l as DevframeCommandsHost, m as DevframeDockActivation, w as DevframeDocksHost } from "./settings-DfrRa9Wf.mjs";
2
2
  import { CreateHostContextOptions } from "devframe/node";
3
- import { DevframeHost, DevframeNodeContext, EventEmitter } from "devframe/types";
3
+ import { DevframeDefinition, DevframeHost, DevframeNodeContext, EventEmitter } from "devframe/types";
4
4
  import { ChildProcess } from "node:child_process";
5
5
  //#region src/types/messages.d.ts
6
6
  type DevframeMessageLevel = 'info' | 'warn' | 'error' | 'success' | 'debug';
@@ -47,7 +47,25 @@ interface DevframeMessageActivateAction {
47
47
  params?: Record<string, unknown>;
48
48
  };
49
49
  }
50
- type DevframeMessageAction = DevframeMessageActivateAction;
50
+ /**
51
+ * `'command'` invokes a command from the hub's command registry (the same
52
+ * registry backing the command palette) by `command.id`, spreading
53
+ * `command.params` as its positional arguments, via the hub's
54
+ * `hub:commands:execute` RPC.
55
+ */
56
+ interface DevframeMessageCommandAction {
57
+ /** Stable id for the action within its entry. */
58
+ id: string;
59
+ /** Button label shown in the messages panel. */
60
+ label: string;
61
+ kind: 'command';
62
+ /** The command to invoke, plus an optional list of positional arguments. */
63
+ command: {
64
+ id: string;
65
+ params?: unknown[];
66
+ };
67
+ }
68
+ type DevframeMessageAction = DevframeMessageActivateAction | DevframeMessageCommandAction;
51
69
  interface DevframeMessageEntry {
52
70
  /**
53
71
  * Unique identifier for this message entry (auto-generated if not provided)
@@ -347,6 +365,23 @@ interface DevframePtyTerminalSession extends DevframeTerminalSession {
347
365
  restart: () => Promise<void>;
348
366
  }
349
367
  //#endregion
368
+ //#region src/node/install-devframe.d.ts
369
+ interface InstallDevframeOptions {
370
+ /**
371
+ * Mount path override. Defaults to `d.basePath` or `/__${d.id}/`.
372
+ */
373
+ base?: string;
374
+ /**
375
+ * Per-mount overrides for the auto-synthesized iframe dock entry. Use
376
+ * this to customize the entry's `category`, override the icon, hide it
377
+ * via `when` (or only its dock-bar button via `visibility`), etc. Takes
378
+ * precedence over the definition's own {@link DevframeDefinition.dock}
379
+ * defaults. Cannot change `id`, `type`, or `url` — those are derived from
380
+ * the devframe definition.
381
+ */
382
+ dock?: Partial<Omit<DevframeViewIframe, 'id' | 'type' | 'url'>>;
383
+ }
384
+ //#endregion
350
385
  //#region src/node/context.d.ts
351
386
  declare module 'devframe/types' {
352
387
  interface DevframeRpcClientFunctions {
@@ -421,17 +456,15 @@ declare module 'devframe/types' {
421
456
  /**
422
457
  * Hub-augmented node context — extends devframe's framework-neutral
423
458
  * `DevframeNodeContext` with the hub-level subsystems (`docks`,
424
- * `terminals`, `messages`, `commands`) and the deprecated
425
- * `createJsonRenderer` compatibility factory.
459
+ * `terminals`, `messages`, `commands`).
426
460
  *
427
461
  * Framework kits further extend this with their own slots (e.g.
428
462
  * `viteConfig`, `viteServer`). Host-specific capabilities (editor open,
429
463
  * filesystem reveal, etc.) ship as kit-registered RPC functions rather
430
- * than as part of this surface. JSON-render itself is not part of the hub:
431
- * it is an opt-in integration (`@devframes/json-render`) that augments any
432
- * devframe context and contributes its own dock type — prefer
433
- * `createJsonRenderView` from `@devframes/json-render/node` over the
434
- * deprecated factory below.
464
+ * than as part of this surface. JSON-render is an opt-in integration
465
+ * (`@devframes/json-render`) that augments any devframe context and
466
+ * contributes its own dock type — use `createJsonRenderView` from
467
+ * `@devframes/json-render/node`.
435
468
  */
436
469
  interface DevframeHubContext extends DevframeNodeContext {
437
470
  readonly host: DevframeHost;
@@ -440,15 +473,13 @@ interface DevframeHubContext extends DevframeNodeContext {
440
473
  messages: DevframeMessagesHost;
441
474
  commands: DevframeCommandsHost;
442
475
  /**
443
- * Create a `JsonRenderer` handle for building json-render powered UIs.
444
- *
445
- * @deprecated json-render moved out of the hub into the opt-in
446
- * `@devframes/json-render` integration in 0.7. This factory is kept
447
- * working (not just type-compatible) for the 0.7 series so existing call
448
- * sites don't break — use `createJsonRenderView(ctx, { id, spec })` from
449
- * `@devframes/json-render/node` instead. Will be removed in 0.8.
476
+ * Install a {@link DevframeDefinition} into this hub: serve its SPA at the
477
+ * resolved base, synthesize an iframe dock from its metadata, and run its
478
+ * `setup(ctx)`. The imperative counterpart to `initHub`'s declarative
479
+ * `devframes` list — call it from a hub host's `configure(ctx)`, or wherever
480
+ * you hold the context, to plug an extra devframe in.
450
481
  */
451
- createJsonRenderer: (spec: JsonRenderSpec) => JsonRenderer;
482
+ install: (devframe: DevframeDefinition, options?: InstallDevframeOptions) => Promise<void>;
452
483
  }
453
484
  /**
454
485
  * Options for {@link createHubContext} — devframe's
@@ -464,4 +495,4 @@ interface CreateHubContextOptions extends CreateHostContextOptions {}
464
495
  */
465
496
  declare function createHubContext(options: CreateHubContextOptions): Promise<DevframeHubContext>;
466
497
  //#endregion
467
- export { DevframeMessageShortcutInput as C, DevframeMessagesListDelta as D, DevframeMessagesLevelShortcuts as E, DevframeMessageLevel as S, DevframeMessagesHost as T, DevframeMessageEntry as _, DevframeChildProcessOutput as a, DevframeMessageFilePosition as b, DevframePtyExecuteOptions as c, DevframeTerminalSessionBase as d, DevframeTerminalStatus as f, DevframeMessageElementPosition as g, DevframeMessageActivateAction as h, DevframeChildProcessExecuteOptions as i, DevframePtyTerminalSession as l, DevframeMessageAction as m, DevframeHubContext as n, DevframeChildProcessResult as o, DevframeTerminalsHost as p, createHubContext as r, DevframeChildProcessTerminalSession as s, CreateHubContextOptions as t, DevframeTerminalSession as u, DevframeMessageEntryFrom as v, DevframeMessagesClient as w, DevframeMessageHandle as x, DevframeMessageEntryInput as y };
498
+ export { DevframeMessageHandle as C, DevframeMessagesHost as D, DevframeMessagesClient as E, DevframeMessagesLevelShortcuts as O, DevframeMessageFilePosition as S, DevframeMessageShortcutInput as T, DevframeMessageCommandAction as _, DevframeChildProcessExecuteOptions as a, DevframeMessageEntryFrom as b, DevframeChildProcessTerminalSession as c, DevframeTerminalSession as d, DevframeTerminalSessionBase as f, DevframeMessageActivateAction as g, DevframeMessageAction as h, InstallDevframeOptions as i, DevframeMessagesListDelta as k, DevframePtyExecuteOptions as l, DevframeTerminalsHost as m, DevframeHubContext as n, DevframeChildProcessOutput as o, DevframeTerminalStatus as p, createHubContext as r, DevframeChildProcessResult as s, CreateHubContextOptions as t, DevframePtyTerminalSession as u, DevframeMessageElementPosition as v, DevframeMessageLevel as w, DevframeMessageEntryInput as x, DevframeMessageEntry as y };