@draftline/react 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/dist/index.d.ts +207 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +546 -0
- package/package.json +37 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { type ChangedFile, type DraftlineClient, type JsonValue, type SupportRef, type SupportRefScope, type Variation, type Version, type WorkspaceDiagnostics } from '@draftline/client';
|
|
2
|
+
import { type CSSProperties, type KeyboardEvent as ReactKeyboardEvent, type MouseEvent as ReactMouseEvent, type ReactNode } from 'react';
|
|
3
|
+
export type CommandResult = {
|
|
4
|
+
name: string;
|
|
5
|
+
payload: unknown;
|
|
6
|
+
ok: boolean;
|
|
7
|
+
};
|
|
8
|
+
export type InspectorTab = 'json' | 'recovery' | 'supportRefs';
|
|
9
|
+
export type DraftlineMutation<T> = {
|
|
10
|
+
error: unknown;
|
|
11
|
+
isPending: boolean;
|
|
12
|
+
reset: () => void;
|
|
13
|
+
run: () => Promise<T | null>;
|
|
14
|
+
};
|
|
15
|
+
export type DraftlineGraphNodeKind = 'dirty' | 'version' | 'remoteTip';
|
|
16
|
+
export type DraftlineGraphDirtyMode = 'node' | 'none';
|
|
17
|
+
export type DraftlineGraphOrientation = 'horizontal' | 'vertical';
|
|
18
|
+
export type DraftlineGraphLaneOrder = 'activeFirst' | ((left: DraftlineGraphLane, right: DraftlineGraphLane) => number);
|
|
19
|
+
export interface DraftlineGraphLane {
|
|
20
|
+
id: string;
|
|
21
|
+
label: string;
|
|
22
|
+
variationId?: string | null;
|
|
23
|
+
}
|
|
24
|
+
interface DraftlineGraphNodeBase {
|
|
25
|
+
id: string;
|
|
26
|
+
kind: DraftlineGraphNodeKind;
|
|
27
|
+
lane: number;
|
|
28
|
+
row: number;
|
|
29
|
+
label: string;
|
|
30
|
+
variationId?: string | null;
|
|
31
|
+
variationIds: string[];
|
|
32
|
+
parentVersionIds: string[];
|
|
33
|
+
isActiveHead: boolean;
|
|
34
|
+
isHead: boolean;
|
|
35
|
+
}
|
|
36
|
+
export interface DraftlineVersionGraphNode extends DraftlineGraphNodeBase {
|
|
37
|
+
kind: 'version';
|
|
38
|
+
version: Version;
|
|
39
|
+
versionId: string;
|
|
40
|
+
}
|
|
41
|
+
export interface DraftlineDirtyGraphNode extends DraftlineGraphNodeBase {
|
|
42
|
+
kind: 'dirty';
|
|
43
|
+
baseVersionId: string | null;
|
|
44
|
+
changedCount: number;
|
|
45
|
+
changedFiles: ChangedFile[];
|
|
46
|
+
}
|
|
47
|
+
export interface DraftlineRemoteTipGraphNode extends DraftlineGraphNodeBase {
|
|
48
|
+
kind: 'remoteTip';
|
|
49
|
+
remote: string;
|
|
50
|
+
versionId?: string;
|
|
51
|
+
}
|
|
52
|
+
export type DraftlineGraphNode = DraftlineDirtyGraphNode | DraftlineRemoteTipGraphNode | DraftlineVersionGraphNode;
|
|
53
|
+
export interface DraftlineGraphEdge {
|
|
54
|
+
from: string;
|
|
55
|
+
to: string;
|
|
56
|
+
kind: 'parent' | 'remoteTip';
|
|
57
|
+
}
|
|
58
|
+
export interface DraftlineHistoryGraphModel {
|
|
59
|
+
dirtyNode?: DraftlineGraphNode;
|
|
60
|
+
edges: DraftlineGraphEdge[];
|
|
61
|
+
lanes: DraftlineGraphLane[];
|
|
62
|
+
nodes: DraftlineGraphNode[];
|
|
63
|
+
orientation: DraftlineGraphOrientation;
|
|
64
|
+
}
|
|
65
|
+
export interface RemoteTipInput {
|
|
66
|
+
id: string;
|
|
67
|
+
label?: string;
|
|
68
|
+
remote: string;
|
|
69
|
+
variationId?: string | null;
|
|
70
|
+
versionId?: string | null;
|
|
71
|
+
}
|
|
72
|
+
export interface DraftlineHistoryGraphInput {
|
|
73
|
+
activeVariationId?: string | null;
|
|
74
|
+
dirtyFiles?: ChangedFile[];
|
|
75
|
+
dirtyMode?: DraftlineGraphDirtyMode;
|
|
76
|
+
includeRemoteTips?: boolean;
|
|
77
|
+
laneOrder?: DraftlineGraphLaneOrder;
|
|
78
|
+
orientation?: DraftlineGraphOrientation;
|
|
79
|
+
remoteTips?: RemoteTipInput[];
|
|
80
|
+
variations?: Variation[];
|
|
81
|
+
versions?: Version[];
|
|
82
|
+
}
|
|
83
|
+
export interface DraftlineGraphNodeContext {
|
|
84
|
+
graph: DraftlineHistoryGraphModel;
|
|
85
|
+
selectable: boolean;
|
|
86
|
+
}
|
|
87
|
+
export interface DraftlineGraphSelectionEvent extends DraftlineGraphNodeContext {
|
|
88
|
+
nativeEvent?: ReactKeyboardEvent<HTMLButtonElement> | ReactMouseEvent<HTMLButtonElement>;
|
|
89
|
+
node: DraftlineGraphNode;
|
|
90
|
+
}
|
|
91
|
+
export interface DraftlineHistoryGraphProps {
|
|
92
|
+
ariaLabel?: string;
|
|
93
|
+
className?: string;
|
|
94
|
+
getNodeKey?: (node: DraftlineGraphNode) => string;
|
|
95
|
+
graph: DraftlineHistoryGraphModel;
|
|
96
|
+
onSelectNode?: (event: DraftlineGraphSelectionEvent) => Promise<void> | void;
|
|
97
|
+
orientation?: DraftlineGraphOrientation;
|
|
98
|
+
renderBadge?: (node: DraftlineGraphNode, context: DraftlineGraphNodeContext) => ReactNode;
|
|
99
|
+
renderNode?: (node: DraftlineGraphNode, context: DraftlineGraphNodeContext) => ReactNode;
|
|
100
|
+
style?: CSSProperties;
|
|
101
|
+
}
|
|
102
|
+
export interface VariationOptionRenderState {
|
|
103
|
+
isActive: boolean;
|
|
104
|
+
label: string;
|
|
105
|
+
switchVariation: () => void;
|
|
106
|
+
}
|
|
107
|
+
export interface DraftlineVariationListItem {
|
|
108
|
+
id: string;
|
|
109
|
+
isActive: boolean;
|
|
110
|
+
label: string;
|
|
111
|
+
variation: Variation;
|
|
112
|
+
}
|
|
113
|
+
export interface DraftlineVariationListModel {
|
|
114
|
+
activeVariationId?: string | null;
|
|
115
|
+
items: DraftlineVariationListItem[];
|
|
116
|
+
}
|
|
117
|
+
export interface DraftlineVariationListInput {
|
|
118
|
+
activeVariationId?: string | null;
|
|
119
|
+
getLabel?: (variation: Variation) => string;
|
|
120
|
+
variations: Variation[];
|
|
121
|
+
}
|
|
122
|
+
export interface DraftlineVariationSelectProps {
|
|
123
|
+
className?: string;
|
|
124
|
+
activeVariationId?: string | null;
|
|
125
|
+
getLabel?: (variation: Variation) => string;
|
|
126
|
+
onSwitch?: (variation: Variation) => void;
|
|
127
|
+
renderOption?: (variation: Variation, state: VariationOptionRenderState) => ReactNode;
|
|
128
|
+
style?: CSSProperties;
|
|
129
|
+
variations: Variation[];
|
|
130
|
+
}
|
|
131
|
+
export interface DraftlineProviderProps {
|
|
132
|
+
children: ReactNode;
|
|
133
|
+
client?: DraftlineClient;
|
|
134
|
+
workspacePath: string;
|
|
135
|
+
onError?: (error: unknown, command: string) => void;
|
|
136
|
+
}
|
|
137
|
+
interface RefreshOptions {
|
|
138
|
+
manageBusy?: boolean;
|
|
139
|
+
recordResult?: boolean;
|
|
140
|
+
rethrow?: boolean;
|
|
141
|
+
}
|
|
142
|
+
export declare function DraftlineProvider({ children, client: clientProp, onError, workspacePath, }: DraftlineProviderProps): import("react").JSX.Element;
|
|
143
|
+
export declare function useDraftlineWorkspace(): {
|
|
144
|
+
diagnostics: WorkspaceDiagnostics | null;
|
|
145
|
+
error: unknown;
|
|
146
|
+
isBusy: boolean;
|
|
147
|
+
refresh: (options?: RefreshOptions) => Promise<WorkspaceDiagnostics | null>;
|
|
148
|
+
workspacePath: string;
|
|
149
|
+
};
|
|
150
|
+
export declare function useDraftlineChanges(): {
|
|
151
|
+
files: ChangedFile[];
|
|
152
|
+
isDirty: boolean;
|
|
153
|
+
};
|
|
154
|
+
export declare function useDraftlineInspector(): {
|
|
155
|
+
commandResult: CommandResult | null;
|
|
156
|
+
diagnostics: WorkspaceDiagnostics | null;
|
|
157
|
+
supportRefs: SupportRef[];
|
|
158
|
+
};
|
|
159
|
+
export declare function useDraftlineHistoryGraph(options?: Omit<DraftlineHistoryGraphInput, 'variations' | 'versions' | 'dirtyFiles' | 'activeVariationId'>): DraftlineHistoryGraphModel;
|
|
160
|
+
export declare function useDraftlineSupportRefs(scope?: SupportRefScope): {
|
|
161
|
+
load: DraftlineMutation<SupportRef[]>;
|
|
162
|
+
supportRefs: SupportRef[];
|
|
163
|
+
};
|
|
164
|
+
export declare function useDraftlineRemoteSync(remote: string): {
|
|
165
|
+
applyIncoming: DraftlineMutation<import("@draftline/client").ApplyIncomingCommandResult>;
|
|
166
|
+
fetch: DraftlineMutation<import("@draftline/client").FetchRemoteResult>;
|
|
167
|
+
mergeIncoming: DraftlineMutation<import("@draftline/client").MergeIncomingCommandResult>;
|
|
168
|
+
preflightApply: DraftlineMutation<import("@draftline/client").ApplyIncomingReport>;
|
|
169
|
+
preflightMerge: DraftlineMutation<import("@draftline/client").MergeIncomingReport>;
|
|
170
|
+
publish: DraftlineMutation<import("@draftline/client").PublishCurrentVariationResult>;
|
|
171
|
+
remote: string;
|
|
172
|
+
};
|
|
173
|
+
export declare function useDraftlineSelectedOperations(selectedPaths: string[]): {
|
|
174
|
+
discard: DraftlineMutation<import("@draftline/client").SelectedDiscardResult>;
|
|
175
|
+
save: DraftlineMutation<import("@draftline/client").SelectedSaveResult>;
|
|
176
|
+
shelve: DraftlineMutation<import("@draftline/client").SelectedShelveResult>;
|
|
177
|
+
};
|
|
178
|
+
export declare function WorkspaceSummaryPanel(): import("react").JSX.Element;
|
|
179
|
+
export declare function WorkspaceStatusRail(): import("react").JSX.Element;
|
|
180
|
+
export declare function SelectedOperationsPanel({ selectedPaths, setSelectedPaths, }: {
|
|
181
|
+
selectedPaths: string;
|
|
182
|
+
setSelectedPaths: (paths: string) => void;
|
|
183
|
+
}): import("react").JSX.Element;
|
|
184
|
+
export declare function OperationsPanel({ remote, selectedPaths, setRemote, setSelectedPaths, }: {
|
|
185
|
+
remote: string;
|
|
186
|
+
selectedPaths: string;
|
|
187
|
+
setRemote: (remote: string) => void;
|
|
188
|
+
setSelectedPaths: (paths: string) => void;
|
|
189
|
+
}): import("react").JSX.Element;
|
|
190
|
+
export declare function RemoteSyncBar({ remote, setRemote, }: {
|
|
191
|
+
remote: string;
|
|
192
|
+
setRemote: (remote: string) => void;
|
|
193
|
+
}): import("react").JSX.Element;
|
|
194
|
+
export declare function ChangedFilesList({ files }: {
|
|
195
|
+
files?: ChangedFile[];
|
|
196
|
+
}): import("react").JSX.Element;
|
|
197
|
+
export declare function VariationList(): import("react").JSX.Element;
|
|
198
|
+
export declare function VersionLane(): import("react").JSX.Element;
|
|
199
|
+
export declare function createDraftlineHistoryGraph({ activeVariationId, dirtyFiles, dirtyMode, includeRemoteTips, laneOrder, orientation, remoteTips, variations, versions, }: DraftlineHistoryGraphInput): DraftlineHistoryGraphModel;
|
|
200
|
+
export declare function createDraftlineVariationListModel({ activeVariationId, getLabel, variations, }: DraftlineVariationListInput): DraftlineVariationListModel;
|
|
201
|
+
export declare function DraftlineHistoryGraph({ ariaLabel, className, getNodeKey, graph, onSelectNode, orientation, renderBadge, renderNode, style, }: DraftlineHistoryGraphProps): import("react").JSX.Element;
|
|
202
|
+
export declare function DraftlineVariationSelect({ activeVariationId, className, getLabel, onSwitch, renderOption, style, variations, }: DraftlineVariationSelectProps): import("react").JSX.Element;
|
|
203
|
+
export declare function RawInspectorPanel(): import("react").JSX.Element;
|
|
204
|
+
export declare function ContentPolicyDiagnosticsPanel(): import("react").JSX.Element;
|
|
205
|
+
export type { JsonValue };
|
|
206
|
+
export type { DraftlineClient } from '@draftline/client';
|
|
207
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { createDraftlineClient, } from '@draftline/client';
|
|
3
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useState, } from 'react';
|
|
4
|
+
const DraftlineContext = createContext(null);
|
|
5
|
+
export function DraftlineProvider({ children, client: clientProp, onError, workspacePath, }) {
|
|
6
|
+
const client = useMemo(() => clientProp ?? createDraftlineClient(), [clientProp]);
|
|
7
|
+
const [commandResult, setCommandResult] = useState(null);
|
|
8
|
+
const [diagnostics, setDiagnostics] = useState(null);
|
|
9
|
+
const [supportRefs, setSupportRefs] = useState([]);
|
|
10
|
+
const [error, setError] = useState(null);
|
|
11
|
+
const [isBusy, setIsBusy] = useState(false);
|
|
12
|
+
const runCommand = useCallback(async (name, command, options = {}) => {
|
|
13
|
+
const manageBusy = options.manageBusy ?? true;
|
|
14
|
+
const recordResult = options.recordResult ?? true;
|
|
15
|
+
if (manageBusy) {
|
|
16
|
+
setIsBusy(true);
|
|
17
|
+
}
|
|
18
|
+
setError(null);
|
|
19
|
+
try {
|
|
20
|
+
const payload = await command();
|
|
21
|
+
if (recordResult) {
|
|
22
|
+
setCommandResult({ name, payload, ok: true });
|
|
23
|
+
}
|
|
24
|
+
return payload;
|
|
25
|
+
}
|
|
26
|
+
catch (caught) {
|
|
27
|
+
setError(caught);
|
|
28
|
+
onError?.(caught, name);
|
|
29
|
+
if (recordResult) {
|
|
30
|
+
setCommandResult({ name, payload: normalizeCommandError(caught), ok: false });
|
|
31
|
+
}
|
|
32
|
+
if (options.rethrow) {
|
|
33
|
+
throw caught;
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
if (manageBusy) {
|
|
39
|
+
setIsBusy(false);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}, [onError]);
|
|
43
|
+
const refresh = useCallback(async (options = {}) => {
|
|
44
|
+
const recordResult = options.recordResult ?? true;
|
|
45
|
+
if (!workspacePath.trim()) {
|
|
46
|
+
const payload = {
|
|
47
|
+
code: 'missing_workspace_path',
|
|
48
|
+
message: 'Enter a workspace path first.',
|
|
49
|
+
};
|
|
50
|
+
if (recordResult) {
|
|
51
|
+
setCommandResult({ name: 'inspect_workspace', ok: false, payload });
|
|
52
|
+
}
|
|
53
|
+
setDiagnostics(null);
|
|
54
|
+
setSupportRefs([]);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
setIsBusy(true);
|
|
58
|
+
try {
|
|
59
|
+
const next = await runCommand('inspect_workspace', () => client.inspectWorkspace(workspacePath), { manageBusy: false, recordResult });
|
|
60
|
+
if (next) {
|
|
61
|
+
setDiagnostics(next);
|
|
62
|
+
const refs = await runCommand('list_support_refs', () => client.listSupportRefs(workspacePath, 'local'), { manageBusy: false, recordResult: false });
|
|
63
|
+
setSupportRefs(refs ?? []);
|
|
64
|
+
}
|
|
65
|
+
return next;
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
setIsBusy(false);
|
|
69
|
+
}
|
|
70
|
+
}, [client, runCommand, workspacePath]);
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
setDiagnostics(null);
|
|
73
|
+
setSupportRefs([]);
|
|
74
|
+
setCommandResult(null);
|
|
75
|
+
setError(null);
|
|
76
|
+
}, [workspacePath]);
|
|
77
|
+
const value = useMemo(() => ({
|
|
78
|
+
client,
|
|
79
|
+
commandResult,
|
|
80
|
+
diagnostics,
|
|
81
|
+
error,
|
|
82
|
+
isBusy,
|
|
83
|
+
refresh,
|
|
84
|
+
runCommand,
|
|
85
|
+
supportRefs,
|
|
86
|
+
workspacePath,
|
|
87
|
+
}), [
|
|
88
|
+
client,
|
|
89
|
+
commandResult,
|
|
90
|
+
diagnostics,
|
|
91
|
+
error,
|
|
92
|
+
isBusy,
|
|
93
|
+
refresh,
|
|
94
|
+
runCommand,
|
|
95
|
+
supportRefs,
|
|
96
|
+
workspacePath,
|
|
97
|
+
]);
|
|
98
|
+
return _jsx(DraftlineContext.Provider, { value: value, children: children });
|
|
99
|
+
}
|
|
100
|
+
export function useDraftlineWorkspace() {
|
|
101
|
+
const context = useDraftlineContext();
|
|
102
|
+
return {
|
|
103
|
+
diagnostics: context.diagnostics,
|
|
104
|
+
error: context.error,
|
|
105
|
+
isBusy: context.isBusy,
|
|
106
|
+
refresh: context.refresh,
|
|
107
|
+
workspacePath: context.workspacePath,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
export function useDraftlineChanges() {
|
|
111
|
+
const { diagnostics } = useDraftlineContext();
|
|
112
|
+
return {
|
|
113
|
+
files: diagnostics?.summary.dirty_files ?? [],
|
|
114
|
+
isDirty: diagnostics?.summary.is_dirty ?? false,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
export function useDraftlineInspector() {
|
|
118
|
+
const context = useDraftlineContext();
|
|
119
|
+
return {
|
|
120
|
+
commandResult: context.commandResult,
|
|
121
|
+
diagnostics: context.diagnostics,
|
|
122
|
+
supportRefs: context.supportRefs,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
export function useDraftlineHistoryGraph(options = {}) {
|
|
126
|
+
const { diagnostics } = useDraftlineWorkspace();
|
|
127
|
+
return useMemo(() => createDraftlineHistoryGraph({
|
|
128
|
+
activeVariationId: diagnostics?.summary.active_variation.id,
|
|
129
|
+
dirtyFiles: diagnostics?.summary.dirty_files,
|
|
130
|
+
dirtyMode: options.dirtyMode,
|
|
131
|
+
includeRemoteTips: options.includeRemoteTips,
|
|
132
|
+
laneOrder: options.laneOrder,
|
|
133
|
+
orientation: options.orientation,
|
|
134
|
+
remoteTips: options.remoteTips,
|
|
135
|
+
variations: diagnostics?.summary.variations,
|
|
136
|
+
versions: diagnostics?.summary.versions,
|
|
137
|
+
}), [
|
|
138
|
+
diagnostics,
|
|
139
|
+
options.dirtyMode,
|
|
140
|
+
options.includeRemoteTips,
|
|
141
|
+
options.laneOrder,
|
|
142
|
+
options.orientation,
|
|
143
|
+
options.remoteTips,
|
|
144
|
+
]);
|
|
145
|
+
}
|
|
146
|
+
export function useDraftlineSupportRefs(scope = 'local') {
|
|
147
|
+
const context = useDraftlineContext();
|
|
148
|
+
const load = useDraftlineMutation('list_support_refs', () => context.client.listSupportRefs(context.workspacePath, scope));
|
|
149
|
+
return {
|
|
150
|
+
load,
|
|
151
|
+
supportRefs: scope === 'local' ? context.supportRefs : [],
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
export function useDraftlineRemoteSync(remote) {
|
|
155
|
+
const context = useDraftlineContext();
|
|
156
|
+
const request = { workspace_path: context.workspacePath, remote };
|
|
157
|
+
const publish = useDraftlineMutation('publish_current_variation', async () => {
|
|
158
|
+
const result = await context.client.publishCurrentVariation(request);
|
|
159
|
+
await context.refresh({ recordResult: false });
|
|
160
|
+
return result;
|
|
161
|
+
});
|
|
162
|
+
const fetch = useDraftlineMutation('fetch_remote', async () => {
|
|
163
|
+
const result = await context.client.fetchRemote(request);
|
|
164
|
+
await context.refresh({ recordResult: false });
|
|
165
|
+
return result;
|
|
166
|
+
});
|
|
167
|
+
const preflightApply = useDraftlineMutation('preflight_apply_incoming', () => context.client.preflightApplyIncoming(request));
|
|
168
|
+
const applyIncoming = useDraftlineMutation('apply_incoming', async () => {
|
|
169
|
+
const result = await context.client.applyIncoming(request);
|
|
170
|
+
await context.refresh({ recordResult: false });
|
|
171
|
+
return result;
|
|
172
|
+
});
|
|
173
|
+
const preflightMerge = useDraftlineMutation('preflight_merge_incoming', () => context.client.preflightMergeIncoming(request));
|
|
174
|
+
const mergeIncoming = useDraftlineMutation('merge_incoming', async () => {
|
|
175
|
+
const result = await context.client.mergeIncoming({
|
|
176
|
+
...request,
|
|
177
|
+
label: 'Workbench merge incoming',
|
|
178
|
+
});
|
|
179
|
+
await context.refresh({ recordResult: false });
|
|
180
|
+
return result;
|
|
181
|
+
});
|
|
182
|
+
return {
|
|
183
|
+
applyIncoming,
|
|
184
|
+
fetch,
|
|
185
|
+
mergeIncoming,
|
|
186
|
+
preflightApply,
|
|
187
|
+
preflightMerge,
|
|
188
|
+
publish,
|
|
189
|
+
remote,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
export function useDraftlineSelectedOperations(selectedPaths) {
|
|
193
|
+
const context = useDraftlineContext();
|
|
194
|
+
const request = { workspace_path: context.workspacePath, paths: selectedPaths };
|
|
195
|
+
const save = useDraftlineMutation('selected_save', async () => {
|
|
196
|
+
const result = await context.client.selectedSave({ ...request, label: 'Workbench save' });
|
|
197
|
+
await context.refresh({ recordResult: false });
|
|
198
|
+
return result;
|
|
199
|
+
});
|
|
200
|
+
const shelve = useDraftlineMutation('selected_shelve', async () => {
|
|
201
|
+
const result = await context.client.selectedShelve({ ...request, name: 'workbench-shelf' });
|
|
202
|
+
await context.refresh({ recordResult: false });
|
|
203
|
+
return result;
|
|
204
|
+
});
|
|
205
|
+
const discard = useDraftlineMutation('selected_discard', async () => {
|
|
206
|
+
const result = await context.client.selectedDiscard(request);
|
|
207
|
+
await context.refresh({ recordResult: false });
|
|
208
|
+
return result;
|
|
209
|
+
});
|
|
210
|
+
return { discard, save, shelve };
|
|
211
|
+
}
|
|
212
|
+
export function WorkspaceSummaryPanel() {
|
|
213
|
+
const { diagnostics } = useDraftlineWorkspace();
|
|
214
|
+
const summary = diagnostics?.summary;
|
|
215
|
+
const inspection = diagnostics?.inspection;
|
|
216
|
+
const lock = diagnostics?.operation_lock;
|
|
217
|
+
return (_jsxs("article", { className: "panel panel-large", children: [_jsxs("div", { className: "panel-heading", children: [_jsx("h2", { children: "Workspace" }), _jsx(StatusPill, { tone: summary?.is_dirty ? 'warn' : 'ok', children: summary?.is_dirty ? 'Dirty' : 'Ready' })] }), _jsxs("dl", { className: "metric-grid", children: [_jsx(Metric, { label: "Current variation", value: summary?.active_variation.name ?? '-' }), _jsx(Metric, { label: "Versions", value: String(summary?.versions.length ?? 0) }), _jsx(Metric, { label: "Dirty files", value: String(summary?.dirty_files.length ?? 0) }), _jsx(Metric, { label: "Sharing", value: inspection?.sharing_mode ?? '-' }), _jsx(Metric, { label: "Recovery", value: summary?.recovery ? 'Present' : 'Clear' }), _jsx(Metric, { label: "Lock", value: lock?.state ?? '-' })] }), _jsx("div", { className: "safe-actions", children: (inspection?.safe_next_actions ?? ['normal_work']).map((action) => (_jsx("span", { children: action }, action))) })] }));
|
|
218
|
+
}
|
|
219
|
+
export function WorkspaceStatusRail() {
|
|
220
|
+
const { diagnostics, isBusy } = useDraftlineWorkspace();
|
|
221
|
+
const summary = diagnostics?.summary;
|
|
222
|
+
const inspection = diagnostics?.inspection;
|
|
223
|
+
const lock = diagnostics?.operation_lock;
|
|
224
|
+
return (_jsxs("article", { className: "panel status-rail", children: [_jsx("h2", { children: "Status rail" }), _jsxs("div", { className: "rail-items", children: [_jsx(RailItem, { label: "Contract", tone: "ok", value: "Tauri invoke" }), _jsx(RailItem, { label: "Command", tone: isBusy ? 'warn' : 'ok', value: isBusy ? 'Busy' : 'Idle' }), _jsx(RailItem, { label: "Content", tone: summary?.is_dirty ? 'warn' : 'ok', value: summary?.is_dirty ? `${summary.dirty_files.length} dirty` : 'Clean' }), _jsx(RailItem, { label: "Recovery", tone: summary?.recovery ? 'danger' : 'ok', value: summary?.recovery ? summary.recovery.operation : 'Clear' }), _jsx(RailItem, { label: "Lock", tone: lock?.state === 'locked' ? 'danger' : 'ok', value: lock?.state ?? 'unknown' }), _jsx(RailItem, { label: "Remote", tone: (inspection?.remotes.length ?? 0) > 0 ? 'ok' : 'neutral', value: `${inspection?.remotes.length ?? 0} configured` })] })] }));
|
|
225
|
+
}
|
|
226
|
+
export function SelectedOperationsPanel({ selectedPaths, setSelectedPaths, }) {
|
|
227
|
+
const selectedPathList = useMemo(() => splitSelectedPaths(selectedPaths), [selectedPaths]);
|
|
228
|
+
return (_jsx(SelectedOperationsForm, { selectedPathList: selectedPathList, selectedPaths: selectedPaths, setSelectedPaths: setSelectedPaths }));
|
|
229
|
+
}
|
|
230
|
+
function SelectedOperationsForm({ selectedPathList, selectedPaths, setSelectedPaths, }) {
|
|
231
|
+
const { isBusy } = useDraftlineWorkspace();
|
|
232
|
+
const operations = useDraftlineSelectedOperations(selectedPathList);
|
|
233
|
+
return (_jsxs("form", { className: "stack", onSubmit: (event) => void submitMutation(event, operations.save), children: [_jsx("label", { htmlFor: "selected-paths", children: "Selected paths" }), _jsx("input", { id: "selected-paths", value: selectedPaths, onChange: (event) => setSelectedPaths(event.target.value), placeholder: "post.md, notes/idea.md" }), _jsxs("div", { className: "button-row", children: [_jsx("button", { disabled: isBusy, type: "submit", children: "Save" }), _jsx("button", { disabled: isBusy, onClick: () => void operations.shelve.run(), type: "button", children: "Shelve" }), _jsx("button", { disabled: isBusy, onClick: () => void operations.discard.run(), type: "button", children: "Discard" })] })] }));
|
|
234
|
+
}
|
|
235
|
+
export function OperationsPanel({ remote, selectedPaths, setRemote, setSelectedPaths, }) {
|
|
236
|
+
return (_jsxs("article", { className: "panel", children: [_jsx("h2", { children: "Operations" }), _jsx(SelectedOperationsPanel, { selectedPaths: selectedPaths, setSelectedPaths: setSelectedPaths }), _jsx(RemoteSyncBar, { remote: remote, setRemote: setRemote })] }));
|
|
237
|
+
}
|
|
238
|
+
export function RemoteSyncBar({ remote, setRemote, }) {
|
|
239
|
+
const { isBusy } = useDraftlineWorkspace();
|
|
240
|
+
const sync = useDraftlineRemoteSync(remote);
|
|
241
|
+
return (_jsxs("form", { className: "stack", onSubmit: (event) => void submitMutation(event, sync.publish), children: [_jsx("label", { htmlFor: "remote-name", children: "Remote" }), _jsxs("div", { className: "inline-form", children: [_jsx("input", { id: "remote-name", value: remote, onChange: (event) => setRemote(event.target.value) }), _jsx("button", { disabled: isBusy, type: "submit", children: "Publish" })] }), _jsxs("div", { className: "button-row", children: [_jsx("button", { disabled: isBusy, onClick: () => void sync.fetch.run(), type: "button", children: "Fetch" }), _jsx("button", { disabled: isBusy, onClick: () => void sync.preflightApply.run(), type: "button", children: "Preflight apply" }), _jsx("button", { disabled: isBusy, onClick: () => void sync.applyIncoming.run(), type: "button", children: "Apply incoming" }), _jsx("button", { disabled: isBusy, onClick: () => void sync.preflightMerge.run(), type: "button", children: "Preflight merge" }), _jsx("button", { disabled: isBusy, onClick: () => void sync.mergeIncoming.run(), type: "button", children: "Merge incoming" })] })] }));
|
|
242
|
+
}
|
|
243
|
+
export function ChangedFilesList({ files }) {
|
|
244
|
+
const contextFiles = useDraftlineChanges().files;
|
|
245
|
+
const resolvedFiles = files ?? contextFiles;
|
|
246
|
+
const groups = groupChangedFiles(resolvedFiles);
|
|
247
|
+
return (_jsxs("article", { className: "panel", children: [_jsx("h2", { children: "Dirty files" }), resolvedFiles.length === 0 ? (_jsx("p", { className: "empty", children: "No tracked content changes." })) : (_jsx("div", { className: "file-groups", children: groups.map(([kind, groupedFiles]) => (_jsxs("section", { className: "file-group", children: [_jsxs("div", { className: "group-heading", children: [_jsx("h3", { children: kind }), _jsx(StatusPill, { tone: "neutral", children: String(groupedFiles.length) })] }), _jsx("ul", { className: "item-list", children: groupedFiles.map((file) => (_jsxs("li", { children: [_jsx("span", { children: file.path }), _jsxs("span", { className: "badge-row", children: [file.is_large ? _jsx(StatusPill, { tone: "warn", children: "large" }) : null, file.is_binary ? _jsx(StatusPill, { tone: "warn", children: "binary" }) : null] })] }, file.path))) })] }, kind))) }))] }));
|
|
248
|
+
}
|
|
249
|
+
export function VariationList() {
|
|
250
|
+
const { diagnostics } = useDraftlineWorkspace();
|
|
251
|
+
const variations = diagnostics?.summary.variations ?? [];
|
|
252
|
+
const activeVariationId = diagnostics?.summary.active_variation.id;
|
|
253
|
+
return (_jsxs("article", { className: "panel", children: [_jsx("h2", { children: "Variations" }), variations.length === 0 ? (_jsx("p", { className: "empty", children: "Inspect a workspace to list variations." })) : (_jsx(DraftlineVariationSelect, { activeVariationId: activeVariationId, variations: variations }))] }));
|
|
254
|
+
}
|
|
255
|
+
export function VersionLane() {
|
|
256
|
+
const graph = useDraftlineHistoryGraph();
|
|
257
|
+
return (_jsxs("article", { className: "panel panel-large", children: [_jsxs("div", { className: "panel-heading", children: [_jsx("h2", { children: "Version lane" }), graph.dirtyNode ? _jsx(StatusPill, { tone: "warn", children: "unsaved changes" }) : null] }), graph.nodes.length === 0 ? (_jsx("p", { className: "empty", children: "Inspect a workspace to render version history." })) : (_jsx(DraftlineHistoryGraph, { graph: graph }))] }));
|
|
258
|
+
}
|
|
259
|
+
export function createDraftlineHistoryGraph({ activeVariationId, dirtyFiles = [], dirtyMode = 'node', includeRemoteTips = true, laneOrder = 'activeFirst', orientation = 'vertical', remoteTips = [], variations = [], versions = [], }) {
|
|
260
|
+
const activeVariation = variations.find((variation) => variation.id === activeVariationId);
|
|
261
|
+
const activeLane = {
|
|
262
|
+
id: activeVariation?.id ?? activeVariationId ?? 'active',
|
|
263
|
+
label: activeVariation?.metadata.label ?? activeVariation?.name ?? 'Current variation',
|
|
264
|
+
variationId: activeVariation?.id ?? activeVariationId ?? null,
|
|
265
|
+
};
|
|
266
|
+
const lanes = [activeLane];
|
|
267
|
+
const nodes = [];
|
|
268
|
+
const edges = [];
|
|
269
|
+
const activeVariationIds = activeLane.variationId ? [activeLane.variationId] : [];
|
|
270
|
+
let rowOffset = 0;
|
|
271
|
+
let dirtyNode;
|
|
272
|
+
if (dirtyMode === 'node' && dirtyFiles.length > 0) {
|
|
273
|
+
dirtyNode = {
|
|
274
|
+
baseVersionId: versions[0]?.id ?? null,
|
|
275
|
+
changedFiles: dirtyFiles,
|
|
276
|
+
changedCount: dirtyFiles.length,
|
|
277
|
+
id: 'dirty',
|
|
278
|
+
isActiveHead: true,
|
|
279
|
+
isHead: true,
|
|
280
|
+
kind: 'dirty',
|
|
281
|
+
label: 'Unsaved changes',
|
|
282
|
+
lane: 0,
|
|
283
|
+
parentVersionIds: versions[0] ? [versions[0].id] : [],
|
|
284
|
+
row: 0,
|
|
285
|
+
variationId: activeLane.variationId,
|
|
286
|
+
variationIds: activeVariationIds,
|
|
287
|
+
};
|
|
288
|
+
nodes.push(dirtyNode);
|
|
289
|
+
rowOffset = 1;
|
|
290
|
+
}
|
|
291
|
+
versions.forEach((version, index) => {
|
|
292
|
+
const node = {
|
|
293
|
+
id: `version:${version.id}`,
|
|
294
|
+
isActiveHead: index === 0 && !dirtyNode,
|
|
295
|
+
isHead: index === 0,
|
|
296
|
+
kind: 'version',
|
|
297
|
+
label: version.label || version.id.slice(0, 7),
|
|
298
|
+
lane: 0,
|
|
299
|
+
parentVersionIds: index + 1 < versions.length ? [versions[index + 1].id] : [],
|
|
300
|
+
row: index + rowOffset,
|
|
301
|
+
variationId: activeLane.variationId,
|
|
302
|
+
variationIds: activeVariationIds,
|
|
303
|
+
version,
|
|
304
|
+
versionId: version.id,
|
|
305
|
+
};
|
|
306
|
+
nodes.push(node);
|
|
307
|
+
if (index > 0) {
|
|
308
|
+
edges.push({
|
|
309
|
+
from: `version:${versions[index].id}`,
|
|
310
|
+
kind: 'parent',
|
|
311
|
+
to: `version:${versions[index - 1].id}`,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
if (dirtyNode && versions[0]) {
|
|
316
|
+
edges.push({ from: `version:${versions[0].id}`, kind: 'parent', to: dirtyNode.id });
|
|
317
|
+
}
|
|
318
|
+
const versionNodeIds = new Set(versions.map((version) => `version:${version.id}`));
|
|
319
|
+
const remoteTipRowOffset = rowOffset + versions.length;
|
|
320
|
+
const tips = includeRemoteTips ? remoteTips : [];
|
|
321
|
+
tips.forEach((tip, index) => {
|
|
322
|
+
const lane = lanes.findIndex((existing) => existing.id === `remote:${tip.remote}:${tip.variationId ?? tip.id}`);
|
|
323
|
+
const laneIndex = lane >= 0
|
|
324
|
+
? lane
|
|
325
|
+
: lanes.push({
|
|
326
|
+
id: `remote:${tip.remote}:${tip.variationId ?? tip.id}`,
|
|
327
|
+
label: tip.label ?? `${tip.remote}/${tip.variationId ?? tip.id}`,
|
|
328
|
+
variationId: tip.variationId,
|
|
329
|
+
}) - 1;
|
|
330
|
+
const node = {
|
|
331
|
+
id: `remote:${tip.id}`,
|
|
332
|
+
isActiveHead: false,
|
|
333
|
+
isHead: true,
|
|
334
|
+
kind: 'remoteTip',
|
|
335
|
+
label: tip.label ?? tip.id,
|
|
336
|
+
lane: laneIndex,
|
|
337
|
+
parentVersionIds: tip.versionId ? [tip.versionId] : [],
|
|
338
|
+
remote: tip.remote,
|
|
339
|
+
row: remoteTipRowOffset + index,
|
|
340
|
+
variationId: tip.variationId,
|
|
341
|
+
variationIds: tip.variationId ? [tip.variationId] : [],
|
|
342
|
+
versionId: tip.versionId ?? undefined,
|
|
343
|
+
};
|
|
344
|
+
nodes.push(node);
|
|
345
|
+
if (tip.versionId && versionNodeIds.has(`version:${tip.versionId}`)) {
|
|
346
|
+
edges.push({ from: `version:${tip.versionId}`, kind: 'remoteTip', to: node.id });
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
const orderedLanes = orderGraphLanes(lanes, laneOrder);
|
|
350
|
+
const orderedNodes = nodes.map((node) => ({
|
|
351
|
+
...node,
|
|
352
|
+
lane: orderedLanes.indexByOriginalLane.get(node.lane) ?? node.lane,
|
|
353
|
+
}));
|
|
354
|
+
const orderedDirtyNode = dirtyNode
|
|
355
|
+
? orderedNodes.find((node) => node.id === dirtyNode.id && node.kind === 'dirty')
|
|
356
|
+
: undefined;
|
|
357
|
+
return {
|
|
358
|
+
dirtyNode: orderedDirtyNode,
|
|
359
|
+
edges,
|
|
360
|
+
lanes: orderedLanes.lanes,
|
|
361
|
+
nodes: orderedNodes,
|
|
362
|
+
orientation,
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
export function createDraftlineVariationListModel({ activeVariationId, getLabel = defaultVariationLabel, variations, }) {
|
|
366
|
+
return {
|
|
367
|
+
activeVariationId,
|
|
368
|
+
items: variations.map((variation) => ({
|
|
369
|
+
id: variation.id,
|
|
370
|
+
isActive: activeVariationId ? variation.id === activeVariationId : variation.is_current,
|
|
371
|
+
label: getLabel(variation),
|
|
372
|
+
variation,
|
|
373
|
+
})),
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
export function DraftlineHistoryGraph({ ariaLabel = 'Draftline history graph', className, getNodeKey = (node) => node.id, graph, onSelectNode, orientation = graph.orientation, renderBadge, renderNode, style, }) {
|
|
377
|
+
return (_jsx("ol", { "aria-label": ariaLabel, className: ['version-lane', className].filter(Boolean).join(' '), "data-orientation": orientation, "data-testid": "draftline-history-graph", id: "draftline-history-graph", style: style, children: [...graph.nodes].sort(compareGraphNodes).map((node) => (_jsx(DraftlineHistoryGraphRow, { getNodeKey: getNodeKey, graph: graph, node: node, onSelectNode: onSelectNode, renderBadge: renderBadge, renderNode: renderNode }, getNodeKey(node)))) }));
|
|
378
|
+
}
|
|
379
|
+
function DraftlineHistoryGraphRow({ getNodeKey, graph, node, onSelectNode, renderBadge, renderNode, }) {
|
|
380
|
+
const selectable = Boolean(onSelectNode);
|
|
381
|
+
const context = { graph, selectable };
|
|
382
|
+
const notifyNodeSelected = (nativeEvent) => {
|
|
383
|
+
const result = onSelectNode?.({ graph, nativeEvent, node, selectable });
|
|
384
|
+
if (result) {
|
|
385
|
+
void Promise.resolve(result).catch((error) => {
|
|
386
|
+
setTimeout(() => {
|
|
387
|
+
throw error;
|
|
388
|
+
}, 0);
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
const handleKeyDown = (event) => {
|
|
393
|
+
if (event.key !== 'Enter' && event.key !== ' ') {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
// Preserve keyboard event context for hosts while avoiding a second synthesized click.
|
|
397
|
+
event.preventDefault();
|
|
398
|
+
notifyNodeSelected(event);
|
|
399
|
+
};
|
|
400
|
+
return (_jsxs("li", { className: node.kind === 'dirty' ? 'version-row dirty-row' : 'version-row', style: { ['--draftline-lane']: node.lane }, children: [_jsx("span", { className: "graph-line", children: _jsx("span", { className: node.kind === 'dirty'
|
|
401
|
+
? 'graph-node dirty-node'
|
|
402
|
+
: node.isHead
|
|
403
|
+
? 'graph-node head-node'
|
|
404
|
+
: 'graph-node' }) }), onSelectNode ? (_jsx("button", { "aria-current": node.isActiveHead ? 'step' : undefined, className: "graph-node-button", onClick: (event) => notifyNodeSelected(event), onKeyDown: handleKeyDown, type: "button", children: renderNode ? renderNode(node, context) : _jsx(DefaultGraphNode, { node: node }) })) : (_jsx("div", { className: "graph-node-content", children: renderNode ? renderNode(node, context) : _jsx(DefaultGraphNode, { node: node }) })), _jsxs("span", { className: "badge-row", children: [renderBadge?.(node, context), node.isActiveHead ? _jsx(StatusPill, { tone: "ok", children: "HEAD" }) : null, node.kind === 'remoteTip' ? _jsx(StatusPill, { tone: "neutral", children: node.remote }) : null] })] }, getNodeKey(node)));
|
|
405
|
+
}
|
|
406
|
+
export function DraftlineVariationSelect({ activeVariationId, className, getLabel = defaultVariationLabel, onSwitch, renderOption, style, variations, }) {
|
|
407
|
+
const model = createDraftlineVariationListModel({ activeVariationId, getLabel, variations });
|
|
408
|
+
return (_jsx("ul", { className: ['item-list', className].filter(Boolean).join(' '), "data-testid": "draftline-variation-select", id: "draftline-variation-select", style: style, children: model.items.map(({ isActive, label, variation }) => {
|
|
409
|
+
const switchVariation = () => onSwitch?.(variation);
|
|
410
|
+
return (_jsx("li", { children: renderOption ? (renderOption(variation, { isActive, label, switchVariation })) : (onSwitch ? (_jsxs("button", { className: "variation-option", onClick: switchVariation, type: "button", children: [_jsx("span", { children: label }), isActive ? _jsx(StatusPill, { tone: "ok", children: "current" }) : null] })) : (_jsxs("div", { className: "variation-option", children: [_jsx("span", { children: label }), isActive ? _jsx(StatusPill, { tone: "ok", children: "current" }) : null] }))) }, variation.id));
|
|
411
|
+
}) }));
|
|
412
|
+
}
|
|
413
|
+
export function RawInspectorPanel() {
|
|
414
|
+
const [selectedTab, setSelectedTab] = useState('json');
|
|
415
|
+
const { commandResult, diagnostics, supportRefs } = useDraftlineInspector();
|
|
416
|
+
return (_jsxs("article", { className: "panel panel-json", children: [_jsxs("div", { className: "panel-heading", children: [_jsx("h2", { children: "Inspector" }), commandResult ? (_jsx(StatusPill, { tone: commandResult.ok ? 'ok' : 'danger', children: commandResult.name })) : null] }), _jsxs("div", { className: "tab-row", children: [_jsx(TabButton, { current: selectedTab, setCurrent: setSelectedTab, tab: "json", children: "Raw JSON" }), _jsx(TabButton, { current: selectedTab, setCurrent: setSelectedTab, tab: "recovery", children: "Recovery & locks" }), _jsx(TabButton, { current: selectedTab, setCurrent: setSelectedTab, tab: "supportRefs", children: "Support refs" })] }), selectedTab === 'json' ? _jsx(JsonPanel, { result: commandResult }) : null, selectedTab === 'recovery' ? _jsx(RecoveryPanel, { diagnostics: diagnostics }) : null, selectedTab === 'supportRefs' ? _jsx(SupportRefsPanel, { supportRefs: supportRefs }) : null] }));
|
|
417
|
+
}
|
|
418
|
+
export function ContentPolicyDiagnosticsPanel() {
|
|
419
|
+
const { diagnostics } = useDraftlineWorkspace();
|
|
420
|
+
const diagnosticsList = diagnostics?.verification.diagnostics ?? [];
|
|
421
|
+
return (_jsxs("article", { className: "panel", children: [_jsx("h2", { children: "Content policy" }), diagnosticsList.length === 0 ? (_jsx("p", { className: "empty", children: "No content policy diagnostics." })) : (_jsx("ul", { className: "item-list", children: diagnosticsList.map((diagnostic) => (_jsxs("li", { children: [_jsx("span", { children: diagnostic.message }), _jsx(StatusPill, { tone: diagnostic.severity === 'blocking' ? 'danger' : 'warn', children: diagnostic.code })] }, `${diagnostic.code}-${diagnostic.message}`))) }))] }));
|
|
422
|
+
}
|
|
423
|
+
function useDraftlineContext() {
|
|
424
|
+
const context = useContext(DraftlineContext);
|
|
425
|
+
if (!context) {
|
|
426
|
+
throw new Error('Draftline hooks must be used inside DraftlineProvider.');
|
|
427
|
+
}
|
|
428
|
+
return context;
|
|
429
|
+
}
|
|
430
|
+
function useDraftlineMutation(name, command) {
|
|
431
|
+
const context = useDraftlineContext();
|
|
432
|
+
const [error, setError] = useState(null);
|
|
433
|
+
const [isPending, setIsPending] = useState(false);
|
|
434
|
+
return {
|
|
435
|
+
error,
|
|
436
|
+
isPending,
|
|
437
|
+
reset: () => setError(null),
|
|
438
|
+
run: async () => {
|
|
439
|
+
setIsPending(true);
|
|
440
|
+
setError(null);
|
|
441
|
+
try {
|
|
442
|
+
return await context.runCommand(name, command, { rethrow: true });
|
|
443
|
+
}
|
|
444
|
+
catch (caught) {
|
|
445
|
+
setError(caught);
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
finally {
|
|
449
|
+
setIsPending(false);
|
|
450
|
+
}
|
|
451
|
+
},
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
function RailItem({ label, tone, value, }) {
|
|
455
|
+
return (_jsxs("div", { className: "rail-item", children: [_jsx("span", { children: label }), _jsx(StatusPill, { tone: tone, children: value })] }));
|
|
456
|
+
}
|
|
457
|
+
function DefaultGraphNode({ node }) {
|
|
458
|
+
if (node.kind === 'dirty') {
|
|
459
|
+
return (_jsxs("span", { children: [_jsx("strong", { children: "Unsaved changes" }), _jsxs("small", { children: [node.changedCount ?? 0, " tracked file(s) need save, shelf, or discard."] })] }));
|
|
460
|
+
}
|
|
461
|
+
if (node.kind === 'remoteTip') {
|
|
462
|
+
return (_jsxs("span", { children: [_jsx("strong", { children: node.label }), _jsxs("small", { children: [node.remote ?? 'remote', " tip"] })] }));
|
|
463
|
+
}
|
|
464
|
+
return (_jsxs("span", { children: [_jsx("strong", { children: node.label }), _jsxs("small", { children: [node.version?.author.name ?? 'Unknown', " -", ' ', node.version ? new Date(node.version.time_seconds * 1000).toLocaleString() : 'unsaved'] })] }));
|
|
465
|
+
}
|
|
466
|
+
function defaultVariationLabel(variation) {
|
|
467
|
+
return variation.metadata.label ?? variation.name;
|
|
468
|
+
}
|
|
469
|
+
function compareGraphNodes(left, right) {
|
|
470
|
+
if (left.row !== right.row) {
|
|
471
|
+
return left.row - right.row;
|
|
472
|
+
}
|
|
473
|
+
return left.lane - right.lane;
|
|
474
|
+
}
|
|
475
|
+
function orderGraphLanes(lanes, laneOrder) {
|
|
476
|
+
const indexed = lanes.map((lane, originalIndex) => ({ lane, originalIndex }));
|
|
477
|
+
if (typeof laneOrder === 'function') {
|
|
478
|
+
indexed.sort((left, right) => laneOrder(left.lane, right.lane));
|
|
479
|
+
}
|
|
480
|
+
else if (laneOrder === 'activeFirst') {
|
|
481
|
+
indexed.sort((left, right) => {
|
|
482
|
+
if (left.originalIndex === 0) {
|
|
483
|
+
return -1;
|
|
484
|
+
}
|
|
485
|
+
if (right.originalIndex === 0) {
|
|
486
|
+
return 1;
|
|
487
|
+
}
|
|
488
|
+
return left.originalIndex - right.originalIndex;
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
const indexByOriginalLane = new Map();
|
|
492
|
+
indexed.forEach(({ originalIndex }, nextIndex) => {
|
|
493
|
+
indexByOriginalLane.set(originalIndex, nextIndex);
|
|
494
|
+
});
|
|
495
|
+
return {
|
|
496
|
+
indexByOriginalLane,
|
|
497
|
+
lanes: indexed.map(({ lane }) => lane),
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
function RecoveryPanel({ diagnostics }) {
|
|
501
|
+
const recovery = diagnostics?.summary.recovery;
|
|
502
|
+
const lock = diagnostics?.operation_lock;
|
|
503
|
+
const diagnosticsList = diagnostics?.verification.diagnostics ?? [];
|
|
504
|
+
return (_jsxs("div", { className: "inspector-card", children: [_jsxs("dl", { className: "metric-grid compact", children: [_jsx(Metric, { label: "Recovery", value: recovery ? recovery.operation : 'Clear' }), _jsx(Metric, { label: "State inconsistent", value: String(diagnostics?.summary.state_may_be_inconsistent ?? false) }), _jsx(Metric, { label: "Lock", value: lock?.state ?? '-' })] }), diagnosticsList.length === 0 ? (_jsx("p", { className: "empty", children: "No verification diagnostics." })) : (_jsx("ul", { className: "item-list", children: diagnosticsList.map((diagnostic) => (_jsxs("li", { children: [_jsx("span", { children: diagnostic.message }), _jsx(StatusPill, { tone: diagnostic.severity === 'blocking' ? 'danger' : 'warn', children: diagnostic.code })] }, `${diagnostic.code}-${diagnostic.message}`))) })), lock?.metadata ? _jsx("pre", { children: JSON.stringify(lock.metadata, null, 2) }) : null] }));
|
|
505
|
+
}
|
|
506
|
+
function SupportRefsPanel({ supportRefs }) {
|
|
507
|
+
return (_jsx("div", { className: "inspector-card", children: supportRefs.length === 0 ? (_jsx("p", { className: "empty", children: "No local recovery support refs." })) : (_jsx("ul", { className: "item-list", children: supportRefs.map((supportRef) => (_jsxs("li", { children: [_jsx("span", { children: supportRef.source_variation ?? supportRef.id }), _jsx(StatusPill, { tone: "neutral", children: supportRef.kind })] }, supportRef.id))) })) }));
|
|
508
|
+
}
|
|
509
|
+
function JsonPanel({ result }) {
|
|
510
|
+
return (_jsx("div", { className: "inspector-card", children: _jsx("pre", { children: result ? JSON.stringify(result.payload, null, 2) : 'Run a command to inspect JSON.' }) }));
|
|
511
|
+
}
|
|
512
|
+
function TabButton({ children, current, setCurrent, tab, }) {
|
|
513
|
+
return (_jsx("button", { className: current === tab ? 'tab-button tab-button-active' : 'tab-button', onClick: () => setCurrent(tab), type: "button", children: children }));
|
|
514
|
+
}
|
|
515
|
+
function Metric({ label, value }) {
|
|
516
|
+
return (_jsxs("div", { children: [_jsx("dt", { children: label }), _jsx("dd", { children: value })] }));
|
|
517
|
+
}
|
|
518
|
+
function StatusPill({ children, tone, }) {
|
|
519
|
+
return _jsx("span", { className: `pill pill-${tone}`, children: children });
|
|
520
|
+
}
|
|
521
|
+
function normalizeCommandError(error) {
|
|
522
|
+
if (error && typeof error === 'object' && 'code' in error && 'message' in error) {
|
|
523
|
+
return error;
|
|
524
|
+
}
|
|
525
|
+
return {
|
|
526
|
+
code: 'unknown_error',
|
|
527
|
+
message: error instanceof Error ? error.message : String(error),
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
function groupChangedFiles(files) {
|
|
531
|
+
const groups = new Map();
|
|
532
|
+
for (const file of files) {
|
|
533
|
+
groups.set(file.kind, [...(groups.get(file.kind) ?? []), file]);
|
|
534
|
+
}
|
|
535
|
+
return [...groups.entries()];
|
|
536
|
+
}
|
|
537
|
+
function splitSelectedPaths(paths) {
|
|
538
|
+
return paths
|
|
539
|
+
.split(',')
|
|
540
|
+
.map((path) => path.trim())
|
|
541
|
+
.filter(Boolean);
|
|
542
|
+
}
|
|
543
|
+
async function submitMutation(event, mutation) {
|
|
544
|
+
event.preventDefault();
|
|
545
|
+
await mutation.run();
|
|
546
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@draftline/react",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc -p tsconfig.json",
|
|
18
|
+
"test": "vitest run --environment jsdom"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@draftline/client": "0.1.0"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"react": "^19.0.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@testing-library/dom": "^10.4.1",
|
|
28
|
+
"@testing-library/react": "^16.3.2",
|
|
29
|
+
"@types/react": "^19.2.7",
|
|
30
|
+
"jsdom": "^29.1.1",
|
|
31
|
+
"typescript": "^6.0.3",
|
|
32
|
+
"vitest": "^4.1.9"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
}
|
|
37
|
+
}
|