@joewinke/jatui 0.37.0 → 0.38.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/artifact-blocks/AttachmentChip.svelte +66 -0
- package/dist/artifact-blocks/AttachmentChip.svelte.d.ts +7 -0
- package/dist/artifact-blocks/BlockComments.svelte +309 -0
- package/dist/artifact-blocks/BlockComments.svelte.d.ts +33 -0
- package/dist/artifact-blocks/BlockRenderer.svelte +674 -0
- package/dist/artifact-blocks/BlockRenderer.svelte.d.ts +55 -0
- package/dist/artifact-blocks/BlockVerdict.svelte +151 -0
- package/dist/artifact-blocks/BlockVerdict.svelte.d.ts +13 -0
- package/dist/artifact-blocks/BlocksArtifactView.svelte +1616 -0
- package/dist/artifact-blocks/BlocksArtifactView.svelte.d.ts +113 -0
- package/dist/artifact-blocks/CONTRACT.md +225 -0
- package/dist/artifact-blocks/CommentPill.svelte +49 -0
- package/dist/artifact-blocks/CommentPill.svelte.d.ts +16 -0
- package/dist/artifact-blocks/FoldedRounds.svelte +95 -0
- package/dist/artifact-blocks/FoldedRounds.svelte.d.ts +9 -0
- package/dist/artifact-blocks/Glyph.svelte +54 -0
- package/dist/artifact-blocks/Glyph.svelte.d.ts +10 -0
- package/dist/artifact-blocks/HistoryScrubber.svelte +500 -0
- package/dist/artifact-blocks/HistoryScrubber.svelte.d.ts +15 -0
- package/dist/artifact-blocks/Markdown.svelte +186 -0
- package/dist/artifact-blocks/Markdown.svelte.d.ts +30 -0
- package/dist/artifact-blocks/NoteComposer.svelte +31 -0
- package/dist/artifact-blocks/NoteComposer.svelte.d.ts +8 -0
- package/dist/artifact-blocks/PlainComposer.svelte +200 -0
- package/dist/artifact-blocks/PlainComposer.svelte.d.ts +15 -0
- package/dist/artifact-blocks/SectionTools.svelte +102 -0
- package/dist/artifact-blocks/SectionTools.svelte.d.ts +19 -0
- package/dist/artifact-blocks/adapter.d.ts +13 -0
- package/dist/artifact-blocks/adapter.js +31 -0
- package/dist/artifact-blocks/autoSend.d.ts +49 -0
- package/dist/artifact-blocks/autoSend.js +103 -0
- package/dist/artifact-blocks/blocks/actions/ActionsBlock.svelte +557 -0
- package/dist/artifact-blocks/blocks/actions/ActionsBlock.svelte.d.ts +48 -0
- package/dist/artifact-blocks/blocks/actions/block.d.ts +8 -0
- package/dist/artifact-blocks/blocks/actions/block.js +74 -0
- package/dist/artifact-blocks/blocks/actions/requests.d.ts +62 -0
- package/dist/artifact-blocks/blocks/actions/requests.js +26 -0
- package/dist/artifact-blocks/blocks/actions/schema.d.ts +168 -0
- package/dist/artifact-blocks/blocks/actions/schema.js +253 -0
- package/dist/artifact-blocks/blocks/callout/CalloutBlock.svelte +779 -0
- package/dist/artifact-blocks/blocks/callout/CalloutBlock.svelte.d.ts +40 -0
- package/dist/artifact-blocks/blocks/callout/block.d.ts +7 -0
- package/dist/artifact-blocks/blocks/callout/block.js +39 -0
- package/dist/artifact-blocks/blocks/callout/schema.d.ts +72 -0
- package/dist/artifact-blocks/blocks/callout/schema.js +115 -0
- package/dist/artifact-blocks/blocks/chart/ChartBlock.svelte +875 -0
- package/dist/artifact-blocks/blocks/chart/ChartBlock.svelte.d.ts +59 -0
- package/dist/artifact-blocks/blocks/chart/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/chart/block.js +70 -0
- package/dist/artifact-blocks/blocks/chart/schema.d.ts +171 -0
- package/dist/artifact-blocks/blocks/chart/schema.js +498 -0
- package/dist/artifact-blocks/blocks/chart/thumb.d.ts +2 -0
- package/dist/artifact-blocks/blocks/chart/thumb.js +201 -0
- package/dist/artifact-blocks/blocks/checklist/ChecklistBlock.svelte +1048 -0
- package/dist/artifact-blocks/blocks/checklist/ChecklistBlock.svelte.d.ts +16 -0
- package/dist/artifact-blocks/blocks/checklist/Icon.svelte +105 -0
- package/dist/artifact-blocks/blocks/checklist/Icon.svelte.d.ts +17 -0
- package/dist/artifact-blocks/blocks/checklist/block.d.ts +10 -0
- package/dist/artifact-blocks/blocks/checklist/block.js +63 -0
- package/dist/artifact-blocks/blocks/checklist/itemReview.d.ts +16 -0
- package/dist/artifact-blocks/blocks/checklist/itemReview.js +63 -0
- package/dist/artifact-blocks/blocks/checklist/labels.d.ts +62 -0
- package/dist/artifact-blocks/blocks/checklist/labels.js +96 -0
- package/dist/artifact-blocks/blocks/checklist/retest.d.ts +43 -0
- package/dist/artifact-blocks/blocks/checklist/retest.js +68 -0
- package/dist/artifact-blocks/blocks/checklist/schema.d.ts +84 -0
- package/dist/artifact-blocks/blocks/checklist/schema.js +91 -0
- package/dist/artifact-blocks/blocks/code/CodeBlock.svelte +128 -0
- package/dist/artifact-blocks/blocks/code/CodeBlock.svelte.d.ts +12 -0
- package/dist/artifact-blocks/blocks/code/block.d.ts +8 -0
- package/dist/artifact-blocks/blocks/code/block.js +31 -0
- package/dist/artifact-blocks/blocks/code/highlight.d.ts +34 -0
- package/dist/artifact-blocks/blocks/code/highlight.js +280 -0
- package/dist/artifact-blocks/blocks/code/schema.d.ts +32 -0
- package/dist/artifact-blocks/blocks/code/schema.js +39 -0
- package/dist/artifact-blocks/blocks/collage/CollageBlock.svelte +290 -0
- package/dist/artifact-blocks/blocks/collage/CollageBlock.svelte.d.ts +29 -0
- package/dist/artifact-blocks/blocks/collage/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/collage/block.js +29 -0
- package/dist/artifact-blocks/blocks/collage/schema.d.ts +52 -0
- package/dist/artifact-blocks/blocks/collage/schema.js +86 -0
- package/dist/artifact-blocks/blocks/compare/CompareBlock.svelte +484 -0
- package/dist/artifact-blocks/blocks/compare/CompareBlock.svelte.d.ts +25 -0
- package/dist/artifact-blocks/blocks/compare/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/compare/block.js +32 -0
- package/dist/artifact-blocks/blocks/compare/schema.d.ts +74 -0
- package/dist/artifact-blocks/blocks/compare/schema.js +148 -0
- package/dist/artifact-blocks/blocks/diagram/DiagramBlock.svelte +392 -0
- package/dist/artifact-blocks/blocks/diagram/DiagramBlock.svelte.d.ts +30 -0
- package/dist/artifact-blocks/blocks/diagram/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/diagram/block.js +50 -0
- package/dist/artifact-blocks/blocks/diagram/constants.d.ts +48 -0
- package/dist/artifact-blocks/blocks/diagram/constants.js +53 -0
- package/dist/artifact-blocks/blocks/diagram/schema.d.ts +161 -0
- package/dist/artifact-blocks/blocks/diagram/schema.js +621 -0
- package/dist/artifact-blocks/blocks/diagram/thumb.d.ts +2 -0
- package/dist/artifact-blocks/blocks/diagram/thumb.js +157 -0
- package/dist/artifact-blocks/blocks/diagram/types.d.ts +74 -0
- package/dist/artifact-blocks/blocks/diagram/types.js +1 -0
- package/dist/artifact-blocks/blocks/diff/DiffBlock.svelte +81 -0
- package/dist/artifact-blocks/blocks/diff/DiffBlock.svelte.d.ts +10 -0
- package/dist/artifact-blocks/blocks/diff/block.d.ts +7 -0
- package/dist/artifact-blocks/blocks/diff/block.js +36 -0
- package/dist/artifact-blocks/blocks/diff/schema.d.ts +18 -0
- package/dist/artifact-blocks/blocks/diff/schema.js +35 -0
- package/dist/artifact-blocks/blocks/diff-review/DiffReviewBlock.svelte +1055 -0
- package/dist/artifact-blocks/blocks/diff-review/DiffReviewBlock.svelte.d.ts +5 -0
- package/dist/artifact-blocks/blocks/diff-review/block.d.ts +15 -0
- package/dist/artifact-blocks/blocks/diff-review/block.js +64 -0
- package/dist/artifact-blocks/blocks/diff-review/decisions.d.ts +57 -0
- package/dist/artifact-blocks/blocks/diff-review/decisions.js +80 -0
- package/dist/artifact-blocks/blocks/diff-review/schema.d.ts +13 -0
- package/dist/artifact-blocks/blocks/diff-review/schema.js +84 -0
- package/dist/artifact-blocks/blocks/diff-review/types.d.ts +35 -0
- package/dist/artifact-blocks/blocks/diff-review/types.js +1 -0
- package/dist/artifact-blocks/blocks/explainer/ExplainerBlock.svelte +145 -0
- package/dist/artifact-blocks/blocks/explainer/ExplainerBlock.svelte.d.ts +5 -0
- package/dist/artifact-blocks/blocks/explainer/block.d.ts +8 -0
- package/dist/artifact-blocks/blocks/explainer/block.js +21 -0
- package/dist/artifact-blocks/blocks/explainer/schema.d.ts +26 -0
- package/dist/artifact-blocks/blocks/explainer/schema.js +69 -0
- package/dist/artifact-blocks/blocks/group-content.d.ts +10 -0
- package/dist/artifact-blocks/blocks/group-content.js +16 -0
- package/dist/artifact-blocks/blocks/group-data.d.ts +10 -0
- package/dist/artifact-blocks/blocks/group-data.js +7 -0
- package/dist/artifact-blocks/blocks/group-review.d.ts +10 -0
- package/dist/artifact-blocks/blocks/group-review.js +12 -0
- package/dist/artifact-blocks/blocks/group-visual.d.ts +17 -0
- package/dist/artifact-blocks/blocks/group-visual.js +14 -0
- package/dist/artifact-blocks/blocks/map/MapBlock.svelte +413 -0
- package/dist/artifact-blocks/blocks/map/MapBlock.svelte.d.ts +53 -0
- package/dist/artifact-blocks/blocks/map/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/map/block.js +62 -0
- package/dist/artifact-blocks/blocks/map/schema.d.ts +123 -0
- package/dist/artifact-blocks/blocks/map/schema.js +113 -0
- package/dist/artifact-blocks/blocks/map/thumb.d.ts +2 -0
- package/dist/artifact-blocks/blocks/map/thumb.js +69 -0
- package/dist/artifact-blocks/blocks/markdown/MarkdownBlock.svelte +33 -0
- package/dist/artifact-blocks/blocks/markdown/MarkdownBlock.svelte.d.ts +15 -0
- package/dist/artifact-blocks/blocks/markdown/block.d.ts +7 -0
- package/dist/artifact-blocks/blocks/markdown/block.js +28 -0
- package/dist/artifact-blocks/blocks/markdown/render.d.ts +17 -0
- package/dist/artifact-blocks/blocks/markdown/render.js +115 -0
- package/dist/artifact-blocks/blocks/markdown/schema.d.ts +10 -0
- package/dist/artifact-blocks/blocks/markdown/schema.js +23 -0
- package/dist/artifact-blocks/blocks/media-picker/MediaPickerBlock.svelte +2112 -0
- package/dist/artifact-blocks/blocks/media-picker/MediaPickerBlock.svelte.d.ts +5 -0
- package/dist/artifact-blocks/blocks/media-picker/block.d.ts +24 -0
- package/dist/artifact-blocks/blocks/media-picker/block.js +137 -0
- package/dist/artifact-blocks/blocks/media-picker/choices.d.ts +28 -0
- package/dist/artifact-blocks/blocks/media-picker/choices.js +35 -0
- package/dist/artifact-blocks/blocks/media-picker/decisions.d.ts +51 -0
- package/dist/artifact-blocks/blocks/media-picker/decisions.js +75 -0
- package/dist/artifact-blocks/blocks/media-picker/schema.d.ts +7 -0
- package/dist/artifact-blocks/blocks/media-picker/schema.js +199 -0
- package/dist/artifact-blocks/blocks/media-picker/types.d.ts +92 -0
- package/dist/artifact-blocks/blocks/media-picker/types.js +1 -0
- package/dist/artifact-blocks/blocks/media-picker/versions.d.ts +51 -0
- package/dist/artifact-blocks/blocks/media-picker/versions.js +118 -0
- package/dist/artifact-blocks/blocks/mockup/MockupBlock.svelte +213 -0
- package/dist/artifact-blocks/blocks/mockup/MockupBlock.svelte.d.ts +5 -0
- package/dist/artifact-blocks/blocks/mockup/block.d.ts +26 -0
- package/dist/artifact-blocks/blocks/mockup/block.js +52 -0
- package/dist/artifact-blocks/blocks/mockup/doc.d.ts +24 -0
- package/dist/artifact-blocks/blocks/mockup/doc.js +44 -0
- package/dist/artifact-blocks/blocks/mockup/sandbox.d.ts +21 -0
- package/dist/artifact-blocks/blocks/mockup/sandbox.js +27 -0
- package/dist/artifact-blocks/blocks/mockup/schema.d.ts +7 -0
- package/dist/artifact-blocks/blocks/mockup/schema.js +90 -0
- package/dist/artifact-blocks/blocks/mockup/types.d.ts +36 -0
- package/dist/artifact-blocks/blocks/mockup/types.js +17 -0
- package/dist/artifact-blocks/blocks/program/ProgramBlock.svelte +359 -0
- package/dist/artifact-blocks/blocks/program/ProgramBlock.svelte.d.ts +32 -0
- package/dist/artifact-blocks/blocks/program/block.d.ts +35 -0
- package/dist/artifact-blocks/blocks/program/block.js +68 -0
- package/dist/artifact-blocks/blocks/program/schema.d.ts +132 -0
- package/dist/artifact-blocks/blocks/program/schema.js +268 -0
- package/dist/artifact-blocks/blocks/stats/StatsBlock.svelte +315 -0
- package/dist/artifact-blocks/blocks/stats/StatsBlock.svelte.d.ts +24 -0
- package/dist/artifact-blocks/blocks/stats/block.d.ts +7 -0
- package/dist/artifact-blocks/blocks/stats/block.js +49 -0
- package/dist/artifact-blocks/blocks/stats/schema.d.ts +82 -0
- package/dist/artifact-blocks/blocks/stats/schema.js +43 -0
- package/dist/artifact-blocks/blocks/stats/thumb.d.ts +2 -0
- package/dist/artifact-blocks/blocks/stats/thumb.js +102 -0
- package/dist/artifact-blocks/blocks/table/TableBlock.svelte +490 -0
- package/dist/artifact-blocks/blocks/table/TableBlock.svelte.d.ts +28 -0
- package/dist/artifact-blocks/blocks/table/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/table/block.js +100 -0
- package/dist/artifact-blocks/blocks/table/schema.d.ts +143 -0
- package/dist/artifact-blocks/blocks/table/schema.js +278 -0
- package/dist/artifact-blocks/blocks/timeline/TimelineBlock.svelte +347 -0
- package/dist/artifact-blocks/blocks/timeline/TimelineBlock.svelte.d.ts +42 -0
- package/dist/artifact-blocks/blocks/timeline/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/timeline/block.js +96 -0
- package/dist/artifact-blocks/blocks/timeline/constants.d.ts +26 -0
- package/dist/artifact-blocks/blocks/timeline/constants.js +27 -0
- package/dist/artifact-blocks/blocks/timeline/schema.d.ts +102 -0
- package/dist/artifact-blocks/blocks/timeline/schema.js +405 -0
- package/dist/artifact-blocks/blocks/timeline/thumb.d.ts +2 -0
- package/dist/artifact-blocks/blocks/timeline/thumb.js +117 -0
- package/dist/artifact-blocks/blocks/timeline/types.d.ts +66 -0
- package/dist/artifact-blocks/blocks/timeline/types.js +1 -0
- package/dist/artifact-blocks/blocks/transcript/TranscriptBlock.svelte +169 -0
- package/dist/artifact-blocks/blocks/transcript/TranscriptBlock.svelte.d.ts +26 -0
- package/dist/artifact-blocks/blocks/transcript/block.d.ts +9 -0
- package/dist/artifact-blocks/blocks/transcript/block.js +86 -0
- package/dist/artifact-blocks/blocks/transcript/schema.d.ts +114 -0
- package/dist/artifact-blocks/blocks/transcript/schema.js +126 -0
- package/dist/artifact-blocks/builtins.d.ts +40 -0
- package/dist/artifact-blocks/builtins.js +22 -0
- package/dist/artifact-blocks/contractBlocks.d.ts +92 -0
- package/dist/artifact-blocks/contractBlocks.js +279 -0
- package/dist/artifact-blocks/history.d.ts +140 -0
- package/dist/artifact-blocks/history.js +363 -0
- package/dist/artifact-blocks/index.d.ts +40 -0
- package/dist/artifact-blocks/index.js +46 -0
- package/dist/artifact-blocks/listen/context.d.ts +30 -0
- package/dist/artifact-blocks/listen/context.js +30 -0
- package/dist/artifact-blocks/liveData.d.ts +78 -0
- package/dist/artifact-blocks/liveData.js +137 -0
- package/dist/artifact-blocks/liveDataPoller.d.ts +41 -0
- package/dist/artifact-blocks/liveDataPoller.js +140 -0
- package/dist/artifact-blocks/openNav.d.ts +46 -0
- package/dist/artifact-blocks/openNav.js +106 -0
- package/dist/artifact-blocks/pageReviewKeys.d.ts +17 -0
- package/dist/artifact-blocks/pageReviewKeys.js +115 -0
- package/dist/artifact-blocks/params.d.ts +98 -0
- package/dist/artifact-blocks/params.js +159 -0
- package/dist/artifact-blocks/registry.d.ts +27 -0
- package/dist/artifact-blocks/registry.js +83 -0
- package/dist/artifact-blocks/review/BlockIcon.svelte +99 -0
- package/dist/artifact-blocks/review/BlockIcon.svelte.d.ts +17 -0
- package/dist/artifact-blocks/review/BulkRejectForm.svelte +94 -0
- package/dist/artifact-blocks/review/BulkRejectForm.svelte.d.ts +11 -0
- package/dist/artifact-blocks/review/BulkUndoBar.svelte +40 -0
- package/dist/artifact-blocks/review/BulkUndoBar.svelte.d.ts +9 -0
- package/dist/artifact-blocks/review/ItemNote.svelte +193 -0
- package/dist/artifact-blocks/review/ItemNote.svelte.d.ts +33 -0
- package/dist/artifact-blocks/review/NoteToggle.svelte +52 -0
- package/dist/artifact-blocks/review/NoteToggle.svelte.d.ts +12 -0
- package/dist/artifact-blocks/review/TeamChip.svelte +71 -0
- package/dist/artifact-blocks/review/TeamChip.svelte.d.ts +8 -0
- package/dist/artifact-blocks/review/attachments.d.ts +10 -0
- package/dist/artifact-blocks/review/attachments.js +37 -0
- package/dist/artifact-blocks/review/bulk.d.ts +52 -0
- package/dist/artifact-blocks/review/bulk.js +61 -0
- package/dist/artifact-blocks/review/itemReview.d.ts +33 -0
- package/dist/artifact-blocks/review/itemReview.js +112 -0
- package/dist/artifact-blocks/review/roundLines.d.ts +14 -0
- package/dist/artifact-blocks/review/roundLines.js +20 -0
- package/dist/artifact-blocks/review/rounds.d.ts +86 -0
- package/dist/artifact-blocks/review/rounds.js +302 -0
- package/dist/artifact-blocks/review/saver.d.ts +23 -0
- package/dist/artifact-blocks/review/saver.js +48 -0
- package/dist/artifact-blocks/review/team.d.ts +31 -0
- package/dist/artifact-blocks/review/team.js +42 -0
- package/dist/artifact-blocks/review/visualOrder.d.ts +29 -0
- package/dist/artifact-blocks/review/visualOrder.js +66 -0
- package/dist/artifact-blocks/review.d.ts +180 -0
- package/dist/artifact-blocks/review.js +716 -0
- package/dist/artifact-blocks/thumb-kit.d.ts +80 -0
- package/dist/artifact-blocks/thumb-kit.js +149 -0
- package/dist/artifact-blocks/types.d.ts +513 -0
- package/dist/artifact-blocks/types.js +1 -0
- package/package.json +11 -2
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* actions block — buttons and small forms that send a REQUEST to the
|
|
4
|
+
* owning agent (mos-p9auh, living artifacts phase 4).
|
|
5
|
+
*
|
|
6
|
+
* A press goes to the HOST's actions service (`ctx.services.actions`,
|
|
7
|
+
* requests.ts ActionsService — MOS: POST /api/v1/artifacts/[id]/actions,
|
|
8
|
+
* one artifact_events row of kind 'action'). The agent sees it, acts and
|
|
9
|
+
* acks; this block polls the request list while anything is open and
|
|
10
|
+
* walks the button through Queued → Running → Done ✓ / Failed with the
|
|
11
|
+
* agent's note. The renderer never runs the action itself.
|
|
12
|
+
*
|
|
13
|
+
* Live only where the viewer can act (ctx.saveState present, not
|
|
14
|
+
* readonly) AND the host wired the service. Elsewhere — a share link,
|
|
15
|
+
* print, previews, a host with no actions leg — the actions render as a
|
|
16
|
+
* quiet list with no controls and a line saying why.
|
|
17
|
+
*
|
|
18
|
+
* Link actions (`href`, PARAMS-SPEC §2) are plain `<a>`s: they navigate,
|
|
19
|
+
* fire no event and need no service or role, so they work everywhere.
|
|
20
|
+
*/
|
|
21
|
+
import BlockIcon from "../../review/BlockIcon.svelte"
|
|
22
|
+
import { onDestroy, onMount } from "svelte"
|
|
23
|
+
import type { BlockProps } from "../../types.js"
|
|
24
|
+
import {
|
|
25
|
+
intentOf,
|
|
26
|
+
isLinkAction,
|
|
27
|
+
isOffOrigin,
|
|
28
|
+
isTerminal,
|
|
29
|
+
validateActionRequest,
|
|
30
|
+
type ActionDef,
|
|
31
|
+
type ActionsParams,
|
|
32
|
+
type AckStatus,
|
|
33
|
+
} from "./schema.js"
|
|
34
|
+
import {
|
|
35
|
+
actionsServiceOf,
|
|
36
|
+
latestByAction,
|
|
37
|
+
type ActionRequestView,
|
|
38
|
+
} from "./requests.js"
|
|
39
|
+
import SectionTools from "../../SectionTools.svelte"
|
|
40
|
+
|
|
41
|
+
let { params, ctx }: BlockProps<ActionsParams> = $props()
|
|
42
|
+
|
|
43
|
+
/** Poll cadence while a request is open (queued/running). */
|
|
44
|
+
const POLL_MS = 4000
|
|
45
|
+
|
|
46
|
+
const service = $derived(actionsServiceOf(ctx?.services))
|
|
47
|
+
/** The viewer could act here, but the host wired no actions service. */
|
|
48
|
+
const unwired = $derived(
|
|
49
|
+
Boolean(ctx?.saveState) && !ctx?.readonly && service === null,
|
|
50
|
+
)
|
|
51
|
+
const live = $derived(
|
|
52
|
+
Boolean(ctx?.saveState) && !ctx?.readonly && service !== null,
|
|
53
|
+
)
|
|
54
|
+
const origin = typeof location !== "undefined" ? location.origin : undefined
|
|
55
|
+
const requestActions = $derived(
|
|
56
|
+
(params?.actions ?? []).filter((a) => !isLinkAction(a)),
|
|
57
|
+
)
|
|
58
|
+
const actions = $derived(params?.actions ?? [])
|
|
59
|
+
|
|
60
|
+
let requests = $state<ActionRequestView[]>([])
|
|
61
|
+
let allowed = $state<Record<string, boolean> | null>(null)
|
|
62
|
+
let loadError = $state<string | null>(null)
|
|
63
|
+
/** actionId → in-flight POST */
|
|
64
|
+
let sending = $state<Record<string, boolean>>({})
|
|
65
|
+
/** actionId → last send/validation error */
|
|
66
|
+
let sendError = $state<Record<string, string>>({})
|
|
67
|
+
/** actionId → the danger confirm panel is open */
|
|
68
|
+
let confirming = $state<Record<string, boolean>>({})
|
|
69
|
+
/** actionId → typed reason (danger) */
|
|
70
|
+
let reasons = $state<Record<string, string>>({})
|
|
71
|
+
/** actionId → fieldId → raw form value */
|
|
72
|
+
let values = $state<Record<string, Record<string, string>>>({})
|
|
73
|
+
|
|
74
|
+
// Seed form values from field defaults once per action id.
|
|
75
|
+
$effect.pre(() => {
|
|
76
|
+
for (const a of requestActions) {
|
|
77
|
+
if (values[a.id]) continue
|
|
78
|
+
const seeded: Record<string, string> = {}
|
|
79
|
+
for (const f of a.fields) {
|
|
80
|
+
if (f.default !== undefined) seeded[f.id] = String(f.default)
|
|
81
|
+
else if (f.type === "select" && f.required)
|
|
82
|
+
seeded[f.id] = f.options[0]?.value ?? ""
|
|
83
|
+
else seeded[f.id] = ""
|
|
84
|
+
}
|
|
85
|
+
values[a.id] = seeded
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
const latest = $derived(latestByAction(requests))
|
|
90
|
+
const anyOpen = $derived(requests.some((r) => !isTerminal(r.status)))
|
|
91
|
+
|
|
92
|
+
let timer: ReturnType<typeof setTimeout> | null = null
|
|
93
|
+
let destroyed = false
|
|
94
|
+
|
|
95
|
+
async function refresh() {
|
|
96
|
+
if (!live || !service || !ctx?.artifactId) return
|
|
97
|
+
try {
|
|
98
|
+
const out = await service.load({
|
|
99
|
+
artifactId: ctx.artifactId,
|
|
100
|
+
blockId: ctx.blockId,
|
|
101
|
+
})
|
|
102
|
+
requests = out.requests
|
|
103
|
+
allowed = out.allowed
|
|
104
|
+
loadError = null
|
|
105
|
+
} catch (e) {
|
|
106
|
+
loadError =
|
|
107
|
+
e instanceof Error ? e.message : "Could not load request status"
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function schedule() {
|
|
112
|
+
if (timer) clearTimeout(timer)
|
|
113
|
+
timer = null
|
|
114
|
+
if (destroyed || !live || !anyOpen) return
|
|
115
|
+
timer = setTimeout(async () => {
|
|
116
|
+
if (typeof document !== "undefined" && document.hidden) {
|
|
117
|
+
schedule()
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
await refresh()
|
|
121
|
+
schedule()
|
|
122
|
+
}, POLL_MS)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
onMount(() => {
|
|
126
|
+
if (!live) return
|
|
127
|
+
void refresh().then(schedule)
|
|
128
|
+
})
|
|
129
|
+
onDestroy(() => {
|
|
130
|
+
destroyed = true
|
|
131
|
+
if (timer) clearTimeout(timer)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
function mayAct(a: ActionDef): boolean {
|
|
135
|
+
if (!live) return false
|
|
136
|
+
// Until the first read answers, assume yes; the server re-checks anyway.
|
|
137
|
+
return allowed === null ? true : allowed[a.id] === true
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function send(a: ActionDef) {
|
|
141
|
+
if (!ctx?.artifactId || !service || sending[a.id]) return
|
|
142
|
+
const check = validateActionRequest(
|
|
143
|
+
a,
|
|
144
|
+
values[a.id] ?? {},
|
|
145
|
+
reasons[a.id] ?? "",
|
|
146
|
+
)
|
|
147
|
+
if (!check.ok) {
|
|
148
|
+
sendError[a.id] = check.error
|
|
149
|
+
return
|
|
150
|
+
}
|
|
151
|
+
sending[a.id] = true
|
|
152
|
+
sendError[a.id] = ""
|
|
153
|
+
try {
|
|
154
|
+
const req = await service.send({
|
|
155
|
+
artifactId: ctx.artifactId,
|
|
156
|
+
blockId: ctx.blockId,
|
|
157
|
+
actionId: a.id,
|
|
158
|
+
inputs: check.inputs,
|
|
159
|
+
reason: check.reason,
|
|
160
|
+
})
|
|
161
|
+
requests = [...requests.filter((r) => r.id !== req.id), req]
|
|
162
|
+
confirming[a.id] = false
|
|
163
|
+
reasons[a.id] = ""
|
|
164
|
+
schedule()
|
|
165
|
+
} catch (e) {
|
|
166
|
+
// Already open: show the existing request instead of a second one.
|
|
167
|
+
// Duck-typed (any error carrying `request`), so a host need not import
|
|
168
|
+
// ActionRequestError to say so.
|
|
169
|
+
const existing = (e as { request?: ActionRequestView | null } | null)
|
|
170
|
+
?.request
|
|
171
|
+
if (existing && typeof existing === "object" && existing.id) {
|
|
172
|
+
requests = [...requests.filter((r) => r.id !== existing.id), existing]
|
|
173
|
+
schedule()
|
|
174
|
+
}
|
|
175
|
+
sendError[a.id] = e instanceof Error ? e.message : "Request failed"
|
|
176
|
+
} finally {
|
|
177
|
+
sending[a.id] = false
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** The danger confirm needs the reason first; the button opens it. */
|
|
182
|
+
function primaryClick(a: ActionDef) {
|
|
183
|
+
if (a.variant === "danger" && !confirming[a.id]) {
|
|
184
|
+
const check = validateActionRequest(a, values[a.id] ?? {}, null, {
|
|
185
|
+
fieldsOnly: true,
|
|
186
|
+
})
|
|
187
|
+
if (!check.ok) {
|
|
188
|
+
sendError[a.id] = check.error
|
|
189
|
+
return
|
|
190
|
+
}
|
|
191
|
+
sendError[a.id] = ""
|
|
192
|
+
confirming[a.id] = true
|
|
193
|
+
return
|
|
194
|
+
}
|
|
195
|
+
void send(a)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const STATUS: Record<
|
|
199
|
+
AckStatus,
|
|
200
|
+
{ label: string; cls: string; icon: "clock" | "spin" | "check" | "x" }
|
|
201
|
+
> = {
|
|
202
|
+
queued: {
|
|
203
|
+
label: "Queued",
|
|
204
|
+
cls: "border-base-content/20 bg-base-200 text-base-content/80",
|
|
205
|
+
icon: "clock",
|
|
206
|
+
},
|
|
207
|
+
running: {
|
|
208
|
+
label: "Running",
|
|
209
|
+
cls: "border-info/40 bg-info/10 text-base-content",
|
|
210
|
+
icon: "spin",
|
|
211
|
+
},
|
|
212
|
+
done: {
|
|
213
|
+
label: "Done",
|
|
214
|
+
cls: "border-success/40 bg-success/10 text-base-content",
|
|
215
|
+
icon: "check",
|
|
216
|
+
},
|
|
217
|
+
failed: {
|
|
218
|
+
label: "Failed",
|
|
219
|
+
cls: "border-error/40 bg-error/10 text-base-content",
|
|
220
|
+
icon: "x",
|
|
221
|
+
},
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const whenFmt = new Intl.DateTimeFormat("en-US", {
|
|
225
|
+
month: "short",
|
|
226
|
+
day: "numeric",
|
|
227
|
+
hour: "numeric",
|
|
228
|
+
minute: "2-digit",
|
|
229
|
+
})
|
|
230
|
+
function when(iso: string | null | undefined): string {
|
|
231
|
+
if (!iso) return ""
|
|
232
|
+
const d = new Date(iso)
|
|
233
|
+
return Number.isFinite(d.getTime()) ? whenFmt.format(d) : ""
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function buttonClass(a: ActionDef): string {
|
|
237
|
+
if (a.variant === "danger") return "btn btn-sm btn-error"
|
|
238
|
+
if (a.variant === "primary") return "btn btn-sm btn-primary"
|
|
239
|
+
return "btn btn-sm btn-outline"
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const inputCls =
|
|
243
|
+
"input input-sm w-full rounded-field border-base-content/20 bg-base-100"
|
|
244
|
+
</script>
|
|
245
|
+
|
|
246
|
+
<section
|
|
247
|
+
class="rounded-box border border-base-content/10 bg-base-100 p-4 sm:p-5"
|
|
248
|
+
data-block="actions"
|
|
249
|
+
aria-label={params?.title ?? "Actions"}
|
|
250
|
+
>
|
|
251
|
+
{#if params?.title}
|
|
252
|
+
<div class="flex items-center gap-2">
|
|
253
|
+
<h3 class="min-w-0 text-base font-semibold text-base-content">
|
|
254
|
+
{params.title}
|
|
255
|
+
</h3>
|
|
256
|
+
<SectionTools {ctx} inline />
|
|
257
|
+
</div>
|
|
258
|
+
{/if}
|
|
259
|
+
{#if params?.description}
|
|
260
|
+
<p class="mt-1 text-sm whitespace-pre-line text-base-content/70">
|
|
261
|
+
{params.description}
|
|
262
|
+
</p>
|
|
263
|
+
{/if}
|
|
264
|
+
|
|
265
|
+
{#if !live && requestActions.length > 0}
|
|
266
|
+
<p class="mt-2 text-xs text-base-content/60" data-testid="actions-readonly">
|
|
267
|
+
{unwired
|
|
268
|
+
? "Requests can't be sent from this view."
|
|
269
|
+
: "Requests can be sent by signed-in workspace members."}
|
|
270
|
+
</p>
|
|
271
|
+
{:else if loadError}
|
|
272
|
+
<p class="mt-2 text-xs text-error" role="status">{loadError}</p>
|
|
273
|
+
{/if}
|
|
274
|
+
|
|
275
|
+
<ul class="mt-3 flex flex-col gap-3">
|
|
276
|
+
{#each actions as a (a.id)}
|
|
277
|
+
{#if isLinkAction(a)}
|
|
278
|
+
{@const external = isOffOrigin(a.href, origin)}
|
|
279
|
+
<li
|
|
280
|
+
class="rounded-field border border-base-content/10 p-3"
|
|
281
|
+
data-action={a.id}
|
|
282
|
+
data-link-action
|
|
283
|
+
>
|
|
284
|
+
<div class="flex flex-wrap items-center justify-between gap-2">
|
|
285
|
+
<div class="min-w-0 flex-1">
|
|
286
|
+
<p class="text-sm font-medium text-base-content">{a.label}</p>
|
|
287
|
+
{#if a.description}
|
|
288
|
+
<p class="mt-0.5 text-xs text-base-content/70">
|
|
289
|
+
{a.description}
|
|
290
|
+
</p>
|
|
291
|
+
{/if}
|
|
292
|
+
</div>
|
|
293
|
+
<a
|
|
294
|
+
class={buttonClass(a)}
|
|
295
|
+
href={a.href}
|
|
296
|
+
target={external ? "_blank" : undefined}
|
|
297
|
+
rel={external ? "noopener noreferrer" : undefined}
|
|
298
|
+
>
|
|
299
|
+
{a.label}
|
|
300
|
+
{#if external}
|
|
301
|
+
<BlockIcon name="external-link" size={14} />
|
|
302
|
+
<span class="sr-only"> (opens in a new tab)</span>
|
|
303
|
+
{/if}
|
|
304
|
+
</a>
|
|
305
|
+
</div>
|
|
306
|
+
</li>
|
|
307
|
+
{:else}
|
|
308
|
+
{@const req = latest.get(a.id)}
|
|
309
|
+
{@const open = req ? !isTerminal(req.status) : false}
|
|
310
|
+
{@const permitted = mayAct(a)}
|
|
311
|
+
{@const st = req ? STATUS[req.status] : null}
|
|
312
|
+
<li
|
|
313
|
+
class="rounded-field border border-base-content/10 p-3"
|
|
314
|
+
data-action={a.id}
|
|
315
|
+
data-status={req?.status ?? "idle"}
|
|
316
|
+
>
|
|
317
|
+
<form
|
|
318
|
+
class="flex flex-col gap-2"
|
|
319
|
+
onsubmit={(e) => {
|
|
320
|
+
e.preventDefault()
|
|
321
|
+
primaryClick(a)
|
|
322
|
+
}}
|
|
323
|
+
>
|
|
324
|
+
<div class="flex flex-wrap items-start justify-between gap-2">
|
|
325
|
+
<div class="min-w-0 flex-1">
|
|
326
|
+
<p class="text-sm font-medium text-base-content">{a.label}</p>
|
|
327
|
+
{#if a.description}
|
|
328
|
+
<p class="mt-0.5 text-xs text-base-content/70">
|
|
329
|
+
{a.description}
|
|
330
|
+
</p>
|
|
331
|
+
{/if}
|
|
332
|
+
<p class="mt-0.5 font-mono text-2xs text-base-content/50">
|
|
333
|
+
{intentOf(a)}
|
|
334
|
+
</p>
|
|
335
|
+
</div>
|
|
336
|
+
{#if st && req}
|
|
337
|
+
<span
|
|
338
|
+
class="inline-flex shrink-0 items-center gap-1 rounded-full border px-2 py-0.5 text-xs font-medium {st.cls}"
|
|
339
|
+
data-testid="action-status"
|
|
340
|
+
>
|
|
341
|
+
{#if st.icon === "clock"}
|
|
342
|
+
<BlockIcon name="clock" size={12} aria-hidden="true" />
|
|
343
|
+
{:else if st.icon === "spin"}
|
|
344
|
+
<BlockIcon name="loader-circle"
|
|
345
|
+
size={12}
|
|
346
|
+
class="motion-safe:animate-spin"
|
|
347
|
+
aria-hidden="true"
|
|
348
|
+
/>
|
|
349
|
+
{:else if st.icon === "check"}
|
|
350
|
+
<BlockIcon name="check" size={12} class="text-success" aria-hidden="true" />
|
|
351
|
+
{:else}
|
|
352
|
+
<BlockIcon name="circle-alert"
|
|
353
|
+
size={12}
|
|
354
|
+
class="text-error"
|
|
355
|
+
aria-hidden="true"
|
|
356
|
+
/>
|
|
357
|
+
{/if}
|
|
358
|
+
{st.label}{req.status === "done" ? " ✓" : ""}
|
|
359
|
+
</span>
|
|
360
|
+
{/if}
|
|
361
|
+
</div>
|
|
362
|
+
|
|
363
|
+
{#if a.fields.length > 0}
|
|
364
|
+
<div class="grid gap-2 sm:grid-cols-2">
|
|
365
|
+
{#each a.fields as f (f.id)}
|
|
366
|
+
{@const fid = `act-${ctx?.blockId}-${a.id}-${f.id}`}
|
|
367
|
+
<label
|
|
368
|
+
class="flex min-w-0 flex-col gap-1 text-xs text-base-content/75 {f.type ===
|
|
369
|
+
'text' && f.multiline
|
|
370
|
+
? 'sm:col-span-2'
|
|
371
|
+
: ''}"
|
|
372
|
+
for={fid}
|
|
373
|
+
>
|
|
374
|
+
<span
|
|
375
|
+
>{f.label}{#if f.required}<span
|
|
376
|
+
class="text-error"
|
|
377
|
+
aria-hidden="true">{" *"}</span
|
|
378
|
+
>{/if}</span
|
|
379
|
+
>
|
|
380
|
+
{#if !values[a.id]}
|
|
381
|
+
<!-- seeded on the next tick -->
|
|
382
|
+
{:else if f.type === "select"}
|
|
383
|
+
<div
|
|
384
|
+
class="select select-sm w-full rounded-field border-base-content/20"
|
|
385
|
+
>
|
|
386
|
+
<select
|
|
387
|
+
id={fid}
|
|
388
|
+
bind:value={values[a.id][f.id]}
|
|
389
|
+
disabled={!permitted || open}
|
|
390
|
+
required={f.required}
|
|
391
|
+
>
|
|
392
|
+
{#if !f.required}<option value="">—</option>{/if}
|
|
393
|
+
{#each f.options as o (o.value)}
|
|
394
|
+
<option value={o.value}>{o.label}</option>
|
|
395
|
+
{/each}
|
|
396
|
+
</select>
|
|
397
|
+
</div>
|
|
398
|
+
{:else if f.type === "number"}
|
|
399
|
+
<input
|
|
400
|
+
id={fid}
|
|
401
|
+
type="number"
|
|
402
|
+
class={inputCls}
|
|
403
|
+
bind:value={values[a.id][f.id]}
|
|
404
|
+
min={f.min}
|
|
405
|
+
max={f.max}
|
|
406
|
+
step={f.step ?? "any"}
|
|
407
|
+
disabled={!permitted || open}
|
|
408
|
+
required={f.required}
|
|
409
|
+
/>
|
|
410
|
+
{:else if f.multiline}
|
|
411
|
+
<textarea
|
|
412
|
+
id={fid}
|
|
413
|
+
class="textarea textarea-sm w-full rounded-field border-base-content/20"
|
|
414
|
+
rows="2"
|
|
415
|
+
bind:value={values[a.id][f.id]}
|
|
416
|
+
maxlength={f.maxLength}
|
|
417
|
+
placeholder={f.placeholder}
|
|
418
|
+
disabled={!permitted || open}
|
|
419
|
+
required={f.required}
|
|
420
|
+
></textarea>
|
|
421
|
+
{:else}
|
|
422
|
+
<input
|
|
423
|
+
id={fid}
|
|
424
|
+
type="text"
|
|
425
|
+
class={inputCls}
|
|
426
|
+
bind:value={values[a.id][f.id]}
|
|
427
|
+
maxlength={f.maxLength}
|
|
428
|
+
placeholder={f.placeholder}
|
|
429
|
+
disabled={!permitted || open}
|
|
430
|
+
required={f.required}
|
|
431
|
+
/>
|
|
432
|
+
{/if}
|
|
433
|
+
</label>
|
|
434
|
+
{/each}
|
|
435
|
+
</div>
|
|
436
|
+
{/if}
|
|
437
|
+
|
|
438
|
+
{#if confirming[a.id]}
|
|
439
|
+
<div
|
|
440
|
+
class="flex flex-col gap-2 rounded-field border border-error/40 bg-error/10 p-3"
|
|
441
|
+
data-testid="action-confirm"
|
|
442
|
+
>
|
|
443
|
+
<p class="flex items-start gap-2 text-sm text-base-content">
|
|
444
|
+
<BlockIcon name="triangle-alert"
|
|
445
|
+
size={16}
|
|
446
|
+
class="mt-0.5 shrink-0 text-error"
|
|
447
|
+
aria-hidden="true"
|
|
448
|
+
/>
|
|
449
|
+
<span
|
|
450
|
+
>{a.confirm ??
|
|
451
|
+
`This asks the agent to “${a.label}”. Say why before it is sent.`}</span
|
|
452
|
+
>
|
|
453
|
+
</p>
|
|
454
|
+
<label
|
|
455
|
+
class="flex flex-col gap-1 text-xs text-base-content/75"
|
|
456
|
+
for={`act-${ctx?.blockId}-${a.id}-reason`}
|
|
457
|
+
>
|
|
458
|
+
Reason (required)
|
|
459
|
+
<textarea
|
|
460
|
+
id={`act-${ctx?.blockId}-${a.id}-reason`}
|
|
461
|
+
class="textarea textarea-sm w-full rounded-field border-base-content/20"
|
|
462
|
+
rows="2"
|
|
463
|
+
maxlength="500"
|
|
464
|
+
bind:value={reasons[a.id]}
|
|
465
|
+
></textarea>
|
|
466
|
+
</label>
|
|
467
|
+
<div class="flex flex-wrap gap-2">
|
|
468
|
+
<button
|
|
469
|
+
type="button"
|
|
470
|
+
class="btn btn-sm btn-error"
|
|
471
|
+
disabled={sending[a.id]}
|
|
472
|
+
onclick={() => void send(a)}
|
|
473
|
+
>
|
|
474
|
+
{#if sending[a.id]}<BlockIcon name="loader-circle"
|
|
475
|
+
size={14}
|
|
476
|
+
class="motion-safe:animate-spin"
|
|
477
|
+
aria-hidden="true"
|
|
478
|
+
/>{/if}
|
|
479
|
+
Send request
|
|
480
|
+
</button>
|
|
481
|
+
<button
|
|
482
|
+
type="button"
|
|
483
|
+
class="btn btn-sm btn-ghost"
|
|
484
|
+
onclick={() => {
|
|
485
|
+
confirming[a.id] = false
|
|
486
|
+
sendError[a.id] = ""
|
|
487
|
+
}}>Cancel</button
|
|
488
|
+
>
|
|
489
|
+
</div>
|
|
490
|
+
</div>
|
|
491
|
+
{:else if live}
|
|
492
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
493
|
+
<button
|
|
494
|
+
type="submit"
|
|
495
|
+
class={buttonClass(a)}
|
|
496
|
+
disabled={!permitted || open || sending[a.id]}
|
|
497
|
+
aria-describedby={req
|
|
498
|
+
? `act-${ctx?.blockId}-${a.id}-st`
|
|
499
|
+
: undefined}
|
|
500
|
+
>
|
|
501
|
+
{#if sending[a.id]}
|
|
502
|
+
<BlockIcon name="loader-circle"
|
|
503
|
+
size={14}
|
|
504
|
+
class="motion-safe:animate-spin"
|
|
505
|
+
aria-hidden="true"
|
|
506
|
+
/>
|
|
507
|
+
{:else}
|
|
508
|
+
<BlockIcon name="send" size={14} aria-hidden="true" />
|
|
509
|
+
{/if}
|
|
510
|
+
{req && isTerminal(req.status) ? `${a.label} again` : a.label}
|
|
511
|
+
</button>
|
|
512
|
+
{#if !permitted}
|
|
513
|
+
<span class="text-xs text-base-content/60"
|
|
514
|
+
>Your role can't send this request.</span
|
|
515
|
+
>
|
|
516
|
+
{:else if open}
|
|
517
|
+
<span class="text-xs text-base-content/60"
|
|
518
|
+
>Waiting on the agent…</span
|
|
519
|
+
>
|
|
520
|
+
{/if}
|
|
521
|
+
</div>
|
|
522
|
+
{/if}
|
|
523
|
+
|
|
524
|
+
{#if sendError[a.id]}
|
|
525
|
+
<p class="text-xs text-error" role="alert">{sendError[a.id]}</p>
|
|
526
|
+
{/if}
|
|
527
|
+
|
|
528
|
+
{#if req}
|
|
529
|
+
<div
|
|
530
|
+
id={`act-${ctx?.blockId}-${a.id}-st`}
|
|
531
|
+
class="text-xs text-base-content/70"
|
|
532
|
+
role="status"
|
|
533
|
+
aria-live="polite"
|
|
534
|
+
>
|
|
535
|
+
{#if req.note}
|
|
536
|
+
<p
|
|
537
|
+
class="whitespace-pre-line text-base-content"
|
|
538
|
+
data-testid="action-note"
|
|
539
|
+
>
|
|
540
|
+
{req.note}
|
|
541
|
+
</p>
|
|
542
|
+
{/if}
|
|
543
|
+
<p class="mt-0.5 text-base-content/60">
|
|
544
|
+
{req.mine ? "You" : (req.viewerLabel ?? "Someone")} asked · {when(
|
|
545
|
+
req.createdAt,
|
|
546
|
+
)}{#if req.ackedBy}{" · "}{req.ackedBy}{req.ackedAt
|
|
547
|
+
? ` · ${when(req.ackedAt)}`
|
|
548
|
+
: ""}{/if}
|
|
549
|
+
</p>
|
|
550
|
+
</div>
|
|
551
|
+
{/if}
|
|
552
|
+
</form>
|
|
553
|
+
</li>
|
|
554
|
+
{/if}
|
|
555
|
+
{/each}
|
|
556
|
+
</ul>
|
|
557
|
+
</section>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { BlockProps } from "../../types.js";
|
|
2
|
+
declare const ActionsBlock: import("svelte").Component<BlockProps<{
|
|
3
|
+
actions: {
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
variant: "primary" | "default" | "danger";
|
|
7
|
+
fields: ({
|
|
8
|
+
id: string;
|
|
9
|
+
type: "number";
|
|
10
|
+
label: string;
|
|
11
|
+
required: boolean;
|
|
12
|
+
min?: number | undefined;
|
|
13
|
+
max?: number | undefined;
|
|
14
|
+
step?: number | undefined;
|
|
15
|
+
default?: number | undefined;
|
|
16
|
+
} | {
|
|
17
|
+
id: string;
|
|
18
|
+
type: "select";
|
|
19
|
+
label: string;
|
|
20
|
+
required: boolean;
|
|
21
|
+
options: {
|
|
22
|
+
value: string;
|
|
23
|
+
label: string;
|
|
24
|
+
}[];
|
|
25
|
+
default?: string | undefined;
|
|
26
|
+
} | {
|
|
27
|
+
id: string;
|
|
28
|
+
type: "text";
|
|
29
|
+
label: string;
|
|
30
|
+
required: boolean;
|
|
31
|
+
multiline: boolean;
|
|
32
|
+
maxLength: number;
|
|
33
|
+
placeholder?: string | undefined;
|
|
34
|
+
default?: string | undefined;
|
|
35
|
+
})[];
|
|
36
|
+
description?: string | undefined;
|
|
37
|
+
intent?: string | undefined;
|
|
38
|
+
confirm?: string | undefined;
|
|
39
|
+
roles?: ("owner" | "admin" | "member" | "viewer" | "office" | "provider")[] | undefined;
|
|
40
|
+
owner?: string | undefined;
|
|
41
|
+
href?: string | undefined;
|
|
42
|
+
}[];
|
|
43
|
+
title?: string | undefined;
|
|
44
|
+
description?: string | undefined;
|
|
45
|
+
owner?: string | undefined;
|
|
46
|
+
}>, {}, "">;
|
|
47
|
+
type ActionsBlock = ReturnType<typeof ActionsBlock>;
|
|
48
|
+
export default ActionsBlock;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* actions block — buttons and small forms that send a REQUEST back to the
|
|
3
|
+
* owning agent (mos-p9auh, living artifacts phase 4).
|
|
4
|
+
*/
|
|
5
|
+
import type { ArtifactBlockDef } from "../../types.js";
|
|
6
|
+
import { type ActionsParams } from "./schema.js";
|
|
7
|
+
declare const def: ArtifactBlockDef<ActionsParams>;
|
|
8
|
+
export default def;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import ActionsBlock from "./ActionsBlock.svelte";
|
|
2
|
+
import { actionsSchema } from "./schema.js";
|
|
3
|
+
const EXAMPLE = {
|
|
4
|
+
title: "Soak controls",
|
|
5
|
+
description: "Ask the soak agent to re-run a scenario, apply loops, or pause.",
|
|
6
|
+
actions: [
|
|
7
|
+
{
|
|
8
|
+
id: "rerun-scenario",
|
|
9
|
+
label: "Re-run scenario",
|
|
10
|
+
description: "Queues one more soak pass of the chosen scenario.",
|
|
11
|
+
intent: "rerun-scenario",
|
|
12
|
+
variant: "primary",
|
|
13
|
+
fields: [
|
|
14
|
+
{
|
|
15
|
+
id: "scenario",
|
|
16
|
+
type: "select",
|
|
17
|
+
label: "Scenario",
|
|
18
|
+
required: true,
|
|
19
|
+
multiline: false,
|
|
20
|
+
options: [
|
|
21
|
+
{ value: "ruby_office_switch", label: "Ruby office switch" },
|
|
22
|
+
{ value: "after_hours", label: "After hours" },
|
|
23
|
+
],
|
|
24
|
+
default: "ruby_office_switch",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: "calls",
|
|
28
|
+
type: "number",
|
|
29
|
+
label: "Calls",
|
|
30
|
+
required: false,
|
|
31
|
+
min: 1,
|
|
32
|
+
max: 50,
|
|
33
|
+
default: 10,
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "apply-v3-loops",
|
|
39
|
+
label: "Approve & apply v3 loops",
|
|
40
|
+
intent: "apply-loops:v3",
|
|
41
|
+
variant: "default",
|
|
42
|
+
fields: [],
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: "open-uat",
|
|
46
|
+
label: "Open the UAT build",
|
|
47
|
+
description: "A link, not a request: opens the build to test.",
|
|
48
|
+
href: "https://uat.example.com/soak",
|
|
49
|
+
variant: "default",
|
|
50
|
+
fields: [],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: "pause-soak",
|
|
54
|
+
label: "Pause soak",
|
|
55
|
+
description: "Stops new calls; in-flight calls finish.",
|
|
56
|
+
intent: "pause-soak",
|
|
57
|
+
variant: "danger",
|
|
58
|
+
confirm: "Pausing stops the overnight soak until someone resumes it.",
|
|
59
|
+
roles: ["admin", "owner"],
|
|
60
|
+
fields: [],
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
const def = {
|
|
65
|
+
type: "actions",
|
|
66
|
+
title: "Actions (buttons that ask the agent)",
|
|
67
|
+
description: "Two-way page controls: buttons and small forms whose press sends a REQUEST back to the owning agent — 'Re-run scenario ruby_office_switch', 'Approve & apply v3 loops', 'Pause soak'. MOS never runs anything: a press records one event (kind 'action': viewer, intent, inputs, reason) that you receive through `mos-artifact watch --slug s --kind action` or `mos-artifact drain --owner <you>`; act on it, then answer with `mos-artifact ack --event <id> --status running|done|failed --note '…'` and the button walks Queued → Running → Done ✓ / Failed with your note (open pages poll every 4s). A finished request is final; pressing again is a new request, and while one is open the button is held so a double-click cannot queue twice. Params: title?, description?, owner? (the agent that hears these; falls back to the block owner, then the publisher), actions[1-12] of { id (stable, lowercase a-z0-9_-), label (≤80), description?, intent? (the machine string you switch on; defaults to id), variant default|primary|danger, confirm? (danger copy), roles? (exact team roles allowed — viewer|member|office|provider|admin|owner; default every member-rank role), owner? (per-action routing), fields[0-8] }. Fields: { id, type:'text', label, placeholder?, required?, multiline?, maxLength? (≤2000) } | { id, type:'number', label, min?, max?, step?, required?, default? } | { id, type:'select', label, options[1-30] {value,label}, required?, default? }. The server re-validates every input against these declarations and drops undeclared ones. variant 'danger' requires a confirm step AND a typed reason (sent to you as payload.reason). LINKS (PARAMS-SPEC §2): give an action `href` (http(s):// or root-relative) and it is a plain link button — it opens the url (off-origin in a new tab), sends no request, ignores intent/fields/confirm/roles/owner and needs no team role, so it works on a read-only share link too; use it for 'Open the UAT build', 'Open the task'. Requests go through the host's actions service (jatui HostAdapter.services.actions); a host without one shows the request list without controls and says requests can't be sent from that view. Rate-limited per viewer. Signed-out / public-link viewers see the list without controls. Tip: one action per thing the agent can actually do, and always ack — an unacked request sits at Queued.",
|
|
68
|
+
schema: actionsSchema,
|
|
69
|
+
component: ActionsBlock,
|
|
70
|
+
// mos-8fvt9: the section's Listen + comment sit on the heading row.
|
|
71
|
+
hostsTools: (p) => Boolean(p.title),
|
|
72
|
+
example: EXAMPLE,
|
|
73
|
+
};
|
|
74
|
+
export default def;
|