@opetope/devtools 0.1.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/CHANGELOG.md +5 -0
- package/LICENSE +21 -0
- package/README.md +105 -0
- package/README.ru.md +105 -0
- package/dist/activity-export.d.ts +4 -0
- package/dist/activity.d.ts +63 -0
- package/dist/conformance-activity.d.ts +8 -0
- package/dist/conformance.d.ts +37 -0
- package/dist/control-subject.d.ts +15 -0
- package/dist/control-ui-DMmVy9Ob.js +16 -0
- package/dist/control-ui-DMmVy9Ob.js.map +1 -0
- package/dist/control.d.ts +45 -0
- package/dist/corpus.d.ts +33 -0
- package/dist/decode-activity.d.ts +4 -0
- package/dist/decode-shapes.d.ts +32 -0
- package/dist/decode-values.d.ts +36 -0
- package/dist/decode.d.ts +41 -0
- package/dist/digest.d.ts +17 -0
- package/dist/export-run-DIQZax_d.js +2 -0
- package/dist/export-run-DIQZax_d.js.map +1 -0
- package/dist/export-snapshot-Buw-c6ZG.js +2 -0
- package/dist/export-snapshot-Buw-c6ZG.js.map +1 -0
- package/dist/export-snapshot.d.ts +34 -0
- package/dist/extension-points.d.ts +45 -0
- package/dist/fixture-plan.d.ts +62 -0
- package/dist/fixture-runtime.d.ts +37 -0
- package/dist/fixture.d.ts +23 -0
- package/dist/fold-snapshot-DjiURn8v.js +2 -0
- package/dist/fold-snapshot-DjiURn8v.js.map +1 -0
- package/dist/fold-snapshot.d.ts +15 -0
- package/dist/graph-index.d.ts +104 -0
- package/dist/graph-store.d.ts +77 -0
- package/dist/graph.d.ts +211 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/inspector.d.ts +70 -0
- package/dist/install.d.ts +44 -0
- package/dist/install.js +2 -0
- package/dist/install.js.map +1 -0
- package/dist/integration.d.ts +42 -0
- package/dist/integration.js +2 -0
- package/dist/integration.js.map +1 -0
- package/dist/legend-DT7j0_pD.js +2 -0
- package/dist/legend-DT7j0_pD.js.map +1 -0
- package/dist/node-facts.d.ts +20 -0
- package/dist/node-state.d.ts +152 -0
- package/dist/optional.d.ts +9 -0
- package/dist/panel-5jvisQBr.js +367 -0
- package/dist/panel-5jvisQBr.js.map +1 -0
- package/dist/producer-ids.d.ts +15 -0
- package/dist/protocol-joKTjlMj.js +2 -0
- package/dist/protocol-joKTjlMj.js.map +1 -0
- package/dist/protocol.d.ts +238 -0
- package/dist/react/activity-rows.d.ts +12 -0
- package/dist/react/activity-view.d.ts +9 -0
- package/dist/react/control-locale.d.ts +25 -0
- package/dist/react/control-styles.d.ts +7 -0
- package/dist/react/control-ui.d.ts +37 -0
- package/dist/react/edge-filter.d.ts +36 -0
- package/dist/react/edge-filters.d.ts +17 -0
- package/dist/react/export-delivery.d.ts +33 -0
- package/dist/react/export-run.d.ts +20 -0
- package/dist/react/format.d.ts +17 -0
- package/dist/react/glyph.d.ts +20 -0
- package/dist/react/graph-layout.d.ts +67 -0
- package/dist/react/graph-view.d.ts +30 -0
- package/dist/react/graph-viewport.d.ts +48 -0
- package/dist/react/icons.d.ts +12 -0
- package/dist/react/inspector-view.d.ts +29 -0
- package/dist/react/label.d.ts +28 -0
- package/dist/react/lazy-module.d.ts +38 -0
- package/dist/react/legend-locale.d.ts +23 -0
- package/dist/react/legend.d.ts +10 -0
- package/dist/react/locale-context.d.ts +17 -0
- package/dist/react/locale.d.ts +115 -0
- package/dist/react/main-column.d.ts +22 -0
- package/dist/react/panel-header.d.ts +20 -0
- package/dist/react/panel-mode.d.ts +3 -0
- package/dist/react/panel-status.d.ts +19 -0
- package/dist/react/panel.d.ts +41 -0
- package/dist/react/search-results.d.ts +18 -0
- package/dist/react/selection.d.ts +15 -0
- package/dist/react/styles.d.ts +13 -0
- package/dist/react/theme.d.ts +39 -0
- package/dist/react/timeline-locale.d.ts +20 -0
- package/dist/react/timeline-view.d.ts +14 -0
- package/dist/react/tree.d.ts +26 -0
- package/dist/react/use-store.d.ts +11 -0
- package/dist/react.d.ts +16 -0
- package/dist/react.js +2 -0
- package/dist/react.js.map +1 -0
- package/dist/search-CC7avy6I.js +2 -0
- package/dist/search-CC7avy6I.js.map +1 -0
- package/dist/search.d.ts +20 -0
- package/dist/session-qjKT0E69.js +2 -0
- package/dist/session-qjKT0E69.js.map +1 -0
- package/dist/session.d.ts +59 -0
- package/dist/testing.d.ts +10 -0
- package/dist/testing.js +4 -0
- package/dist/testing.js.map +1 -0
- package/dist/timeline-view-ByPB1a7-.js +2 -0
- package/dist/timeline-view-ByPB1a7-.js.map +1 -0
- package/dist/timeline.d.ts +57 -0
- package/dist/visual-axes.d.ts +35 -0
- package/package.json +107 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The inspector of `docs/devtools.md` §4: axis tags, key/value grid, "why this state", blockers, owned
|
|
3
|
+
* nodes and the last causal chain, over a scrolling body with a pinned action footer.
|
|
4
|
+
*
|
|
5
|
+
* Every sentence here is composed from the typed facts of `InspectorProjection`. The panel decides wording; it never
|
|
6
|
+
* receives prose, parses an error, or infers a state from a colour (§2).
|
|
7
|
+
*/
|
|
8
|
+
import type { ReactElement, ReactNode } from 'react';
|
|
9
|
+
import type { InspectorProjection } from '../inspector.js';
|
|
10
|
+
import type { DiagnosticConditionId, DiagnosticNodeId } from '../protocol.js';
|
|
11
|
+
type InspectorViewProps = Readonly<{
|
|
12
|
+
/** The last outcome of an action, shown next to the buttons so an export or a copy is visibly not a no-op. */
|
|
13
|
+
actionStatus?: string;
|
|
14
|
+
/** Resolves the atom a cause names to the declaration id the author wrote (D200). */
|
|
15
|
+
conditionLabelOf: ConditionLabel;
|
|
16
|
+
/** The control slot of §5, when a host passed a write port. Nothing at all otherwise. */
|
|
17
|
+
control: ReactNode;
|
|
18
|
+
/** Resolves a causal step's node ref to its label. Labels live on nodes, not repeated on every step. */
|
|
19
|
+
labelOf: (id: DiagnosticNodeId) => string;
|
|
20
|
+
onCopyId: (id: DiagnosticNodeId) => void;
|
|
21
|
+
onExport: () => void;
|
|
22
|
+
onSelect: (id: DiagnosticNodeId) => void;
|
|
23
|
+
onShowInTimeline: () => void;
|
|
24
|
+
projection: InspectorProjection;
|
|
25
|
+
}>;
|
|
26
|
+
/** How a view turns the atom a condition is addressed by into the name a reader recognises. */
|
|
27
|
+
type ConditionLabel = (id: DiagnosticConditionId) => string;
|
|
28
|
+
declare function InspectorView({ actionStatus, conditionLabelOf, control, labelOf, onCopyId, onExport, onSelect, onShowInTimeline, projection, }: InspectorViewProps): ReactElement;
|
|
29
|
+
export { InspectorView };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fitting a node's name into a card of fixed width without losing the part that tells it apart (§4.3).
|
|
3
|
+
*
|
|
4
|
+
* A graph id is a path, and the tail is what distinguishes siblings: `sample.dev-mode` and `sample.dev-mode.settings`
|
|
5
|
+
* differ only after the last dot, which is exactly what a CSS ellipsis on the right throws away first. So the panel
|
|
6
|
+
* shortens on purpose instead of letting the box do it — relative to the selection where that is possible, and from
|
|
7
|
+
* the middle where it is not — and hands the full id to `title`, so hovering always answers what the card is.
|
|
8
|
+
*
|
|
9
|
+
* The budgets are estimates, not measurements: a card is 188 px wide with 12 px of padding either side and a glyph
|
|
10
|
+
* ahead of the text, which leaves roughly 148 px, and the panel's font runs about 6.3 px per character at 12 px and
|
|
11
|
+
* 7.3 px at 14 px. CSS truncation stays as the backstop for the cases arithmetic gets wrong.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* A neighbour written relative to the selected node: `sample.dev-mode.settings` beside `sample.dev-mode` becomes
|
|
15
|
+
* `…settings`. Only a true prefix counts, and only at a dot, so `sample.dev-modes` is left alone.
|
|
16
|
+
*/
|
|
17
|
+
declare function relativeTo(label: string, selected: string | undefined): string;
|
|
18
|
+
/**
|
|
19
|
+
* Middle truncation that keeps the last segment whole: `features.sessions.confirmAction` becomes
|
|
20
|
+
* `features….confirmAction` rather than `features.sessions.conf…`. When the last segment alone does not fit, the
|
|
21
|
+
* cut moves into it from the left, because its end is still the most distinguishing thing left.
|
|
22
|
+
*/
|
|
23
|
+
declare function fitLabel(text: string, budget: number): string;
|
|
24
|
+
/** What a neighbour card draws: relative to the selection first, and cut from the middle only if it still overflows. */
|
|
25
|
+
declare function neighbourLabel(label: string, selected: string | undefined): string;
|
|
26
|
+
/** The selected card carries its own name in a larger type, so it has fewer characters to spend and no relative form. */
|
|
27
|
+
declare function selectedLabel(label: string): string;
|
|
28
|
+
export { fitLabel, neighbourLabel, relativeTo, selectedLabel };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loads a surface only when the panel actually needs it (`docs/devtools.md` §6).
|
|
3
|
+
*
|
|
4
|
+
* Four surfaces earn a chunk of their own: the control plane, which a host without a write port must not carry at
|
|
5
|
+
* all; the legend, collapsed until a developer asks what a shape means; the exporter, behind the click that uses it;
|
|
6
|
+
* and the timeline, which arrives with its tab. They are the same shape — a module the panel wants at a moment it
|
|
7
|
+
* can name — so they share one hook rather than one loader each.
|
|
8
|
+
*
|
|
9
|
+
* The loader is compared by identity, so it has to be a module-level constant; a loader built during render would
|
|
10
|
+
* refetch on every commit. The cache is per page, because a second panel in the same document should not fetch a
|
|
11
|
+
* module the first one already has.
|
|
12
|
+
*/
|
|
13
|
+
import type * as ControlUi from './control-ui.js';
|
|
14
|
+
import type * as ExportRun from './export-run.js';
|
|
15
|
+
import type * as LegendUi from './legend.js';
|
|
16
|
+
import type * as TimelineUi from './timeline-view.js';
|
|
17
|
+
type Loader<Module> = () => Promise<Module>;
|
|
18
|
+
/**
|
|
19
|
+
* What the panel knows about a surface right now. A fetch can fail — a chunk can be missing, a deploy can be half
|
|
20
|
+
* replaced, a network can refuse — and a failure that only rejects a promise leaves the panel showing nothing while
|
|
21
|
+
* telling nobody. So the state is explicit, and a failed surface offers the one action that can help: try again.
|
|
22
|
+
*/
|
|
23
|
+
type LazyModule<Module> = Readonly<{
|
|
24
|
+
error: unknown;
|
|
25
|
+
module: Module | undefined;
|
|
26
|
+
retry: () => void;
|
|
27
|
+
}>;
|
|
28
|
+
declare function useLazyModule<Module>(load: Loader<Module> | undefined): LazyModule<Module>;
|
|
29
|
+
/** The write port's surface (§5): fetched when a host passes a control session, and never otherwise. */
|
|
30
|
+
declare const loadControlUi: () => Promise<typeof ControlUi>;
|
|
31
|
+
/** The legend (§4.6): fetched the first time it is opened, which for most sessions is never. */
|
|
32
|
+
declare const loadLegend: () => Promise<typeof LegendUi>;
|
|
33
|
+
/** The exporter (§2.6): fetched by the click, because a session that never exports never needs it. */
|
|
34
|
+
declare const loadExportRun: () => Promise<typeof ExportRun>;
|
|
35
|
+
/** The timeline (§4.1): a whole tab, and a session that watches the graph never opens it. */
|
|
36
|
+
declare const loadTimeline: () => Promise<typeof TimelineUi>;
|
|
37
|
+
export { loadControlUi, loadExportRun, loadLegend, loadTimeline, useLazyModule };
|
|
38
|
+
export type { LazyModule };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The legend's own dictionary, kept out of the panel's (§4.6).
|
|
3
|
+
*
|
|
4
|
+
* The legend is collapsed until a developer asks what a shape means, so its words travel with its code rather than
|
|
5
|
+
* with every panel that never opens it. The discipline is the panel's: English is the source, the Russian record is
|
|
6
|
+
* typed against it, and the protocol vocabulary the legend explains is not translated in either.
|
|
7
|
+
*/
|
|
8
|
+
import type { DevtoolsLocale } from './locale.js';
|
|
9
|
+
declare const en: {
|
|
10
|
+
legendBadgeDesired: string;
|
|
11
|
+
legendBadgeHealth: string;
|
|
12
|
+
legendBadgeLifecycle: string;
|
|
13
|
+
legendBadges: string;
|
|
14
|
+
legendEdgeContribution: string;
|
|
15
|
+
legendEdgeDependency: string;
|
|
16
|
+
legendEdgeOwnership: string;
|
|
17
|
+
legendEdges: string;
|
|
18
|
+
legendEdgeWeak: string;
|
|
19
|
+
legendLifecycle: string;
|
|
20
|
+
legendWeakCovers: string;
|
|
21
|
+
};
|
|
22
|
+
declare function legendStringsFor(locale: DevtoolsLocale): typeof en;
|
|
23
|
+
export { legendStringsFor };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One legend for the whole panel, per the owner's request: the state glyphs, the three kinds of edge and the badges,
|
|
3
|
+
* in one collapsible place instead of two partial legends pinned to a tree and a canvas.
|
|
4
|
+
*
|
|
5
|
+
* It is collapsed by default. A legend that is always open spends the space a graph needs; a legend that cannot be
|
|
6
|
+
* found is worse than none — so the header carries a button, and the button says whether it is open.
|
|
7
|
+
*/
|
|
8
|
+
import type { ReactElement } from 'react';
|
|
9
|
+
declare function Legend(): ReactElement;
|
|
10
|
+
export { Legend };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The current dictionary, passed down by context rather than threaded through every component.
|
|
3
|
+
*
|
|
4
|
+
* There is no provider-less fallback to English: a component that reads strings outside the panel is a bug, and a
|
|
5
|
+
* silent default would hide it until a Russian panel showed one English button.
|
|
6
|
+
*/
|
|
7
|
+
import type { ReactElement, ReactNode } from 'react';
|
|
8
|
+
import type { DevtoolsLocale, Strings } from './locale.js';
|
|
9
|
+
type LocaleProviderProps = Readonly<{
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
locale: DevtoolsLocale;
|
|
12
|
+
}>;
|
|
13
|
+
declare function LocaleProvider({ children, locale }: LocaleProviderProps): ReactElement;
|
|
14
|
+
declare function useStrings(): Strings;
|
|
15
|
+
/** For the lazily loaded surfaces, which carry their own dictionary rather than adding words to the panel's. */
|
|
16
|
+
declare function useLocale(): DevtoolsLocale;
|
|
17
|
+
export { LocaleProvider, useLocale, useStrings };
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Two languages for the panel chrome, per the owner's request: English and Russian, defaulting to the browser and
|
|
3
|
+
* switchable in the header.
|
|
4
|
+
*
|
|
5
|
+
* What is translated is the prose the panel writes: buttons, headings, empty states, explanations. What is never
|
|
6
|
+
* translated is the vocabulary of the protocol — `Feature`, `FeatureInstance`, `Condition`, `declared`,
|
|
7
|
+
* `hard-import`, `quarantined`, `drain` — because those are identifiers a developer greps for, matches against
|
|
8
|
+
* `spec.md` §3 and reads back out of a snapshot. A translated `hard-import` would be a different word for the same
|
|
9
|
+
* edge, which is exactly what a diagnostic tool must not invent.
|
|
10
|
+
*
|
|
11
|
+
* The English dictionary is the source of truth: `Strings` is derived from it, so a missing or extra Russian key is a
|
|
12
|
+
* type error rather than a silent English word in a Russian panel.
|
|
13
|
+
*/
|
|
14
|
+
type DevtoolsLocale = 'en' | 'ru';
|
|
15
|
+
declare const en: {
|
|
16
|
+
activity: string;
|
|
17
|
+
activityCall: (state: string) => string;
|
|
18
|
+
activityCoverage: string;
|
|
19
|
+
activityEmpty: string;
|
|
20
|
+
activityOwner: string;
|
|
21
|
+
activityTruncated: string;
|
|
22
|
+
axisDesired: string;
|
|
23
|
+
axisHealth: string;
|
|
24
|
+
axisLifecycle: string;
|
|
25
|
+
canvasHidden: (count: number) => string;
|
|
26
|
+
causeGatedBy: string;
|
|
27
|
+
causeOpenUnder: string;
|
|
28
|
+
close: string;
|
|
29
|
+
controlPlaneOff: string;
|
|
30
|
+
controlPlaneRequiresAuthority: string;
|
|
31
|
+
copyId: string;
|
|
32
|
+
copyIdDone: string;
|
|
33
|
+
copyIdFailed: string;
|
|
34
|
+
dependenciesSatisfied: (total: number) => string;
|
|
35
|
+
dependenciesUnsatisfied: (unmet: number, total: number) => string;
|
|
36
|
+
dependsOn: string;
|
|
37
|
+
dependsOnCount: (unmet: number, total: number) => string;
|
|
38
|
+
dependsOnNothing: string;
|
|
39
|
+
edgeFilters: string;
|
|
40
|
+
emptySelection: string;
|
|
41
|
+
exportSnapshot: string;
|
|
42
|
+
failureSentence: (subject: string, phase: string, code: string) => string;
|
|
43
|
+
fit: string;
|
|
44
|
+
instanceCurrent: (generation: number) => string;
|
|
45
|
+
instanceWithPrevious: (generation: number, previous: number) => string;
|
|
46
|
+
keyInstance: string;
|
|
47
|
+
keyKind: string;
|
|
48
|
+
keyLifetime: string;
|
|
49
|
+
keyOverride: string;
|
|
50
|
+
keyProvenance: string;
|
|
51
|
+
keyState: string;
|
|
52
|
+
language: string;
|
|
53
|
+
lastCausalChain: string;
|
|
54
|
+
legend: string;
|
|
55
|
+
live: string;
|
|
56
|
+
matchedFailureCode: string;
|
|
57
|
+
matchedFailureSubject: string;
|
|
58
|
+
matchedId: string;
|
|
59
|
+
matchedKind: string;
|
|
60
|
+
matchedProvenance: string;
|
|
61
|
+
neighbourhood: string;
|
|
62
|
+
noChain: string;
|
|
63
|
+
noSearchHit: string;
|
|
64
|
+
noSelection: string;
|
|
65
|
+
noStateCause: string;
|
|
66
|
+
ownedCount: (count: number, kind: string) => string;
|
|
67
|
+
owns: string;
|
|
68
|
+
ownsNothing: string;
|
|
69
|
+
panelDetails: string;
|
|
70
|
+
panelTitle: string;
|
|
71
|
+
planBadge: (session: string, fingerprint: string) => string;
|
|
72
|
+
presetConditions: string;
|
|
73
|
+
presetExtensionPoints: string;
|
|
74
|
+
presetImportsAndPorts: string;
|
|
75
|
+
presetOwnership: string;
|
|
76
|
+
presets: string;
|
|
77
|
+
reducerPaint: (reducer: string, paint: string) => string;
|
|
78
|
+
searchLabel: string;
|
|
79
|
+
searchResults: string;
|
|
80
|
+
selectedInstanceTag: (generation: number) => string;
|
|
81
|
+
selectionPath: string;
|
|
82
|
+
showInTimeline: string;
|
|
83
|
+
stateDeclared: string;
|
|
84
|
+
stateFailed: string;
|
|
85
|
+
stateOpening: string;
|
|
86
|
+
stateQuarantined: string;
|
|
87
|
+
stateReady: string;
|
|
88
|
+
stateRetiring: string;
|
|
89
|
+
stateWaiting: string;
|
|
90
|
+
statusDropped: (dropped: number) => string;
|
|
91
|
+
statusFrame: (sequence: number, revision: number) => string;
|
|
92
|
+
surfaceFailed: string;
|
|
93
|
+
surfaceRetry: string;
|
|
94
|
+
tabGraph: string;
|
|
95
|
+
tabTimeline: string;
|
|
96
|
+
themeDefaults: string;
|
|
97
|
+
themeHost: (count: number) => string;
|
|
98
|
+
/**
|
|
99
|
+
* The word stays `when`, in both languages: it is the name of the `defineFeature` option a developer greps for,
|
|
100
|
+
* not prose about it. The key exists so the row has one place to read it from, not so it can be translated.
|
|
101
|
+
*/
|
|
102
|
+
treeGroupPrefix: string;
|
|
103
|
+
treeLabel: string;
|
|
104
|
+
treeSummary: (features: number, groups: number) => string;
|
|
105
|
+
whyThisState: string;
|
|
106
|
+
zoomIn: string;
|
|
107
|
+
zoomOut: string;
|
|
108
|
+
};
|
|
109
|
+
type Strings = typeof en;
|
|
110
|
+
declare const locales: readonly DevtoolsLocale[];
|
|
111
|
+
/** The browser's preference, narrowed to what the panel speaks. Anything else falls back to English. */
|
|
112
|
+
declare function detectLocale(languages?: readonly string[]): DevtoolsLocale;
|
|
113
|
+
declare function stringsFor(locale: DevtoolsLocale): Strings;
|
|
114
|
+
export { detectLocale, locales, stringsFor };
|
|
115
|
+
export type { DevtoolsLocale, Strings };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The middle column: breadcrumbs over the canvas, with zoom pinned to its corner.
|
|
3
|
+
*
|
|
4
|
+
* There is no `Graph`/`List` switch (§4.4). The list is what a viewport narrower than 1100 px gets instead of a
|
|
5
|
+
* canvas it cannot draw legibly, not a second way to read a wide one: a manual switch would have been a second
|
|
6
|
+
* layout, a second set of tests and a second set of dictionary strings for a view nobody asked for.
|
|
7
|
+
*/
|
|
8
|
+
import type { ReactElement, ReactNode } from 'react';
|
|
9
|
+
import type { ExtensionPointsProjection } from '../extension-points.js';
|
|
10
|
+
import type { InspectorProjection } from '../inspector.js';
|
|
11
|
+
import type { DiagnosticEdgeKind, DiagnosticNodeId } from '../protocol.js';
|
|
12
|
+
type MainColumnProps = Readonly<{
|
|
13
|
+
/** The control slot of §5, when a host passed a write port. Empty markup otherwise, and absent from the bundle. */
|
|
14
|
+
control: ReactNode;
|
|
15
|
+
edgeKinds: ReadonlySet<DiagnosticEdgeKind>;
|
|
16
|
+
/** Present only under the `Extension points` preset, and then it is what the canvas draws instead (§4.2). */
|
|
17
|
+
extensionPoints: ExtensionPointsProjection | undefined;
|
|
18
|
+
onSelect: (id: DiagnosticNodeId) => void;
|
|
19
|
+
projection: InspectorProjection | undefined;
|
|
20
|
+
}>;
|
|
21
|
+
declare function MainColumn({ control, edgeKinds, extensionPoints, onSelect, projection }: MainColumnProps): ReactElement;
|
|
22
|
+
export { MainColumn };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Header of `docs/devtools.md` §4.1: identity badges, the two tabs, search and window actions. */
|
|
2
|
+
import type { ReactElement, RefObject } from 'react';
|
|
3
|
+
import type { DevtoolsStoreStatus } from '../graph-store.js';
|
|
4
|
+
import type { DevtoolsTab } from './edge-filter.js';
|
|
5
|
+
import type { DevtoolsLocale } from './locale.js';
|
|
6
|
+
type PanelHeaderProps = Readonly<{
|
|
7
|
+
legendOpen: boolean;
|
|
8
|
+
locale: DevtoolsLocale;
|
|
9
|
+
onClose: (() => void) | undefined;
|
|
10
|
+
onLegend: () => void;
|
|
11
|
+
onLocale: (locale: DevtoolsLocale) => void;
|
|
12
|
+
onQuery: (query: string) => void;
|
|
13
|
+
onTab: (tab: DevtoolsTab) => void;
|
|
14
|
+
query: string;
|
|
15
|
+
searchRef: RefObject<HTMLInputElement | null>;
|
|
16
|
+
status: DevtoolsStoreStatus;
|
|
17
|
+
tab: DevtoolsTab;
|
|
18
|
+
}>;
|
|
19
|
+
declare function PanelHeader({ legendOpen, locale, onClose, onLegend, onLocale, onQuery, onTab, query, searchRef, status, tab, }: PanelHeaderProps): ReactElement;
|
|
20
|
+
export { PanelHeader };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Status bar of `docs/devtools.md` §4.5: the panel's own health, not the application's. It answers "can I trust what
|
|
3
|
+
* I am looking at" — the cursor it has reached, the frames it never saw, and that it is live.
|
|
4
|
+
*
|
|
5
|
+
* The rest describes the panel rather than the application, so it lives behind `⋯`: its own cost against the §6
|
|
6
|
+
* budgets, where the theme came from, the schema revision, and whether this build can control anything at all. A
|
|
7
|
+
* developer who wants those numbers asks for them; a developer watching the graph should not have to read past them.
|
|
8
|
+
*/
|
|
9
|
+
import type { ReactElement } from 'react';
|
|
10
|
+
import type { DevtoolsStoreStatus } from '../graph-store.js';
|
|
11
|
+
import type { DevtoolsMode } from './panel-mode.js';
|
|
12
|
+
type PanelStatusProps = Readonly<{
|
|
13
|
+
mappedTokens: number;
|
|
14
|
+
mode: DevtoolsMode;
|
|
15
|
+
renderCost: number;
|
|
16
|
+
status: DevtoolsStoreStatus;
|
|
17
|
+
}>;
|
|
18
|
+
declare function PanelStatus({ mappedTokens, mode, renderCost, status }: PanelStatusProps): ReactElement;
|
|
19
|
+
export { PanelStatus };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bottom-drawer shell of `docs/devtools.md` §4.1: header with the two tabs and search, the edge filter row, a
|
|
3
|
+
* tree / graph / inspector body, and a status bar carrying the panel's own health.
|
|
4
|
+
*
|
|
5
|
+
* The selection and the filter are independent state on purpose (§4.3): switching a preset never moves the
|
|
6
|
+
* selection, and selecting never resets the preset. That is what makes the filter a question about one node rather
|
|
7
|
+
* than a mode the developer has to leave.
|
|
8
|
+
*
|
|
9
|
+
* The panel reads, and writes only where a host handed it a write port and the selection holds something that port
|
|
10
|
+
* can address (§5). Without both it never fetches the surface that would write, so it is read-only by construction
|
|
11
|
+
* rather than by a disabled button; the status bar says which of the two it is.
|
|
12
|
+
*/
|
|
13
|
+
import type { ReactElement } from 'react';
|
|
14
|
+
import type { RuntimeControlSession } from '../control.js';
|
|
15
|
+
import type { DevtoolsExport } from '../export-snapshot.js';
|
|
16
|
+
import type { DevtoolsGraphStore } from '../graph-store.js';
|
|
17
|
+
import type { ExportHost } from './export-delivery.js';
|
|
18
|
+
import type { DevtoolsLocale } from './locale.js';
|
|
19
|
+
import type { DevtoolsMode } from './panel-mode.js';
|
|
20
|
+
import type { DevtoolsTheme } from './theme.js';
|
|
21
|
+
type DevtoolsPanelProps = Readonly<{
|
|
22
|
+
/**
|
|
23
|
+
* The write port, when the host has one to give. Without it the panel is read-only and never even fetches the
|
|
24
|
+
* chunk that would write: the control surface is loaded by its presence, not by a flag (§5).
|
|
25
|
+
*/
|
|
26
|
+
control?: RuntimeControlSession;
|
|
27
|
+
/** Overrides feature detection for the export destinations. Tests and non-browser hosts pass their own. */
|
|
28
|
+
exportHost?: ExportHost;
|
|
29
|
+
/** Initial language. Defaults to the browser's preference; the header switch overrides it either way. */
|
|
30
|
+
locale?: DevtoolsLocale;
|
|
31
|
+
mode?: DevtoolsMode;
|
|
32
|
+
onClose?: () => void;
|
|
33
|
+
onExport?: (payload: DevtoolsExport) => void;
|
|
34
|
+
store: DevtoolsGraphStore;
|
|
35
|
+
theme?: DevtoolsTheme;
|
|
36
|
+
}>;
|
|
37
|
+
/** Owns the language so the provider can sit above everything that reads a string. */
|
|
38
|
+
declare function DevtoolsPanel(props: DevtoolsPanelProps): ReactElement;
|
|
39
|
+
export { DevtoolsPanel };
|
|
40
|
+
export type { DevtoolsMode } from './panel-mode.js';
|
|
41
|
+
export type { DevtoolsPanelProps };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search replaces the tree while a query is active. §2 fixes what is searchable, and the second column explains a
|
|
3
|
+
* surprising match rather than restating the obvious one.
|
|
4
|
+
*
|
|
5
|
+
* It used to print the matched field verbatim, which meant every ordinary hit read `label` — a protocol word the
|
|
6
|
+
* developer never asked about, sitting where a useful fact belonged. A hit matched on the node's own name now shows
|
|
7
|
+
* what kind of node it is; only a match found somewhere else says where.
|
|
8
|
+
*/
|
|
9
|
+
import type { ReactElement } from 'react';
|
|
10
|
+
import type { DiagnosticNodeId } from '../protocol.js';
|
|
11
|
+
import type { SearchHit } from '../search.js';
|
|
12
|
+
type SearchResultsProps = Readonly<{
|
|
13
|
+
hits: readonly SearchHit[];
|
|
14
|
+
onSelect: (id: DiagnosticNodeId) => void;
|
|
15
|
+
selectedId: DiagnosticNodeId | undefined;
|
|
16
|
+
}>;
|
|
17
|
+
declare function SearchResults({ hits, onSelect, selectedId }: SearchResultsProps): ReactElement;
|
|
18
|
+
export { SearchResults };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the panel opens on.
|
|
3
|
+
*
|
|
4
|
+
* A developer opens a runtime inspector because something is not ready, so the initial selection is the first feature
|
|
5
|
+
* that is not, and every lifetime owner holding a troubled feature starts expanded. On a healthy application it falls
|
|
6
|
+
* back to the first feature of the first owner.
|
|
7
|
+
*/
|
|
8
|
+
import type { DevtoolsGraphStore } from '../graph-store.js';
|
|
9
|
+
import type { DiagnosticNodeId } from '../protocol.js';
|
|
10
|
+
type InitialSelection = Readonly<{
|
|
11
|
+
expanded: ReadonlySet<DiagnosticNodeId>;
|
|
12
|
+
selectedId: DiagnosticNodeId | undefined;
|
|
13
|
+
}>;
|
|
14
|
+
declare function initialSelection(store: DevtoolsGraphStore): InitialSelection;
|
|
15
|
+
export { initialSelection };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The panel's stylesheet, injected once per document. Values follow the anatomy of `docs/devtools.md` §4 and the
|
|
3
|
+
* type ramp it fixes; every colour is a variable so a host mapping in config is the only theming mechanism.
|
|
4
|
+
*
|
|
5
|
+
* Layout is flex and grid with `gap` throughout — margin-based spacing is not used — and the inspector scrolls with
|
|
6
|
+
* its action footer pinned.
|
|
7
|
+
*/
|
|
8
|
+
declare const stylesheet = "\n.odt {\n --odt-canvas: #0e0f14;\n --odt-surface: #17191f;\n --odt-surface-2: #292c34;\n --odt-text: rgba(246, 247, 255, 1);\n --odt-text-muted: rgba(246, 247, 255, 0.5);\n --odt-line: rgba(246, 247, 255, 0.06);\n --odt-line-strong: rgba(246, 247, 255, 0.25);\n --odt-control: rgba(246, 247, 255, 0.08);\n --odt-accent: #4763f0;\n --odt-accent-text: #5364ff;\n --odt-accent-surface: rgba(71, 99, 240, 0.15);\n --odt-ok: rgba(0, 185, 122, 1);\n --odt-ok-surface: rgba(0, 159, 112, 0.15);\n --odt-warn: rgba(231, 200, 42, 1);\n --odt-warn-surface: rgba(255, 204, 74, 0.15);\n --odt-bad: rgba(254, 85, 100, 1);\n --odt-bad-surface: rgba(241, 79, 93, 0.15);\n --odt-radius: 8px;\n --odt-font: system-ui, -apple-system, sans-serif;\n\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n height: 100%;\n min-height: 0;\n background: var(--odt-surface);\n border-top: 1px solid var(--odt-line);\n color: var(--odt-text);\n font-family: var(--odt-font);\n font-size: 14px;\n font-weight: 500;\n line-height: 1.142;\n -webkit-font-smoothing: antialiased;\n}\n.odt *, .odt *::before, .odt *::after { box-sizing: border-box; }\n.odt button { appearance: none; border: 0; background: none; color: inherit; font: inherit; cursor: pointer; }\n.odt input { appearance: none; border: 0; background: none; color: inherit; font: inherit; }\n.odt :focus-visible { outline: 2px solid var(--odt-accent-text); outline-offset: 1px; }\n.odt-mono { font-variant-numeric: lining-nums tabular-nums; }\n.odt-sec { font-size: 10px; line-height: 1.6; text-transform: uppercase; color: var(--odt-text-muted); }\n.odt-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n\n.odt-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n height: 48px;\n flex-shrink: 0;\n padding: 0 12px 0 16px;\n border-bottom: 1px solid var(--odt-line);\n}\n.odt-header-group { display: flex; align-items: center; gap: 12px; min-width: 0; }\n.odt-tabs { display: flex; align-items: stretch; gap: 4px; height: 48px; }\n.odt-tab {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 40px;\n padding: 0 12px;\n color: var(--odt-text-muted);\n white-space: nowrap;\n}\n.odt-tab[aria-selected='true'] { color: var(--odt-text); box-shadow: inset 0 -1px 0 var(--odt-text); }\n\n.odt-filters {\n display: flex;\n align-items: center;\n gap: 8px 16px;\n flex-wrap: wrap;\n flex-shrink: 0;\n padding: 6px 16px;\n border-bottom: 1px solid var(--odt-line);\n}\n.odt-presets { display: flex; align-items: center; gap: 2px; padding: 2px; border-radius: 1000px; background: var(--odt-control); }\n.odt-preset {\n padding: 3px 12px;\n border-radius: 50px;\n font-size: 12px;\n line-height: 1.333;\n color: var(--odt-text-muted);\n white-space: nowrap;\n}\n.odt-preset[aria-pressed='true'] { background: var(--odt-surface); color: var(--odt-text); }\n.odt-chips { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; min-width: 0; }\n.odt-chip {\n padding: 2px 8px;\n border: 1px solid var(--odt-line);\n border-radius: 1000px;\n font-size: 11px;\n line-height: 1.45;\n color: var(--odt-text-muted);\n white-space: nowrap;\n}\n.odt-chip[aria-checked='true'] {\n border-color: var(--odt-accent-text);\n background: var(--odt-accent-surface);\n color: var(--odt-accent-text);\n}\n\n.odt-tag {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n min-height: 16px;\n padding: 2px 4px;\n border-radius: 4px;\n font-size: 10px;\n line-height: 1.6;\n text-transform: uppercase;\n white-space: nowrap;\n}\n.odt-tag-ok { background: var(--odt-ok-surface); color: var(--odt-ok); }\n.odt-tag-warn { background: var(--odt-warn-surface); color: var(--odt-warn); }\n.odt-tag-bad { background: var(--odt-bad-surface); color: var(--odt-bad); }\n.odt-tag-mute { background: var(--odt-line); color: var(--odt-text-muted); }\n/* A tag that must give way rather than push its row label out: it clips itself and keeps the rest in the title. */\n.odt-tag-clip { display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; }\n.odt-tag-accent { background: var(--odt-accent-surface); color: var(--odt-accent-text); }\n\n.odt-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n height: 32px;\n padding: 0 12px;\n border-radius: var(--odt-radius);\n background: var(--odt-control);\n white-space: nowrap;\n}\n.odt-btn-small { height: 28px; font-size: 12px; }\n.odt-btn-icon { width: 32px; padding: 0; }\n.odt-btn-quiet { background: none; color: var(--odt-text-muted); }\n.odt-btn:disabled { background: rgba(246, 247, 255, 0.04); color: rgba(246, 247, 255, 0.25); cursor: default; }\n\n.odt-search {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 300px;\n max-width: 40vw;\n height: 32px;\n padding: 0 12px;\n border: 1px solid var(--odt-line);\n border-radius: var(--odt-radius);\n background: rgba(246, 247, 255, 0.04);\n color: var(--odt-text-muted);\n}\n.odt-search input { width: 100%; min-width: 0; color: var(--odt-text); }\n.odt-search input::placeholder { color: var(--odt-text-muted); }\n\n.odt-body { display: grid; grid-template-columns: 320px minmax(0, 1fr) 400px; flex-grow: 1; min-height: 0; }\n\n.odt-tree {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 8px;\n border-right: 1px solid var(--odt-line);\n overflow-y: auto;\n}\n.odt-row {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 32px;\n flex-shrink: 0;\n padding: 0 12px 0 44px;\n border-radius: var(--odt-radius);\n text-align: left;\n width: 100%;\n}\n.odt-row-group { padding: 0 12px 0 20px; font-size: 12px; line-height: 1.333; text-transform: uppercase; color: var(--odt-text-muted); }\n.odt-row-root { padding-left: 12px; }\n.odt-row[aria-selected='true'] { background: var(--odt-control); box-shadow: inset 2px 0 0 var(--odt-accent); }\n.odt-row-label { flex-grow: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n\n.odt-glyph { display: inline-flex; width: 16px; height: 16px; align-items: center; justify-content: center; flex-shrink: 0; }\n.odt-glyph-ok { color: var(--odt-ok); }\n.odt-glyph-warn { color: var(--odt-warn); }\n.odt-glyph-bad { color: var(--odt-bad); }\n.odt-glyph-mute { color: var(--odt-text-muted); }\n\n.odt-main { position: relative; display: flex; flex-direction: column; border-right: 1px solid var(--odt-line); min-width: 0; }\n.odt-main-bar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n height: 40px;\n flex-shrink: 0;\n padding: 0 16px;\n border-bottom: 1px solid var(--odt-line);\n}\n.odt-crumbs { display: flex; align-items: center; gap: 6px; min-width: 0; font-size: 12px; line-height: 1.333; color: var(--odt-text-muted); }\n.odt-crumb { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }\n.odt-crumb-link { color: inherit; text-decoration: underline; text-decoration-color: var(--odt-line-strong); }\n.odt-crumb-link:hover, .odt-crumb-link:focus-visible { color: var(--odt-text); }\n.odt-crumb-current { color: var(--odt-text); }\n.odt-locale {\n padding: 2px 6px;\n border: 1px solid var(--odt-line);\n border-radius: var(--odt-radius);\n background: var(--odt-control);\n font: inherit;\n font-size: 12px;\n line-height: 1.333;\n color: var(--odt-text-muted);\n}\n\n.odt-canvas {\n position: relative;\n flex-grow: 1;\n min-height: 0;\n /* A lens draws inside itself: the camera is what reaches a node the fit did not cover. */\n overflow: hidden;\n touch-action: none;\n /* A drag moves the camera; it must not also select the labels it crosses. */\n user-select: none;\n cursor: grab;\n background-image: radial-gradient(var(--odt-line) 1px, transparent 1px);\n background-size: 16px 16px;\n}\n.odt-canvas-panning { cursor: grabbing; }\n.odt-canvas-layer { position: absolute; top: 0; left: 0; transform-origin: 0 0; }\n/* Edges sit under the cards, and the cards are opaque, so a line can never cross a badge. */\n.odt-edges { position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; pointer-events: none; }\n.odt-node {\n position: absolute;\n display: flex;\n flex-direction: column;\n gap: 4px;\n width: 188px;\n padding: 8px 12px;\n z-index: 1;\n border: 1px solid var(--odt-line);\n border-radius: var(--odt-radius);\n background: var(--odt-surface-2);\n font-size: 12px;\n line-height: 1.333;\n text-align: left;\n color: var(--odt-text);\n}\n.odt-node-contribution { border-style: dashed; }\n.odt-node[aria-current='true'] { gap: 6px; border-color: var(--odt-accent); background: var(--odt-accent-surface); }\n.odt-node-head { display: flex; align-items: center; gap: 6px; min-width: 0; }\n.odt-node-title { font-size: 14px; line-height: 1.142; }\n.odt-node-sub { font-size: 10px; line-height: 1.6; text-transform: uppercase; color: var(--odt-text-muted); }\n.odt-node-detail { font-size: 10px; line-height: 1.6; color: var(--odt-text-muted); }\n.odt-node-tags { display: flex; flex-wrap: wrap; gap: 4px; min-width: 0; }\n.odt-node-tags > * { flex-shrink: 0; }\n.odt-canvas-hint {\n position: absolute;\n right: 16px;\n top: 12px;\n max-width: 60%;\n margin: 0;\n padding: 4px 8px;\n border-radius: var(--odt-radius);\n background: var(--odt-surface-2);\n font-size: 10px;\n line-height: 1.6;\n text-transform: uppercase;\n color: var(--odt-text-muted);\n}\n.odt-legend-panel {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));\n gap: 12px 24px;\n flex-shrink: 0;\n max-height: 40%;\n overflow: auto;\n padding: 12px 16px;\n border-bottom: 1px solid var(--odt-line);\n background: var(--odt-surface);\n}\n.odt-legend-group { display: flex; flex-direction: column; gap: 6px; min-width: 0; }\n.odt-legend-row { display: flex; align-items: center; gap: 8px; min-width: 0; font-size: 12px; line-height: 1.333; }\n.odt-legend-name { flex-shrink: 0; color: var(--odt-text); }\n.odt-legend-detail { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--odt-text-muted); }\n.odt-canvas-zoom { position: absolute; right: 16px; bottom: 12px; display: flex; gap: 4px; }\n.odt-edge { fill: none; stroke-width: 1.5; }\n.odt-edge-wait { stroke: var(--odt-line-strong); }\n.odt-edge-own { stroke: var(--odt-accent-text); }\n.odt-edge-contribute { stroke: var(--odt-line-strong); stroke-dasharray: 4 4; }\n.odt-edge-sample { display: inline-block; width: 16px; height: 0; border-top: 1.5px solid var(--odt-line-strong); }\n.odt-edge-sample-accent { border-color: var(--odt-accent-text); }\n.odt-edge-sample-dashed { border-top-style: dashed; }\n\n.odt-list { display: flex; flex-direction: column; gap: 2px; padding: 8px; overflow-y: auto; flex-grow: 1; min-height: 0; }\n.odt-list-row { display: flex; align-items: center; gap: 8px; height: 32px; flex-shrink: 0; padding: 0 12px; border-radius: var(--odt-radius); text-align: left; }\n.odt-list-row[aria-selected='true'] { background: var(--odt-control); }\n.odt-list-meta { font-size: 12px; line-height: 1.333; color: var(--odt-text-muted); white-space: nowrap; }\n\n.odt-inspector-host { display: flex; min-width: 0; min-height: 0; }\n.odt-inspector { display: flex; flex-direction: column; flex-grow: 1; min-width: 0; min-height: 0; }\n.odt-inspector-scroll { display: flex; flex-direction: column; gap: 10px; padding: 12px 16px; overflow-y: auto; flex-grow: 1; min-height: 0; }\n.odt-inspector-head { display: flex; flex-direction: column; gap: 6px; }\n.odt-inspector-title { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 16px; line-height: 1.25; }\n.odt-axes { display: flex; flex-wrap: wrap; gap: 4px; }\n.odt-kv { display: grid; grid-template-columns: 112px minmax(0, 1fr); gap: 4px 12px; font-size: 12px; line-height: 1.333; }\n.odt-k { color: var(--odt-text-muted); }\n.odt-v { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.odt-why { display: flex; flex-direction: column; gap: 6px; padding: 8px 12px; border-radius: var(--odt-radius); background: rgba(246, 247, 255, 0.05); }\n.odt-why-text { font-size: 12px; line-height: 1.333; }\n.odt-why-ref { color: var(--odt-accent-text); }\n.odt-section { display: flex; flex-direction: column; gap: 6px; }\n.odt-section-rows { display: flex; flex-direction: column; gap: 4px; font-size: 12px; line-height: 1.333; }\n.odt-section-row { display: flex; align-items: center; gap: 8px; text-align: left; width: 100%; }\n.odt-section-row-nested { padding-left: 16px; }\n.odt-section-row-label { flex-grow: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.odt-chain { display: flex; gap: 10px; align-items: flex-start; font-size: 12px; line-height: 1.333; }\n.odt-chain-time { width: 78px; flex-shrink: 0; color: var(--odt-text-muted); font-variant-numeric: tabular-nums; }\n.odt-footer { display: flex; flex-wrap: wrap; gap: 8px; padding: 10px 16px; border-top: 1px solid var(--odt-line); flex-shrink: 0; }\n.odt-footer-end { margin-left: auto; }\n.odt-footer-status { min-width: 0; font-size: 10px; line-height: 1.6; text-align: right; color: var(--odt-text-muted); }\n.odt-empty { padding: 12px 16px; font-size: 12px; line-height: 1.333; color: var(--odt-text-muted); }\n\n.odt-timeline { display: flex; flex-direction: column; gap: 2px; padding: 8px 16px; overflow-y: auto; flex-grow: 1; min-height: 0; }\n.odt-timeline-row { display: flex; align-items: center; gap: 12px; height: 28px; flex-shrink: 0; font-size: 12px; line-height: 1.333; text-align: left; }\n.odt-timeline-gap { color: var(--odt-warn); }\n.odt-timeline-seq { width: 96px; flex-shrink: 0; color: var(--odt-text-muted); font-variant-numeric: tabular-nums; }\n.odt-timeline-time { width: 96px; flex-shrink: 0; color: var(--odt-text-muted); font-variant-numeric: tabular-nums; }\n\n.odt-status {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n flex-wrap: wrap;\n min-height: 28px;\n flex-shrink: 0;\n padding: 4px 16px;\n border-top: 1px solid var(--odt-line);\n font-size: 10px;\n line-height: 1.6;\n text-transform: uppercase;\n color: var(--odt-text-muted);\n}\n.odt-status-group { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 16px; min-width: 0; }\n.odt-status-more { padding: 0 4px; font-size: 12px; line-height: 1; color: var(--odt-text-muted); }\n.odt-status-more[aria-expanded='true'] { color: var(--odt-text); }\n\n/**\n * Below 1100 px the three columns stop being readable, so the neighbourhood \u2014 drawn as a list at this width \u2014 and\n * the inspector share the second column instead of one of them disappearing. Losing the neighbourhood would leave\n * the panel unable to answer what a node is waiting for, which is the question it exists for.\n */\n@media (max-width: 1100px) {\n .odt-body { grid-template-columns: 320px minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) minmax(0, 1fr); }\n .odt-tree { grid-row: 1 / span 2; }\n .odt-main { border-right: 0; border-bottom: 1px solid var(--odt-line); }\n}\n@media (max-width: 720px) {\n .odt-body { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto; }\n .odt-tree { grid-row: auto; border-right: 0; }\n .odt-step-hidden { display: none; }\n .odt-search { width: auto; max-width: none; flex-grow: 1; }\n}\n@media (prefers-reduced-motion: no-preference) {\n .odt-canvas-layer { transition: transform 120ms ease-out; }\n}\n";
|
|
9
|
+
/** Injects one stylesheet once per document. Safe to call on every mount and outside a browser. */
|
|
10
|
+
declare function injectStylesheet(name: string, css: string, target?: Document | undefined): void;
|
|
11
|
+
declare function ensureStylesheet(target?: Document): void;
|
|
12
|
+
export { stylesheet as devtoolsStylesheet };
|
|
13
|
+
export { ensureStylesheet, injectStylesheet };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theming per `docs/devtools.md` §4.6: the panel ships its own CSS variables with neutral dark defaults
|
|
3
|
+
* and never imports a host design system. A host maps its own tokens once, in config.
|
|
4
|
+
*
|
|
5
|
+
* Overrides are applied as inline custom properties on the panel root, so nothing leaks into the host's cascade and
|
|
6
|
+
* the panel still renders correctly in an application that has no design system at all.
|
|
7
|
+
*/
|
|
8
|
+
import type { CSSProperties } from 'react';
|
|
9
|
+
type DevtoolsTheme = Readonly<{
|
|
10
|
+
accent?: string;
|
|
11
|
+
accentSurface?: string;
|
|
12
|
+
accentText?: string;
|
|
13
|
+
bad?: string;
|
|
14
|
+
badSurface?: string;
|
|
15
|
+
canvas?: string;
|
|
16
|
+
control?: string;
|
|
17
|
+
font?: string;
|
|
18
|
+
line?: string;
|
|
19
|
+
lineStrong?: string;
|
|
20
|
+
ok?: string;
|
|
21
|
+
okSurface?: string;
|
|
22
|
+
radius?: string;
|
|
23
|
+
surface?: string;
|
|
24
|
+
surfaceRaised?: string;
|
|
25
|
+
text?: string;
|
|
26
|
+
textMuted?: string;
|
|
27
|
+
warn?: string;
|
|
28
|
+
warnSurface?: string;
|
|
29
|
+
}>;
|
|
30
|
+
type ThemeMapping = Readonly<{
|
|
31
|
+
/** How many variables the host actually mapped. The status bar reports it behind its disclosure (§4.5). */
|
|
32
|
+
mapped: number;
|
|
33
|
+
style: CSSProperties;
|
|
34
|
+
}>;
|
|
35
|
+
declare function themeMapping(theme: DevtoolsTheme | undefined): ThemeMapping;
|
|
36
|
+
/** A neutral explicit theme; applications may provide any subset through DevtoolsTheme. */
|
|
37
|
+
declare const defaultTheme: DevtoolsTheme;
|
|
38
|
+
export { defaultTheme, themeMapping };
|
|
39
|
+
export type { DevtoolsTheme, ThemeMapping };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The timeline's own dictionary, kept out of the panel's (§4.1).
|
|
3
|
+
*
|
|
4
|
+
* The view renders on its own tab and nowhere else, so its words travel with its code: a session that stays on the
|
|
5
|
+
* graph never carries them. The discipline is the panel's — English is the source, the Russian record is typed
|
|
6
|
+
* against it — and the protocol's vocabulary is not translated in either.
|
|
7
|
+
*/
|
|
8
|
+
import type { DevtoolsLocale } from './locale.js';
|
|
9
|
+
declare const en: {
|
|
10
|
+
frameRevision: (sequence: number, revision: number) => string;
|
|
11
|
+
gapDropped: (dropped: number) => string;
|
|
12
|
+
gapRestarted: string;
|
|
13
|
+
gapTitle: string;
|
|
14
|
+
noCommits: string;
|
|
15
|
+
timelineChanged: (changed: number) => string;
|
|
16
|
+
timelineCommit: string;
|
|
17
|
+
timelineLog: string;
|
|
18
|
+
};
|
|
19
|
+
declare function timelineStringsFor(locale: DevtoolsLocale): typeof en;
|
|
20
|
+
export { timelineStringsFor };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The timeline tab: one row per committed transaction, newest first, with dropped-gap marks inline so a hole in the
|
|
3
|
+
* history is impossible to mistake for a quiet period (§6, §7).
|
|
4
|
+
*
|
|
5
|
+
* The time column is empty for a commit with no cause, and always empty for a gap. That is not a missing feature:
|
|
6
|
+
* the protocol's only clock stamps the trigger of a commit, and a gap has none.
|
|
7
|
+
*/
|
|
8
|
+
import type { ReactElement } from 'react';
|
|
9
|
+
import type { TimelineEntry } from '../timeline.js';
|
|
10
|
+
type TimelineViewProps = Readonly<{
|
|
11
|
+
entries: readonly TimelineEntry[];
|
|
12
|
+
}>;
|
|
13
|
+
declare function TimelineView({ entries }: TimelineViewProps): ReactElement;
|
|
14
|
+
export { TimelineView };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The feature tree of `docs/devtools.md` §4, retold in the runtime's words: lifetime owners as uppercase
|
|
3
|
+
* groups — the application itself and one row per condition group — with features as 32 px rows carrying glyph plus
|
|
4
|
+
* text tag, and the glyph legend pinned at the bottom.
|
|
5
|
+
*
|
|
6
|
+
* Rows are flattened to a single list before rendering, both for keyboard navigation and so that swapping in a
|
|
7
|
+
* virtualizer later is a change of one `map` (§6).
|
|
8
|
+
*/
|
|
9
|
+
import type { ReactElement } from 'react';
|
|
10
|
+
import type { TreeGroupRow } from '../graph-index.js';
|
|
11
|
+
import type { DiagnosticLabel, DiagnosticNodeId } from '../protocol.js';
|
|
12
|
+
import type { NodeDisplayState } from '../visual-axes.js';
|
|
13
|
+
type TreeProps = Readonly<{
|
|
14
|
+
/** Resolved per rendered row, so a collapsed owner costs nothing when a commit changes its members (§6). */
|
|
15
|
+
displayOf: (id: DiagnosticNodeId) => NodeDisplayState;
|
|
16
|
+
expanded: ReadonlySet<DiagnosticNodeId>;
|
|
17
|
+
groups: readonly TreeGroupRow[];
|
|
18
|
+
onConfirm: () => void;
|
|
19
|
+
onSelect: (id: DiagnosticNodeId) => void;
|
|
20
|
+
onToggle: (id: DiagnosticNodeId) => void;
|
|
21
|
+
selectedId: DiagnosticNodeId | undefined;
|
|
22
|
+
summary: DiagnosticLabel;
|
|
23
|
+
version: number;
|
|
24
|
+
}>;
|
|
25
|
+
declare function Tree({ displayOf, expanded, groups, onConfirm, onSelect, onToggle, selectedId, summary, version, }: TreeProps): ReactElement;
|
|
26
|
+
export { Tree };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DevtoolsGraphStore } from '../graph-store.js';
|
|
2
|
+
/** Subscribes a component to the store's version counter, which changes exactly once per applied frame or reset. */
|
|
3
|
+
declare function useStoreVersion(store: DevtoolsGraphStore): number;
|
|
4
|
+
/**
|
|
5
|
+
* Rolling cost of the panel's own render-to-commit work, for the status bar budget of §6.
|
|
6
|
+
*
|
|
7
|
+
* It writes to refs during render and reads the previous measurement, so measuring never schedules a render of its
|
|
8
|
+
* own — a diagnostics readout that re-rendered the panel to display its own cost would be measuring itself.
|
|
9
|
+
*/
|
|
10
|
+
declare function useRenderCost(version: number): number;
|
|
11
|
+
export { useRenderCost, useStoreVersion };
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The React panel. A peer dependency on React lives only in this entry point, so the headless session and reducer
|
|
3
|
+
* in `@opetope/devtools` stay usable without a renderer (§3.1).
|
|
4
|
+
*/
|
|
5
|
+
export { edgeKinds, edgePresets, presetKinds } from './react/edge-filter.js';
|
|
6
|
+
export type { DevtoolsTab, EdgePreset, EdgePresetId } from './react/edge-filter.js';
|
|
7
|
+
export { formatClock, formatInstance, formatOptionalTime, formatTime } from './react/format.js';
|
|
8
|
+
export { stateDescription, StateGlyph, StateTag } from './react/glyph.js';
|
|
9
|
+
export { layoutNeighbourhood } from './react/graph-layout.js';
|
|
10
|
+
export type { GraphLayout, LaidOutEdge, LaidOutNode } from './react/graph-layout.js';
|
|
11
|
+
export { DevtoolsPanel } from './react/panel.js';
|
|
12
|
+
export type { DevtoolsMode, DevtoolsPanelProps } from './react/panel.js';
|
|
13
|
+
export { devtoolsStylesheet, ensureStylesheet } from './react/styles.js';
|
|
14
|
+
export { defaultTheme, themeMapping } from './react/theme.js';
|
|
15
|
+
export type { DevtoolsTheme, ThemeMapping } from './react/theme.js';
|
|
16
|
+
export { useRenderCost, useStoreVersion } from './react/use-store.js';
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{D as a,S as t,a as o,d as r,s as i,e as l,b as n,c as d,f as m,g as h,h as p,i as f,l as u,p as g,j as S,t as c,u as y,k as T}from"./panel-5jvisQBr.js";export{a as DevtoolsPanel,t as StateGlyph,o as StateTag,r as defaultTheme,i as devtoolsStylesheet,l as edgeKinds,n as edgePresets,d as ensureStylesheet,m as formatClock,h as formatInstance,p as formatOptionalTime,f as formatTime,u as layoutNeighbourhood,g as presetKinds,S as stateDescription,c as themeMapping,y as useRenderCost,T as useStoreVersion};
|
|
2
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{k as u,n as d,l as m}from"./protocol-joKTjlMj.js";function s(n,e){var t;return((t=n.node(e))==null?void 0:t.label)??e}function w(n,e){const t=n.runtimeNode(e);return(t==null?void 0:t.kind)==="contribution"?t.state.kind:"declared"}function O(n,e){var i;const t=[];for(const o of n.outgoing(e))o.kind==="owns"&&((i=n.node(o.to))==null?void 0:i.kind)==="contribution"&&t.push(o.to);return t}function l(n,e){const t=[];for(const i of n.incoming(e))i.kind==="contributes-to"&&t.push(i.from);return t}function v(n,e){var t;return(t=n.outgoing(e).find(i=>i.kind==="contributes-to"))==null?void 0:t.to}function p(n,e){var t;return(t=n.incoming(e).find(i=>i.kind==="owns"))==null?void 0:t.from}function b(n,e,t,i){const o=p(n,e);return{contribution:e,contributionDisplay:n.displayOf(e),contributionLabel:s(n,e),contributorLabel:o===void 0?s(n,e):s(n,o),endpoint:t,endpointDisplay:n.displayOf(t),endpointLabel:s(n,t),own:i,state:w(n,e)}}function g(n,e){const t=new Set(O(n,e)),i=[],o=new Set;for(const r of t){const a=v(n,r);if(!(a===void 0||o.has(a))){o.add(a);for(const f of l(n,a))i.push(b(n,f,a,t.has(f)))}}return i}function F(n,e){const t=[];for(const i of l(n,e)){const o=p(n,i);o!==void 0&&t.push(b(n,i,o,!0))}return t}function L(n,e){const t=n.graph,i=t.node(e);if(i===void 0)return;const o=i.kind==="contribution-target";return{display:t.displayOf(e),id:e,inverted:o,kind:i.kind,label:i.label,rows:o?F(t,e):g(t,e)}}function c(n){return"failure"in n.state?n.state.failure:void 0}function k(n){return n.kind==="feature-instance"?n.generation:void 0}function h(n){const{state:e}=n;if(e.kind==="opening"&&"total"in e)return`${e.kind} \xB7 ${e.opened}/${e.total}`;if(e.kind==="retiring"&&"remaining"in e)return`${e.kind} \xB7 ${e.remaining}`}function N(n){const{kind:e}=n.state;return e==="ready"||e==="active"||e==="available"||e==="bound"||e==="true"}function y(n){const e={};for(const[t,i]of Object.entries(n))i!==void 0&&(e[t]=i);return e}function j(n,e){if(d(e))return e.lookup==="found";if(!m(e))return!0;const t=n.runtimeNode(e.from);return!e.pending&&(t===void 0||N(t))}function S(n,e){var i;const t=d(e);return{display:n.displayOf(e.from),kind:e.kind,label:((i=n.node(e.from))==null?void 0:i.label)??e.from,...t?{lookup:e.lookup}:{},node:e.from,ready:j(n,e),weak:t}}function $(n,e){const t=[];for(const i of n.incoming(e))u(i)&&t.push(S(n,i));return t}function D(n,e,t){for(const i of n.outgoing(e.id)){if(u(i))continue;const o=n.runtimeNode(i.to);o!==void 0&&t.push({depth:1,display:n.displayOf(o.id),edge:i,node:o})}}function E(n,e){const t=[];for(const i of n.outgoing(e)){if(u(i))continue;const o=n.runtimeNode(i.to);o!==void 0&&(t.push({depth:0,display:n.displayOf(o.id),edge:i,node:o}),o.kind==="feature-instance"&&D(n,o,t))}return t}function R(n,e){const t=n===void 0?void 0:c(n),i=n===void 0?void 0:h(n);return{readyBlockers:e.reduce((o,r)=>o+(r.ready?1:0),0),totalBlockers:e.length,...y({cause:n==null?void 0:n.cause,failure:t,progress:i})}}function C(n,e){var t;if("provenance"in e&&e.provenance!==void 0)return e.provenance;if(!(!("declaration"in e)||e.declaration===void 0))return(t=n.planNode(e.declaration))==null?void 0:t.provenance}function A(n){return{desired:n,health:"healthy",lifecycle:"declared"}}function B(n,e,t,i){const o={id:n.plan.application.id,label:n.plan.application.label},r=[o];return t!==void 0&&i!==e.id&&r.push({...i===void 0?{}:{id:i},label:t}),e.id!==o.id&&r.push({id:e.id,label:e.label}),r}function T(n){return{id:n==null?void 0:n.id,label:n==null?void 0:n.label,override:(n==null?void 0:n.desired.override)??"none"}}function G(n,e,t,i){return y({generation:t===void 0?void 0:k(t),groupLabel:i==null?void 0:i.label,node:t,provenance:C(n,e)})}function H(n,e){const t=n.graph,i=t.node(e);if(i===void 0)return;const o=t.runtimeNode(e),r=t.groupOf(e),a=T(r),f=$(t,e);return{applicationActivity:n.activity,axes:t.axesOf(e)??A(t.desiredOf(e)),blockers:f,breadcrumbs:B(t,i,a.label,a.id),chain:n.chainFor(e),display:t.displayOf(e),id:e,kind:i.kind,label:i.label,override:a.override,owned:E(t,e),why:R(o,f),...G(t,i,o,r)}}const P=40,W=[["label",n=>n.label],["id",n=>n.id],["kind",n=>n.kind],["provenance",n=>"provenance"in n?n.provenance:void 0],["failure-code",n=>{var e;return"state"in n?(e=c(n))==null?void 0:e.code:void 0}],["failure-subject",n=>{var e;return"state"in n?(e=c(n))==null?void 0:e.subject:void 0}]];function q(n,e){var t;for(const[i,o]of W)if(((t=o(n))==null?void 0:t.toLowerCase().includes(e))===!0)return i}function z(n,e,t=P){const i=e.trim().toLowerCase();if(i.length===0)return[];const o=[];for(const r of n.nodes()){if(o.length>=t)break;const a=q(r,i);a!==void 0&&o.push({display:n.displayOf(r.id),field:a,id:r.id,kind:r.kind,label:r.label})}return o}export{L as a,H as b,c as f,k as g,h as p,z as s};
|
|
2
|
+
//# sourceMappingURL=search-CC7avy6I.js.map
|