@shivaedev/work-board 0.14.1 → 0.15.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 +26 -0
- package/README.md +1 -1
- package/assets/agent.js +1 -1
- package/assets/native.js +25 -25
- package/assets/react.js +38 -37
- package/dist/browser/diagnostics/schema.d.ts +15 -12
- package/dist/browser/diagnostics/schema.d.ts.map +1 -1
- package/dist/browser/diagnostics/schema.js +12 -2
- package/dist/browser/diagnostics/schema.js.map +1 -1
- package/dist/browser/views/remaining.d.ts +20 -12
- package/dist/browser/views/remaining.d.ts.map +1 -1
- package/dist/client/client.d.ts +1 -0
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +1 -1
- package/dist/client/client.js.map +1 -1
- package/dist/client/formStatus.d.ts +12 -0
- package/dist/client/formStatus.d.ts.map +1 -0
- package/dist/client/formStatus.js +27 -0
- package/dist/client/formStatus.js.map +1 -0
- package/dist/client/react/BoardShell.d.ts.map +1 -1
- package/dist/client/react/BoardShell.js +15 -21
- package/dist/client/react/BoardShell.js.map +1 -1
- package/dist/client/react/DocumentContext.d.ts.map +1 -1
- package/dist/client/react/DocumentContext.js +6 -3
- package/dist/client/react/DocumentContext.js.map +1 -1
- package/dist/client/react/FormContext.d.ts +5 -0
- package/dist/client/react/FormContext.d.ts.map +1 -0
- package/dist/client/react/FormContext.js +10 -0
- package/dist/client/react/FormContext.js.map +1 -0
- package/dist/client/react/LiveControls.d.ts +4 -3
- package/dist/client/react/LiveControls.d.ts.map +1 -1
- package/dist/client/react/LiveControls.js +5 -19
- package/dist/client/react/LiveControls.js.map +1 -1
- package/dist/client/react/MainLocationContext.d.ts +3 -0
- package/dist/client/react/MainLocationContext.d.ts.map +1 -0
- package/dist/client/react/MainLocationContext.js +7 -0
- package/dist/client/react/MainLocationContext.js.map +1 -0
- package/dist/client/react/ProjectionFrame.d.ts +1 -2
- package/dist/client/react/ProjectionFrame.d.ts.map +1 -1
- package/dist/client/react/ProjectionFrame.js +10 -4
- package/dist/client/react/ProjectionFrame.js.map +1 -1
- package/dist/client/react/ReadingLibrary.d.ts.map +1 -1
- package/dist/client/react/ReadingLibrary.js +7 -8
- package/dist/client/react/ReadingLibrary.js.map +1 -1
- package/dist/client/react/SystemPage.d.ts.map +1 -1
- package/dist/client/react/SystemPage.js +2 -1
- package/dist/client/react/SystemPage.js.map +1 -1
- package/dist/client/react/TodayOptions.d.ts.map +1 -1
- package/dist/client/react/TodayOptions.js +23 -14
- package/dist/client/react/TodayOptions.js.map +1 -1
- package/dist/client/react/VisualViewer.d.ts.map +1 -1
- package/dist/client/react/VisualViewer.js +6 -5
- package/dist/client/react/VisualViewer.js.map +1 -1
- package/dist/client/react/boardRouter.d.ts +3 -2
- package/dist/client/react/boardRouter.d.ts.map +1 -1
- package/dist/client/react/boardRouter.js +2 -1
- package/dist/client/react/boardRouter.js.map +1 -1
- package/dist/client/react/createResponse.d.ts +32 -4
- package/dist/client/react/createResponse.d.ts.map +1 -1
- package/dist/client/react/createResponse.js +7 -28
- package/dist/client/react/createResponse.js.map +1 -1
- package/dist/client/react/diagnosticsValues.d.ts.map +1 -1
- package/dist/client/react/diagnosticsValues.js +5 -7
- package/dist/client/react/diagnosticsValues.js.map +1 -1
- package/dist/client/react/entry.d.ts.map +1 -1
- package/dist/client/react/entry.js +2 -34
- package/dist/client/react/entry.js.map +1 -1
- package/dist/client/react/githubLimited.d.ts +3 -0
- package/dist/client/react/githubLimited.d.ts.map +1 -0
- package/dist/client/react/githubLimited.js +4 -0
- package/dist/client/react/githubLimited.js.map +1 -0
- package/dist/client/react/loadProjection.d.ts +135 -135
- package/dist/client/react/loadRemaining.d.ts +37 -29
- package/dist/client/react/loadRemaining.d.ts.map +1 -1
- package/dist/client/react/mainLocation.d.ts +4 -2
- package/dist/client/react/mainLocation.d.ts.map +1 -1
- package/dist/client/react/mainLocation.js +1 -8
- package/dist/client/react/mainLocation.js.map +1 -1
- package/dist/client/react/mountBoard.d.ts +3 -0
- package/dist/client/react/mountBoard.d.ts.map +1 -0
- package/dist/client/react/mountBoard.js +45 -0
- package/dist/client/react/mountBoard.js.map +1 -0
- package/dist/client/react/navigationLocation.d.ts +1 -1
- package/dist/client/react/navigationLocation.d.ts.map +1 -1
- package/dist/client/react/navigationLocation.js +1 -1
- package/dist/client/react/navigationLocation.js.map +1 -1
- package/dist/client/react/retryPage.d.ts +2 -1
- package/dist/client/react/retryPage.d.ts.map +1 -1
- package/dist/client/react/retryPage.js +3 -4
- package/dist/client/react/retryPage.js.map +1 -1
- package/dist/client/react/useBoardNavigation.d.ts.map +1 -1
- package/dist/client/react/useBoardNavigation.js +18 -8
- package/dist/client/react/useBoardNavigation.js.map +1 -1
- package/dist/client/react/useDocumentPage.d.ts +1 -1
- package/dist/client/react/useDocumentPage.d.ts.map +1 -1
- package/dist/client/react/useDocumentPage.js +9 -6
- package/dist/client/react/useDocumentPage.js.map +1 -1
- package/dist/client/react/useHandoffForm.d.ts +56 -30
- package/dist/client/react/useHandoffForm.d.ts.map +1 -1
- package/dist/client/react/useHandoffForm.js +19 -30
- package/dist/client/react/useHandoffForm.js.map +1 -1
- package/dist/client/react/useResponseForm.d.ts +72 -43
- package/dist/client/react/useResponseForm.d.ts.map +1 -1
- package/dist/client/react/useResponseForm.js +20 -17
- package/dist/client/react/useResponseForm.js.map +1 -1
- package/dist/client/react/visualSources.d.ts.map +1 -1
- package/dist/client/react/visualSources.js +10 -8
- package/dist/client/react/visualSources.js.map +1 -1
- package/dist/client/saveFailure.d.ts +8 -0
- package/dist/client/saveFailure.d.ts.map +1 -0
- package/dist/client/saveFailure.js +8 -0
- package/dist/client/saveFailure.js.map +1 -0
- package/dist/diagnostics/daemonContract.d.ts +65 -38
- package/dist/diagnostics/daemonContract.d.ts.map +1 -1
- package/dist/diagnostics/daemonContract.js +2 -1
- package/dist/diagnostics/daemonContract.js.map +1 -1
- package/dist/diagnostics/health.d.ts +10 -0
- package/dist/diagnostics/health.d.ts.map +1 -1
- package/dist/diagnostics/health.js +2 -0
- package/dist/diagnostics/health.js.map +1 -1
- package/dist/diagnostics/source.d.ts +55 -36
- package/dist/diagnostics/source.d.ts.map +1 -1
- package/dist/diagnostics/source.js +1 -2
- package/dist/diagnostics/source.js.map +1 -1
- package/dist/rpc/contract.d.ts +60 -36
- package/dist/rpc/contract.d.ts.map +1 -1
- package/dist/rpc/handoffContract.d.ts +8 -8
- package/dist/server/diagnostics/daemon.d.ts +41 -25
- package/dist/server/diagnostics/daemon.d.ts.map +1 -1
- package/dist/server/diagnostics/fleetHealth.d.ts +10 -0
- package/dist/server/diagnostics/fleetHealth.d.ts.map +1 -1
- package/dist/server/diagnostics/githubLimits.d.ts +12 -13
- package/dist/server/diagnostics/githubLimits.d.ts.map +1 -1
- package/dist/server/diagnostics/githubLimits.js +6 -6
- package/dist/server/diagnostics/githubLimits.js.map +1 -1
- package/dist/server/integration/diagnostics.d.ts +74 -38
- package/dist/server/integration/diagnostics.d.ts.map +1 -1
- package/dist/server/mcp/client.d.ts +589 -581
- package/dist/server/mcp/client.d.ts.map +1 -1
- package/dist/server/mcp/queries.d.ts +60 -36
- package/dist/server/mcp/queries.d.ts.map +1 -1
- package/dist/server/metadata/fields.js +1 -1
- package/dist/server/metadata/fields.js.map +1 -1
- package/dist/server/packageInfo.d.ts +1 -1
- package/dist/server/packageInfo.js +1 -1
- package/dist/server/views/remainingQueries.d.ts +20 -12
- package/dist/server/views/remainingQueries.d.ts.map +1 -1
- package/package.json +7 -6
- package/src/browser/diagnostics/schema.ts +12 -2
- package/src/client/client.ts +2 -1
- package/src/client/formStatus.ts +35 -0
- package/src/client/react/BoardShell.tsx +19 -26
- package/src/client/react/DocumentContext.tsx +6 -3
- package/src/client/react/FormContext.ts +13 -0
- package/src/client/react/LiveControls.tsx +5 -25
- package/src/client/react/MainLocationContext.ts +8 -0
- package/src/client/react/ProjectionFrame.tsx +13 -5
- package/src/client/react/ReadingLibrary.tsx +7 -8
- package/src/client/react/SystemPage.tsx +2 -1
- package/src/client/react/TodayOptions.tsx +40 -33
- package/src/client/react/VisualViewer.tsx +6 -6
- package/src/client/react/boardRouter.ts +3 -2
- package/src/client/react/createResponse.ts +10 -35
- package/src/client/react/diagnosticsValues.tsx +20 -12
- package/src/client/react/entry.tsx +2 -37
- package/src/client/react/githubLimited.ts +5 -0
- package/src/client/react/mainLocation.ts +5 -8
- package/src/client/react/mountBoard.tsx +49 -0
- package/src/client/react/navigationLocation.ts +2 -2
- package/src/client/react/retryPage.ts +4 -4
- package/src/client/react/useBoardNavigation.ts +20 -8
- package/src/client/react/useDocumentPage.ts +12 -6
- package/src/client/react/useHandoffForm.ts +25 -33
- package/src/client/react/useResponseForm.ts +20 -18
- package/src/client/react/visualSources.ts +10 -8
- package/src/client/saveFailure.ts +14 -0
- package/src/diagnostics/daemonContract.ts +2 -1
- package/src/diagnostics/health.ts +2 -0
- package/src/diagnostics/source.ts +1 -2
- package/src/server/diagnostics/githubLimits.ts +9 -11
- package/src/server/metadata/fields.ts +1 -1
- package/src/server/packageInfo.ts +1 -1
- package/dist/client/handoffs/failureStatus.d.ts +0 -2
- package/dist/client/handoffs/failureStatus.d.ts.map +0 -1
- package/dist/client/handoffs/failureStatus.js +0 -7
- package/dist/client/handoffs/failureStatus.js.map +0 -1
- package/dist/client/responses/failure.d.ts +0 -5
- package/dist/client/responses/failure.d.ts.map +0 -1
- package/dist/client/responses/failure.js +0 -13
- package/dist/client/responses/failure.js.map +0 -1
- package/dist/server/diagnostics/readCommand.d.ts +0 -4
- package/dist/server/diagnostics/readCommand.d.ts.map +0 -1
- package/dist/server/diagnostics/readCommand.js +0 -20
- package/dist/server/diagnostics/readCommand.js.map +0 -1
- package/src/client/handoffs/failureStatus.ts +0 -6
- package/src/client/responses/failure.ts +0 -13
- package/src/server/diagnostics/readCommand.ts +0 -24
|
@@ -3,7 +3,7 @@ import { flushSync } from "react-dom";
|
|
|
3
3
|
import type { Document } from "#browser/document/schema.ts";
|
|
4
4
|
import { documentSourceId } from "#client/document/layout.ts";
|
|
5
5
|
import { fileUrl } from "#files/url.ts";
|
|
6
|
-
import {
|
|
6
|
+
import type { MainLocation } from "./mainLocation.ts";
|
|
7
7
|
export function useDocumentPage({
|
|
8
8
|
data,
|
|
9
9
|
pending,
|
|
@@ -25,9 +25,7 @@ export function useDocumentPage({
|
|
|
25
25
|
}
|
|
26
26
|
const main = element;
|
|
27
27
|
main.dataset.scheme = document.documentElement.dataset.scheme ?? "";
|
|
28
|
-
|
|
29
|
-
delete main.dataset.route;
|
|
30
|
-
}
|
|
28
|
+
delete main.dataset.route;
|
|
31
29
|
let active = true;
|
|
32
30
|
let scheduled = false;
|
|
33
31
|
function announce() {
|
|
@@ -72,10 +70,18 @@ function publishPage(main: HTMLElement, data: Document | undefined, status: stri
|
|
|
72
70
|
} else {
|
|
73
71
|
delete main.dataset.identity;
|
|
74
72
|
}
|
|
75
|
-
flushSync(() => onPage(displayedLocation(main)));
|
|
76
73
|
main.dataset.route = url;
|
|
77
74
|
main.dataset.status = status;
|
|
78
|
-
|
|
75
|
+
flushSync(() =>
|
|
76
|
+
onPage({
|
|
77
|
+
file: data?.file ?? "",
|
|
78
|
+
projection: "document",
|
|
79
|
+
route: url,
|
|
80
|
+
status: Number(status),
|
|
81
|
+
title: main.querySelector("h1")?.textContent?.trim(),
|
|
82
|
+
view: "",
|
|
83
|
+
}),
|
|
84
|
+
);
|
|
79
85
|
}
|
|
80
86
|
function affectsDocument(record: MutationRecord) {
|
|
81
87
|
const element = record.target instanceof Element ? record.target : record.target.parentElement;
|
|
@@ -1,41 +1,20 @@
|
|
|
1
1
|
import { useAtomRef } from "@effect/atom-react";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { Equal, Option, Schema } from "effect";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
5
4
|
import { useCreate } from "@shivaedev/effect-react/create.ts";
|
|
6
5
|
import { HandoffInput, type HandoffPreview } from "#browser/handoffs/schema.ts";
|
|
7
6
|
import type { HandoffProjection } from "#browser/views/remaining.ts";
|
|
8
|
-
import { session } from "#client/entry.ts";
|
|
9
7
|
import { formDraft } from "#client/formDraft.ts";
|
|
10
|
-
import {
|
|
8
|
+
import { type DraftInteraction, formStatus } from "#client/formStatus.ts";
|
|
11
9
|
import { sourceChanged } from "#client/sourceChanged.ts";
|
|
12
|
-
|
|
13
|
-
const runtime = Atom.runtime(Layer.empty);
|
|
10
|
+
import { useFormClient } from "./FormContext.ts";
|
|
14
11
|
export function useHandoffForm(data: HandoffProjection, source: HandoffPreview) {
|
|
12
|
+
const client = useFormClient();
|
|
15
13
|
const [preview, setPreview] = useState<HandoffInput>();
|
|
16
|
-
const [
|
|
17
|
-
const modelRef = useRef<HandoffInput | undefined>(undefined);
|
|
14
|
+
const [interaction, setInteraction] = useState<DraftInteraction>("draft");
|
|
18
15
|
const draft = formDraft("handoff", source.item);
|
|
19
16
|
const created = useCreate({
|
|
20
|
-
create:
|
|
21
|
-
Effect.tryPromise({
|
|
22
|
-
catch: (error) => error,
|
|
23
|
-
try: async () => {
|
|
24
|
-
const native = session(window);
|
|
25
|
-
setStatus("Pending: preparing the local handoff file…");
|
|
26
|
-
try {
|
|
27
|
-
const saved = await native.mutate(native.handoffs.prepareHandoff.run(input));
|
|
28
|
-
const newer = !Equal.equals(modelRef.current, input);
|
|
29
|
-
setStatus(
|
|
30
|
-
`Prepared: ${saved.file}. Receipt: ${saved.handoff.state}; execution and acceptance remain separate.${newer ? " Your newer draft remains unsaved." : ""}`,
|
|
31
|
-
);
|
|
32
|
-
return saved;
|
|
33
|
-
} catch (error) {
|
|
34
|
-
setStatus(failureStatus(error));
|
|
35
|
-
throw error;
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
}),
|
|
17
|
+
create: client.handoffs.prepareHandoff.run,
|
|
39
18
|
fields: HandoffInput,
|
|
40
19
|
initialValues: {
|
|
41
20
|
constraints: "",
|
|
@@ -48,7 +27,8 @@ export function useHandoffForm(data: HandoffProjection, source: HandoffPreview)
|
|
|
48
27
|
source: source.source,
|
|
49
28
|
},
|
|
50
29
|
persistedDraft: draft,
|
|
51
|
-
|
|
30
|
+
rejectField: () => undefined,
|
|
31
|
+
runtime: client.runtime,
|
|
52
32
|
});
|
|
53
33
|
const form = created.form;
|
|
54
34
|
const values = useAtomRef(form.values);
|
|
@@ -59,7 +39,6 @@ export function useHandoffForm(data: HandoffProjection, source: HandoffPreview)
|
|
|
59
39
|
form.change("source", created.created.value.handoff.source);
|
|
60
40
|
}
|
|
61
41
|
}, [form, created.dirty, created.created]);
|
|
62
|
-
modelRef.current = values;
|
|
63
42
|
const stale = Boolean(values.id) && (values.revision !== source.reviewedRevision || values.source !== source.source);
|
|
64
43
|
function edit() {
|
|
65
44
|
if (!form.values.value.id) {
|
|
@@ -68,7 +47,7 @@ export function useHandoffForm(data: HandoffProjection, source: HandoffPreview)
|
|
|
68
47
|
}
|
|
69
48
|
setPreview(undefined);
|
|
70
49
|
if (!created.saving) {
|
|
71
|
-
|
|
50
|
+
setInteraction("draft");
|
|
72
51
|
}
|
|
73
52
|
}
|
|
74
53
|
function showPreview() {
|
|
@@ -85,12 +64,25 @@ export function useHandoffForm(data: HandoffProjection, source: HandoffPreview)
|
|
|
85
64
|
function clear() {
|
|
86
65
|
created.revert();
|
|
87
66
|
setPreview(undefined);
|
|
88
|
-
|
|
67
|
+
setInteraction("cleared");
|
|
89
68
|
}
|
|
90
69
|
const previewed = preview !== undefined && !stale && Equal.equals(preview, values);
|
|
70
|
+
const { text: status } = formStatus(
|
|
71
|
+
created,
|
|
72
|
+
interaction,
|
|
73
|
+
"handoff",
|
|
74
|
+
(saved) =>
|
|
75
|
+
`Prepared: ${saved.file}. Receipt: ${saved.handoff.state}; execution and acceptance remain separate.${created.dirty ? " Your newer draft remains unsaved." : ""}`,
|
|
76
|
+
);
|
|
91
77
|
return {
|
|
92
78
|
clear,
|
|
93
|
-
created
|
|
79
|
+
created: {
|
|
80
|
+
...created,
|
|
81
|
+
save: () => {
|
|
82
|
+
setInteraction("submitted");
|
|
83
|
+
created.save();
|
|
84
|
+
},
|
|
85
|
+
},
|
|
94
86
|
draft,
|
|
95
87
|
edit,
|
|
96
88
|
form,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { useAtomRef } from "@effect/atom-react";
|
|
2
|
-
import { Equal,
|
|
3
|
-
import
|
|
4
|
-
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { Equal, Option, Schema } from "effect";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
5
4
|
import { useCreate } from "@shivaedev/effect-react/create.ts";
|
|
6
5
|
import type { Prompt } from "#browser/responses/Prompt.ts";
|
|
7
6
|
import type { PromptAnswer, QuestionPreview } from "#browser/responses/schema.ts";
|
|
8
7
|
import { formDraft } from "#client/formDraft.ts";
|
|
8
|
+
import { type DraftInteraction, formStatus, responseSaved } from "#client/formStatus.ts";
|
|
9
9
|
import { answerRecovery, recoveryText } from "#client/responses/answerRecovery.ts";
|
|
10
10
|
import { authorLabel } from "#client/responses/authorLabel.ts";
|
|
11
11
|
import { type ResponseInput, responseFormSchema } from "#client/responses/formSchema.ts";
|
|
@@ -14,14 +14,13 @@ import { retainContext } from "#client/responses/retainContext.ts";
|
|
|
14
14
|
import { responseFeedback, responseSummary } from "#client/responses/summary.ts";
|
|
15
15
|
import { sourceChanged } from "#client/sourceChanged.ts";
|
|
16
16
|
import { createResponse } from "./createResponse.ts";
|
|
17
|
-
|
|
18
|
-
const runtime = Atom.runtime(Layer.empty);
|
|
17
|
+
import { useFormClient } from "./FormContext.ts";
|
|
19
18
|
|
|
20
19
|
export function useResponseForm(source: QuestionPreview, prompts: readonly Prompt[]) {
|
|
20
|
+
const client = useFormClient();
|
|
21
21
|
const schema = responseFormSchema(prompts.length > 0);
|
|
22
22
|
const [preview, setPreview] = useState<ResponseInput>();
|
|
23
|
-
const [
|
|
24
|
-
const [status, setStatus] = useState("");
|
|
23
|
+
const [interaction, setInteraction] = useState<DraftInteraction>("draft");
|
|
25
24
|
const [attempted, setAttempted] = useState(false);
|
|
26
25
|
const [label] = useState(() =>
|
|
27
26
|
authorLabel(() => window.localStorage, typeof document === "undefined" ? undefined : document.documentElement.dataset.workspace),
|
|
@@ -39,13 +38,13 @@ export function useResponseForm(source: QuestionPreview, prompts: readonly Promp
|
|
|
39
38
|
type: "answer",
|
|
40
39
|
}));
|
|
41
40
|
const draft = formDraft("response", source.item, source.request);
|
|
42
|
-
const modelRef = useRef<ResponseInput | undefined>(undefined);
|
|
43
41
|
const created = useCreate({
|
|
44
|
-
create: (submission) => createResponse(source, submission
|
|
42
|
+
create: (submission) => createResponse(client, source, submission),
|
|
45
43
|
fields: schema,
|
|
46
44
|
initialValues: initial,
|
|
47
45
|
persistedDraft: draft,
|
|
48
|
-
|
|
46
|
+
rejectField: () => undefined,
|
|
47
|
+
runtime: client.runtime,
|
|
49
48
|
});
|
|
50
49
|
const form = created.form;
|
|
51
50
|
const values = useAtomRef(form.values);
|
|
@@ -57,7 +56,6 @@ export function useResponseForm(source: QuestionPreview, prompts: readonly Promp
|
|
|
57
56
|
}
|
|
58
57
|
}
|
|
59
58
|
}, [form, created.created, created.dirty, initial, label]);
|
|
60
|
-
modelRef.current = values;
|
|
61
59
|
const same = values.question === source.id && values.revision === source.reviewedRevision;
|
|
62
60
|
const stale = Boolean(values.id) && !same;
|
|
63
61
|
const recovery = answerRecovery(values, !stale || values.answerContext === JSON.stringify([source.id, source.reviewedRevision]));
|
|
@@ -86,7 +84,7 @@ export function useResponseForm(source: QuestionPreview, prompts: readonly Promp
|
|
|
86
84
|
edit();
|
|
87
85
|
}
|
|
88
86
|
function showPreview() {
|
|
89
|
-
|
|
87
|
+
setInteraction("draft");
|
|
90
88
|
if (!form.values.value.id || form.values.value.question !== source.id || form.values.value.revision !== source.reviewedRevision) {
|
|
91
89
|
form.change("id", `response.${crypto.randomUUID()}`);
|
|
92
90
|
}
|
|
@@ -98,7 +96,6 @@ export function useResponseForm(source: QuestionPreview, prompts: readonly Promp
|
|
|
98
96
|
form.blur(name);
|
|
99
97
|
}
|
|
100
98
|
setPreview(form.values.value);
|
|
101
|
-
setStatus("");
|
|
102
99
|
}
|
|
103
100
|
function edit() {
|
|
104
101
|
if (!form.values.value.id) {
|
|
@@ -109,18 +106,17 @@ export function useResponseForm(source: QuestionPreview, prompts: readonly Promp
|
|
|
109
106
|
label.save(form.values.value.author);
|
|
110
107
|
setPreview(undefined);
|
|
111
108
|
if (!created.saving) {
|
|
112
|
-
|
|
113
|
-
setStatus("");
|
|
109
|
+
setInteraction("draft");
|
|
114
110
|
}
|
|
115
111
|
}
|
|
116
112
|
function clear() {
|
|
117
113
|
setAttempted(false);
|
|
118
|
-
setOutcome(undefined);
|
|
119
114
|
created.revert();
|
|
120
115
|
setPreview(undefined);
|
|
121
|
-
|
|
116
|
+
setInteraction("cleared");
|
|
122
117
|
}
|
|
123
118
|
const previewed = preview !== undefined && same && Equal.equals(preview, values);
|
|
119
|
+
const { outcome, text: status } = formStatus(created, interaction, "response", (saved) => responseSaved(saved.id, created.dirty));
|
|
124
120
|
const summary = responseSummary(values, prompts, answers(values), attempted);
|
|
125
121
|
return {
|
|
126
122
|
answers: answers(values),
|
|
@@ -131,7 +127,13 @@ export function useResponseForm(source: QuestionPreview, prompts: readonly Promp
|
|
|
131
127
|
},
|
|
132
128
|
attempted,
|
|
133
129
|
clear,
|
|
134
|
-
created
|
|
130
|
+
created: {
|
|
131
|
+
...created,
|
|
132
|
+
save: () => {
|
|
133
|
+
setInteraction("submitted");
|
|
134
|
+
created.save();
|
|
135
|
+
},
|
|
136
|
+
},
|
|
135
137
|
draft,
|
|
136
138
|
edit,
|
|
137
139
|
editAnswers,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Clock, Effect } from "effect";
|
|
2
|
-
import { useEffect } from "react";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import { useMainLocation } from "./MainLocationContext.ts";
|
|
3
4
|
|
|
4
5
|
export interface VisualSource {
|
|
5
6
|
readonly alt: string;
|
|
@@ -13,16 +14,17 @@ export interface VisualSelection {
|
|
|
13
14
|
readonly selected: number;
|
|
14
15
|
}
|
|
15
16
|
export function useVisualSources(inspect: (selection: VisualSelection) => void) {
|
|
17
|
+
const page = useMainLocation();
|
|
18
|
+
const revisionRef = useRef(0);
|
|
16
19
|
useEffect(() => {
|
|
17
|
-
let revision = 0;
|
|
18
20
|
function refresh() {
|
|
19
|
-
|
|
21
|
+
revisionRef.current += 1;
|
|
20
22
|
for (const graphic of document.querySelectorAll<HTMLImageElement>("#doc img[data-local-image]")) {
|
|
21
23
|
graphic.tabIndex = 0;
|
|
22
24
|
graphic.setAttribute("role", "button");
|
|
23
25
|
graphic.setAttribute("aria-label", `Inspect image: ${graphic.alt || "Local image"}`);
|
|
24
26
|
const url = new URL(graphic.dataset.localImage ?? graphic.src, location.href);
|
|
25
|
-
url.searchParams.set("_wb", `${Effect.runSync(Clock.currentTimeMillis)}-${
|
|
27
|
+
url.searchParams.set("_wb", `${Effect.runSync(Clock.currentTimeMillis)}-${revisionRef.current}`);
|
|
26
28
|
graphic.src = url.href;
|
|
27
29
|
graphic.onerror = () => {
|
|
28
30
|
if (!graphic.isConnected || graphic.nextElementSibling?.hasAttribute("data-image-error")) {
|
|
@@ -94,12 +96,12 @@ export function useVisualSources(inspect: (selection: VisualSelection) => void)
|
|
|
94
96
|
}
|
|
95
97
|
document.addEventListener("click", click);
|
|
96
98
|
document.addEventListener("keydown", key);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
+
if (page.route) {
|
|
100
|
+
refresh();
|
|
101
|
+
}
|
|
99
102
|
return () => {
|
|
100
103
|
document.removeEventListener("click", click);
|
|
101
104
|
document.removeEventListener("keydown", key);
|
|
102
|
-
document.removeEventListener("board-page", refresh);
|
|
103
105
|
};
|
|
104
|
-
}, [inspect]);
|
|
106
|
+
}, [inspect, page]);
|
|
105
107
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { RpcClientError } from "effect/rpc/RpcClientError";
|
|
2
|
+
import type { ResponseRejection } from "#rpc/rejections.ts";
|
|
3
|
+
|
|
4
|
+
export type SaveFailure = ResponseRejection | RpcClientError;
|
|
5
|
+
|
|
6
|
+
export function saveFailure(error: SaveFailure, kind: "response" | "handoff") {
|
|
7
|
+
const outcome =
|
|
8
|
+
error._tag === "PublicationUncertain" || error._tag === "InternalServerError" || error._tag === "RpcClientError" ? "uncertain" : "rejected";
|
|
9
|
+
const retained =
|
|
10
|
+
kind === "response"
|
|
11
|
+
? "Draft retained. Preview and retry with the same identity; saved content will be reconciled."
|
|
12
|
+
: "Draft retained; preview and retry.";
|
|
13
|
+
return { outcome, text: `${outcome === "rejected" ? "Rejected" : "Uncertain"}: ${String(error)}. ${retained}` };
|
|
14
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
2
|
import { contract } from "@shivaedev/effect-contract/contract.ts";
|
|
3
3
|
import { query } from "@shivaedev/effect-contract/operation.ts";
|
|
4
|
+
import { ServiceUnavailable } from "@shivaedev/platform/errors/taxonomy.ts";
|
|
4
5
|
import { CodexLimits, GithubLimits } from "#browser/diagnostics/schema.ts";
|
|
5
6
|
import { FleetItems } from "./fleetItems.ts";
|
|
6
7
|
import { FleetHealth } from "./health.ts";
|
|
@@ -24,7 +25,7 @@ export type DiagnosticsSnapshot = typeof DiagnosticsSnapshot.Type;
|
|
|
24
25
|
export const DiagnosticsContract = contract("daemon", {
|
|
25
26
|
queries: [
|
|
26
27
|
query("diagnostics", { payload: {}, reads: () => [], success: DiagnosticsSnapshot }),
|
|
27
|
-
query("fleetItems", { payload: {}, reads: () => [], success: FleetItems }),
|
|
28
|
+
query("fleetItems", { payload: {}, reads: () => [], rejections: { ServiceUnavailable }, success: FleetItems }),
|
|
28
29
|
],
|
|
29
30
|
});
|
|
30
31
|
export const DAEMON_RPC_PATH = "/_fleet/rpc";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
|
+
import { GithubLimits } from "#browser/diagnostics/schema.ts";
|
|
2
3
|
|
|
3
4
|
const Count = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));
|
|
4
5
|
const Timestamp = Schema.Number.check(Schema.isFinite(), Schema.isGreaterThanOrEqualTo(0), Schema.isLessThanOrEqualTo(8_640_000_000_000_000));
|
|
@@ -9,6 +10,7 @@ export const FLEET_HEALTH_NAME = "health.json";
|
|
|
9
10
|
export const FLEET_HEALTH_FILE = `fleet/${FLEET_HEALTH_NAME}`;
|
|
10
11
|
export const FleetHealth = Schema.Struct({
|
|
11
12
|
concurrency: Schema.Int.check(Schema.isGreaterThan(0)),
|
|
13
|
+
github: Schema.optional(GithubLimits),
|
|
12
14
|
lastCapacityRejection: Schema.NullOr(CapacityRejection),
|
|
13
15
|
lastSchedulerPass: Schema.NullOr(Timestamp),
|
|
14
16
|
observedAt: Timestamp,
|
|
@@ -33,10 +33,9 @@ export const observe = Effect.fn("WorkBoard.observe")(function* <A>(source: Sour
|
|
|
33
33
|
|
|
34
34
|
export const diagnostics = Effect.fn("WorkBoard.diagnostics")(function* (sources: Sources) {
|
|
35
35
|
const codex = yield* Effect.cachedWithTTL(observe(sources.codex), "1 minute");
|
|
36
|
-
const github = yield* Effect.cachedWithTTL(observe(sources.github), "1 minute");
|
|
37
36
|
// A bounded shared read must finish caching even if its first tab leaves.
|
|
38
37
|
return Effect.all(
|
|
39
|
-
{ codex: Effect.uninterruptible(codex), fleet: observe(sources.fleet), github:
|
|
38
|
+
{ codex: Effect.uninterruptible(codex), fleet: observe(sources.fleet), github: observe(sources.github) },
|
|
40
39
|
{ concurrency: "unbounded" },
|
|
41
40
|
);
|
|
42
41
|
});
|
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import { Effect
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import type { FleetHealth } from "#diagnostics/health.ts";
|
|
3
|
+
import { type Source, SourceFailure } from "#diagnostics/source.ts";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Effect.mapError((error) =>
|
|
12
|
-
error instanceof SourceFailure ? error : new SourceFailure({ kind: "error", message: "GitHub returned invalid rate limits" }),
|
|
5
|
+
export function githubLimits(fleet: Source<FleetHealth>) {
|
|
6
|
+
return fleet.pipe(
|
|
7
|
+
Effect.flatMap(({ github }) =>
|
|
8
|
+
github === undefined || github.length === 0
|
|
9
|
+
? Effect.fail(new SourceFailure({ kind: "not-configured", message: "Fleet has not recorded a GitHub response yet" }))
|
|
10
|
+
: Effect.succeed(github),
|
|
13
11
|
),
|
|
14
12
|
);
|
|
15
13
|
}
|
|
@@ -3,7 +3,7 @@ import { Metadata } from "#metadata/schema.ts";
|
|
|
3
3
|
import type { Diagnostic } from "./parse.ts";
|
|
4
4
|
|
|
5
5
|
const addonOwners: Readonly<Record<string, readonly string[]>> = {
|
|
6
|
-
fleet: ["fleet", "fleet-launch", "fleet-deliverable"],
|
|
6
|
+
fleet: ["fleet", "fleet-launch", "fleet-deliverable", "fleet-priority"],
|
|
7
7
|
};
|
|
8
8
|
const addonFields = new Set(Object.values(addonOwners).flat());
|
|
9
9
|
const schemas: ReadonlyMap<string, Schema.ConstraintDecoder<unknown, never>> = new Map(Object.entries(Metadata.to.fields));
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export const NAME = "work-board";
|
|
2
|
-
export const VERSION = "0.
|
|
2
|
+
export const VERSION = "0.15.0";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"failureStatus.d.ts","sourceRoot":"","sources":["../../../src/client/handoffs/failureStatus.ts"],"names":[],"mappings":"AAGA,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,UAE3C"}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { Schema } from "effect";
|
|
2
|
-
import { InternalServerError } from "@shivaedev/platform/errors/taxonomy.ts";
|
|
3
|
-
import { PublicationUncertain, ResponseRejection } from "#rpc/rejections.ts";
|
|
4
|
-
export function failureStatus(error) {
|
|
5
|
-
return `${Schema.is(ResponseRejection)(error) && !(error instanceof PublicationUncertain) && !(error instanceof InternalServerError) ? "Rejected" : "Uncertain"}: ${String(error)}. Draft retained; preview and retry.`;
|
|
6
|
-
}
|
|
7
|
-
//# sourceMappingURL=failureStatus.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"failureStatus.js","sourceRoot":"","sources":["../../../src/client/handoffs/failureStatus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC7E,MAAM,UAAU,aAAa,CAAC,KAAc;IAC3C,OAAO,GAAG,MAAM,CAAC,EAAE,CAAC,iBAAiB,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,YAAY,oBAAoB,CAAC,IAAI,CAAC,CAAC,KAAK,YAAY,mBAAmB,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,KAAK,MAAM,CAAC,KAAK,CAAC,sCAAsC,CAAC;AACzN,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"failure.d.ts","sourceRoot":"","sources":["../../../src/client/responses/failure.ts"],"names":[],"mappings":"AAGA,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO;;;EAS7C"}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { Schema } from "effect";
|
|
2
|
-
import { InternalServerError } from "@shivaedev/platform/errors/taxonomy.ts";
|
|
3
|
-
import { PublicationUncertain, ResponseRejection } from "#rpc/rejections.ts";
|
|
4
|
-
export function responseFailure(error) {
|
|
5
|
-
const outcome = Schema.is(ResponseRejection)(error) && !(error instanceof PublicationUncertain) && !(error instanceof InternalServerError)
|
|
6
|
-
? "rejected"
|
|
7
|
-
: "uncertain";
|
|
8
|
-
return {
|
|
9
|
-
outcome,
|
|
10
|
-
text: `${outcome === "rejected" ? "Rejected" : "Uncertain"}: ${String(error)}. Draft retained. Preview and retry with the same identity; saved content will be reconciled.`,
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
//# sourceMappingURL=failure.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"failure.js","sourceRoot":"","sources":["../../../src/client/responses/failure.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC7E,MAAM,UAAU,eAAe,CAAC,KAAc;IAC7C,MAAM,OAAO,GACZ,MAAM,CAAC,EAAE,CAAC,iBAAiB,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,YAAY,oBAAoB,CAAC,IAAI,CAAC,CAAC,KAAK,YAAY,mBAAmB,CAAC;QACzH,CAAC,CAAC,UAAU;QACZ,CAAC,CAAC,WAAW,CAAC;IAChB,OAAO;QACN,OAAO;QACP,IAAI,EAAE,GAAG,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,KAAK,MAAM,CAAC,KAAK,CAAC,+FAA+F;KAC3K,CAAC;AACH,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"readCommand.d.ts","sourceRoot":"","sources":["../../../src/server/diagnostics/readCommand.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEvD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,+CAmBnE"}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
|
-
import { Effect } from "effect";
|
|
3
|
-
import { SourceFailure } from "#diagnostics/source.ts";
|
|
4
|
-
export function readCommand(command, args) {
|
|
5
|
-
return Effect.callback((resume) => {
|
|
6
|
-
const child = execFile(command, args, { maxBuffer: 1024 * 1024, timeout: 30_000 }, (error, stdout, stderr) => {
|
|
7
|
-
if (error === null) {
|
|
8
|
-
resume(Effect.succeed(stdout));
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
const missing = error.code === "ENOENT" || stderr.includes("gh auth login") || stderr.includes("GH_TOKEN environment variable");
|
|
12
|
-
resume(Effect.fail(new SourceFailure({
|
|
13
|
-
kind: missing ? "not-configured" : "error",
|
|
14
|
-
message: missing ? `${command} is not installed or authenticated` : (stderr.trim() || error.message).slice(0, 500),
|
|
15
|
-
})));
|
|
16
|
-
});
|
|
17
|
-
return Effect.sync(() => child.kill());
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
//# sourceMappingURL=readCommand.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"readCommand.js","sourceRoot":"","sources":["../../../src/server/diagnostics/readCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEvD,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,IAAuB;IACnE,OAAO,MAAM,CAAC,QAAQ,CAAwB,CAAC,MAAM,EAAE,EAAE;QACxD,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YAC5G,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBACpB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;gBAC/B,OAAO;YACR,CAAC;YACD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,+BAA+B,CAAC,CAAC;YAChI,MAAM,CACL,MAAM,CAAC,IAAI,CACV,IAAI,aAAa,CAAC;gBACjB,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,OAAO;gBAC1C,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,oCAAoC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;aAClH,CAAC,CACF,CACD,CAAC;QACH,CAAC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { Schema } from "effect";
|
|
2
|
-
import { InternalServerError } from "@shivaedev/platform/errors/taxonomy.ts";
|
|
3
|
-
import { PublicationUncertain, ResponseRejection } from "#rpc/rejections.ts";
|
|
4
|
-
export function failureStatus(error: unknown) {
|
|
5
|
-
return `${Schema.is(ResponseRejection)(error) && !(error instanceof PublicationUncertain) && !(error instanceof InternalServerError) ? "Rejected" : "Uncertain"}: ${String(error)}. Draft retained; preview and retry.`;
|
|
6
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { Schema } from "effect";
|
|
2
|
-
import { InternalServerError } from "@shivaedev/platform/errors/taxonomy.ts";
|
|
3
|
-
import { PublicationUncertain, ResponseRejection } from "#rpc/rejections.ts";
|
|
4
|
-
export function responseFailure(error: unknown) {
|
|
5
|
-
const outcome =
|
|
6
|
-
Schema.is(ResponseRejection)(error) && !(error instanceof PublicationUncertain) && !(error instanceof InternalServerError)
|
|
7
|
-
? "rejected"
|
|
8
|
-
: "uncertain";
|
|
9
|
-
return {
|
|
10
|
-
outcome,
|
|
11
|
-
text: `${outcome === "rejected" ? "Rejected" : "Uncertain"}: ${String(error)}. Draft retained. Preview and retry with the same identity; saved content will be reconciled.`,
|
|
12
|
-
};
|
|
13
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
|
-
import { Effect } from "effect";
|
|
3
|
-
import { SourceFailure } from "#diagnostics/source.ts";
|
|
4
|
-
|
|
5
|
-
export function readCommand(command: string, args: readonly string[]) {
|
|
6
|
-
return Effect.callback<string, SourceFailure>((resume) => {
|
|
7
|
-
const child = execFile(command, args, { maxBuffer: 1024 * 1024, timeout: 30_000 }, (error, stdout, stderr) => {
|
|
8
|
-
if (error === null) {
|
|
9
|
-
resume(Effect.succeed(stdout));
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
const missing = error.code === "ENOENT" || stderr.includes("gh auth login") || stderr.includes("GH_TOKEN environment variable");
|
|
13
|
-
resume(
|
|
14
|
-
Effect.fail(
|
|
15
|
-
new SourceFailure({
|
|
16
|
-
kind: missing ? "not-configured" : "error",
|
|
17
|
-
message: missing ? `${command} is not installed or authenticated` : (stderr.trim() || error.message).slice(0, 500),
|
|
18
|
-
}),
|
|
19
|
-
),
|
|
20
|
-
);
|
|
21
|
-
});
|
|
22
|
-
return Effect.sync(() => child.kill());
|
|
23
|
-
});
|
|
24
|
-
}
|