@mawaru/sdk 0.39.0 → 0.40.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.
Files changed (84) hide show
  1. package/_schemas/graph.test.ts +15 -9
  2. package/_schemas/graph.ts +20 -6
  3. package/_schemas/human-manifest.test.ts +129 -0
  4. package/_schemas/human-manifest.ts +158 -0
  5. package/_schemas/port-spec.test.ts +40 -40
  6. package/_schemas/port-spec.ts +10 -24
  7. package/dist/_cli/login.js +1 -15
  8. package/dist/_cli/openBrowser.d.ts +1 -0
  9. package/dist/_cli/openBrowser.js +15 -0
  10. package/dist/_cli/previewApi.d.ts +1 -1
  11. package/dist/_cli/previewCommand.d.ts +8 -0
  12. package/dist/_cli/previewCommand.js +95 -37
  13. package/dist/_cli/previewFiles.d.ts +2 -1
  14. package/dist/_cli/previewFiles.js +42 -19
  15. package/dist/_cli/previewSlack.d.ts +1 -1
  16. package/dist/_cli/previewSlack.js +1 -1
  17. package/dist/_schemas/graph.d.ts +21 -3
  18. package/dist/_schemas/graph.js +17 -6
  19. package/dist/_schemas/human-manifest.d.ts +27 -0
  20. package/dist/_schemas/human-manifest.js +121 -0
  21. package/dist/_schemas/port-spec.d.ts +6 -9
  22. package/dist/_schemas/port-spec.js +7 -7
  23. package/dist/cli.js +3 -2
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.js +1 -0
  26. package/dist/init.js +22 -3
  27. package/dist/typegen.d.ts +3 -1
  28. package/dist/typegen.js +96 -14
  29. package/dist/validate.js +55 -0
  30. package/dist/views/_approval/ApprovalBar.d.ts +20 -0
  31. package/dist/views/_approval/ApprovalBar.js +74 -0
  32. package/dist/views/_approvalActions.d.ts +18 -0
  33. package/dist/views/_approvalActions.js +10 -0
  34. package/dist/views/_bridge.d.ts +12 -0
  35. package/dist/views/_bridge.js +10 -0
  36. package/dist/views/_parts/DocumentCard.d.ts +2 -0
  37. package/dist/views/_parts/DocumentCard.js +16 -0
  38. package/dist/views/_parts/FilePreview.d.ts +5 -0
  39. package/dist/views/_parts/FilePreview.js +101 -0
  40. package/dist/views/_parts/Linkify.d.ts +7 -0
  41. package/dist/views/_parts/Linkify.js +28 -0
  42. package/dist/views/_parts/Markdown.d.ts +44 -0
  43. package/dist/views/_parts/Markdown.js +313 -0
  44. package/dist/views/_parts/SchemaForm.d.ts +19 -0
  45. package/dist/views/_parts/SchemaForm.js +310 -0
  46. package/dist/views/_parts/fileRef.d.ts +7 -0
  47. package/dist/views/_parts/fileRef.js +11 -0
  48. package/dist/views/_parts/useNarrow.d.ts +5 -0
  49. package/dist/views/_parts/useNarrow.js +23 -0
  50. package/dist/views/host.d.ts +18 -0
  51. package/dist/views/host.js +20 -0
  52. package/dist/views/human.d.ts +73 -0
  53. package/dist/views/human.js +6 -0
  54. package/dist/views/index.d.ts +8 -0
  55. package/dist/views/index.js +12 -0
  56. package/dist/views/protocol.d.ts +34 -0
  57. package/dist/views/protocol.js +5 -0
  58. package/dist/views/runtime.d.ts +3 -0
  59. package/dist/views/runtime.js +86 -0
  60. package/dist/{slack-view.d.ts → views/slack.d.ts} +1 -1
  61. package/docs/development.md +40 -2
  62. package/index.ts +1 -0
  63. package/package.json +36 -4
  64. package/skills/create-loop/SKILL.md +6 -3
  65. package/views/_approval/ApprovalBar.tsx +239 -0
  66. package/views/_approvalActions.tsx +31 -0
  67. package/views/_bridge.tsx +36 -0
  68. package/views/_parts/DocumentCard.ts +18 -0
  69. package/views/_parts/FilePreview.tsx +180 -0
  70. package/views/_parts/Linkify.test.ts +40 -0
  71. package/views/_parts/Linkify.tsx +40 -0
  72. package/views/_parts/Markdown.test.ts +131 -0
  73. package/views/_parts/Markdown.tsx +472 -0
  74. package/views/_parts/SchemaForm.tsx +742 -0
  75. package/views/_parts/fileRef.ts +21 -0
  76. package/views/_parts/useNarrow.ts +24 -0
  77. package/views/host.ts +23 -0
  78. package/views/human.ts +91 -0
  79. package/views/index.ts +16 -0
  80. package/views/protocol.ts +40 -0
  81. package/views/runtime.tsx +132 -0
  82. package/views/slack.test.ts +40 -0
  83. package/{slack-view.ts → views/slack.ts} +3 -3
  84. /package/dist/{slack-view.js → views/slack.js} +0 -0
@@ -0,0 +1,21 @@
1
+ // ノード間を流れる file の値(storage path の参照。バイトや署名 URL は載らない)。
2
+ // @mawaru/common の fileRefSchema(zod)と同じ形で、部品キットは依存を増やさないよう型と判定だけ持つ
3
+ export type FileRef = {
4
+ path: string
5
+ name: string
6
+ mime_type?: string
7
+ size?: number
8
+ }
9
+
10
+ export const isFileRef = (value: unknown): value is FileRef => {
11
+ if (typeof value !== "object" || value === null) return false
12
+ const v = value as Record<string, unknown>
13
+ return (
14
+ typeof v.path === "string" &&
15
+ v.path !== "" &&
16
+ typeof v.name === "string" &&
17
+ v.name !== "" &&
18
+ (v.mime_type === undefined || typeof v.mime_type === "string") &&
19
+ (v.size === undefined || typeof v.size === "number")
20
+ )
21
+ }
@@ -0,0 +1,24 @@
1
+ import { useEffect, useRef, useState } from "react"
2
+
3
+ // 承認ビューの幅対応。判定は**ビューポートではなくコンテナの幅**で行う。
4
+ //
5
+ // 開発プレビュー(/dev/views/approval)は幅の確認を「狭いコンテナ」で再現するので、
6
+ // media query で判定すると実機とプレビューで挙動がズレる(プレビューでは常に
7
+ // デスクトップ扱いになる)。ResizeObserver でコンテナ自身を測れば両方で同じになる。
8
+
9
+ export const NARROW_WIDTH = 560
10
+
11
+ export const useNarrow = <T extends HTMLElement>() => {
12
+ const ref = useRef<T>(null)
13
+ const [narrow, setNarrow] = useState(false)
14
+ useEffect(() => {
15
+ const element = ref.current
16
+ if (!element) return
17
+ const observer = new ResizeObserver(([entry]) => {
18
+ if (entry) setNarrow(entry.contentRect.width < NARROW_WIDTH)
19
+ })
20
+ observer.observe(element)
21
+ return () => observer.disconnect()
22
+ }, [])
23
+ return { ref, narrow }
24
+ }
package/views/host.ts ADDED
@@ -0,0 +1,23 @@
1
+ import { z } from "zod"
2
+ import { VIEW_MESSAGE_TAG } from "./protocol.js"
3
+
4
+ export { ApprovalActionsProvider } from "./_approvalActions.js"
5
+ // ホスト(mawaru の画面)だけが使うもの(公開 API ではない):sandbox iframe からのメッセージを確かめる形
6
+ // (protocol.ts の型と同じ。iframe の中は信用できないコードなので、形を確かめてから使う)と、
7
+ // ビューに頼む口を差す提供部品
8
+ export { ViewBridgeProvider } from "./_bridge.js"
9
+ export * from "./protocol.js"
10
+
11
+ const tag = z.literal(VIEW_MESSAGE_TAG)
12
+
13
+ export const viewToHostSchema = z.discriminatedUnion("type", [
14
+ z.object({ tag, type: z.literal("ready") }),
15
+ z.object({ tag, type: z.literal("error"), message: z.string() }),
16
+ z.object({
17
+ tag,
18
+ type: z.literal("request"),
19
+ id: z.number().int(),
20
+ method: z.string(),
21
+ args: z.unknown(),
22
+ }),
23
+ ])
package/views/human.ts ADDED
@@ -0,0 +1,91 @@
1
+ import type { ReactNode } from "react"
2
+ import type { JsonSchema } from "../_schemas/node.js"
3
+
4
+ // human ノードの実装(nodes/human/<dir>/index.tsx)の約束(`@mawaru/sdk/human`)。承認画面(ビュー)は React の
5
+ // コンポーネント1つで、承認画面の上の帯より下の全部(書類と承認バー)を描く。右上の閉じるボタン・差し戻し後の帯・
6
+ // 差し戻しと追加の出口のダイアログ・認可・decision の POST・Broadcast の購読はホスト(mawaru の承認画面)に残る
7
+ // (docs/tasks/wip/承認ビューの顧客repo配置.md の「承認バーもビューが描く」)。
8
+ //
9
+ // ctx に入るのは JSON で表現できる値だけ(React 要素・クラスインスタンス・ホストの関数は無い)。
10
+ // 顧客 repo のビューは sandbox iframe の中で動き、ctx は postMessage で届く。
11
+ // ホストに頼みたいこと(ファイルの URL・アップロード・コピー)は useViewBridge()、承認の操作は useApprovalActions() から
12
+ //
13
+ // TIn は実装が宣言(config.json の inputs.in)した形。宣言と型引数が食い違ってもコンパイルは通るので、
14
+ // **一致はビュー実装の責任**(typegen の types.d.ts を使うと揃う)
15
+
16
+ /** 承認画面の局面:承認待ち/承認済み/差し戻し後(AI 再実行待ち)/失敗/キャンセル */
17
+ export type ApprovalPhase =
18
+ | "waiting"
19
+ | "approved"
20
+ | "rejected"
21
+ | "failed"
22
+ | "cancelled"
23
+
24
+ /**
25
+ * 承認バーの材料。部品の `ApprovalBar` はこれをそのまま使う。承認バーを自分で書くときに読む。
26
+ */
27
+ export type ApprovalDecisionContext = {
28
+ /** 見ている人が今決定できる(担当で承認待ち)。全員の承認が要るノードでも true(値を直せるかは `editable`) */
29
+ canDecide: boolean
30
+ /** 承認したあとに流れる先のノード名(つながっていなければ null) */
31
+ approveTargetName: string | null
32
+ /** 差し戻し先のノード名(無ければ差し戻せない) */
33
+ rejectTargetName: string | null
34
+ /** 承認・差し戻し以外の出口。`needsInput` は、今の値の型が出口と合わず、値を入れるダイアログが要るもの */
35
+ exits: { key: string; name: string; needsInput: boolean }[]
36
+ /** 担当でない人が開いたときの、担当の名前(担当なら null) */
37
+ otherAssignees: string[] | null
38
+ /** 全員の承認が要るときの進み具合(`approvedByMe` は自分がもう承認したか)。要らなければ null */
39
+ progress: { done: number; total: number; approvedByMe: boolean } | null
40
+ /** 失敗した step の理由(失敗していなければ null) */
41
+ failedReason: string | null
42
+ }
43
+
44
+ /** mawaru から承認ビューへ渡す材料 */
45
+ export type ApprovalViewContext<TIn = unknown> = {
46
+ /** 届いた値(step の input そのもの)。ビューで直した値はビューの state に持つ */
47
+ output: TIn
48
+ /** `output` の型= human の in ポートの型(ビューの宣言が写っている)。`SchemaForm` に渡すとフォームになる */
49
+ outputSchema: JsonSchema | null
50
+ /** ループ変数(この step が始まった時点の写し)。予約キー `input` が run の起動入力 */
51
+ refs: unknown
52
+ /** この human ノードの名前 */
53
+ nodeName: string
54
+ /** 局面 */
55
+ phase: ApprovalPhase
56
+ /** 値を直してよい(承認待ちで、見ている人が担当。全員の承認が要るノードでは false) */
57
+ editable: boolean
58
+ /** 承認バーの材料 */
59
+ decision: ApprovalDecisionContext
60
+ }
61
+
62
+ /**
63
+ * 承認ビュー:ctx を受けて承認画面(上の帯より下の全部)を描く React コンポーネント。
64
+ * 承認バーは `<ApprovalBar ctx={ctx} />` を置く(置かなければ承認バーは出ない)。
65
+ */
66
+ // 引数を双変にする(メソッドの形で宣言する):どの TIn のビューも ApprovalView<unknown> として持てるようにするため
67
+ export type ApprovalView<TIn = unknown> = {
68
+ bivariance(props: { ctx: ApprovalViewContext<TIn> }): ReactNode
69
+ }["bivariance"]
70
+
71
+ /** 承認ビューを定義する。型を付けるためだけの関数(値はそのまま返す) */
72
+ export const defineApprovalView = <TIn = unknown>(
73
+ view: ApprovalView<TIn>,
74
+ ): ApprovalView<TIn> => view
75
+
76
+ /** 手元プレビュー(npx mawaru preview human/<dir>)のケース。nodes/human/<dir>/cases.ts の export default */
77
+ export type ApprovalViewCase = {
78
+ name: string
79
+ /** `ctx.output`(= step の input) */
80
+ input: unknown
81
+ refs?: unknown
82
+ /** 省略時は name */
83
+ nodeName?: string
84
+ /** 省略時は承認待ち */
85
+ phase?: ApprovalPhase
86
+ }
87
+
88
+ export { ApprovalBar } from "./_approval/ApprovalBar.js"
89
+ export { type ApprovalActions, useApprovalActions } from "./_approvalActions.js"
90
+ // 部品キット(土台)もここから import できる
91
+ export * from "./index.js"
package/views/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ // ビューの部品キット(`@mawaru/sdk/views`)。種類(承認・…)に依らない土台:部品・ホストに頼む口。
2
+ // mawaru が顧客 repo のビューをまとめるとき、mawaru の今の版のこのキットを同梱する。
3
+ // **公開 API**:部品の props とホストに頼む口は後方互換でしか変えない
4
+ // (docs/tasks/wip/承認ビューの顧客repo配置.md の「互換の管理」)
5
+ export { useViewBridge, type ViewBridge } from "./_bridge.js"
6
+ export { documentCardStyle } from "./_parts/DocumentCard.js"
7
+ export { FilePreview } from "./_parts/FilePreview.js"
8
+ export { type FileRef, isFileRef } from "./_parts/fileRef.js"
9
+ export { isUrl, Linkify, linkify } from "./_parts/Linkify.js"
10
+ export { isLongText, Markdown, useAutoGrow } from "./_parts/Markdown.js"
11
+ export {
12
+ asFormSchema,
13
+ type JsonSchema as FormSchema,
14
+ SchemaForm,
15
+ } from "./_parts/SchemaForm.js"
16
+ export { NARROW_WIDTH, useNarrow } from "./_parts/useNarrow.js"
@@ -0,0 +1,40 @@
1
+ // sandbox iframe の中の実行部分(runtime.tsx)とホスト(mawaru の画面)とのメッセージの形。
2
+ // **公開 API ではない**:iframe の中の実行部分とホストは同じデプロイの mawaru のコード(まとめるときに
3
+ // その時点の版を同梱する)なので、いつ変えてもよい(docs/tasks/wip/承認ビューの顧客repo配置.md の「互換の管理」)。
4
+ // iframe に入る JS を小さく保つため、ここには型と定数だけを置く(ホストの検査は host.ts)
5
+
6
+ export const VIEW_MESSAGE_TAG = "mawaru:view"
7
+
8
+ export type ThemeMode = "light" | "dark"
9
+
10
+ // ビュー → ホスト
11
+ export type ViewToHost =
12
+ | { tag: typeof VIEW_MESSAGE_TAG; type: "ready" }
13
+ // 描画の例外(ホストは標準ビューに差し替える)
14
+ | { tag: typeof VIEW_MESSAGE_TAG; type: "error"; message: string }
15
+ // ホストに頼む(fileUrl / uploadFile / copyText、承認ビューは decide / openRejectDialog / openExitDialog / close も)
16
+ | {
17
+ tag: typeof VIEW_MESSAGE_TAG
18
+ type: "request"
19
+ id: number
20
+ method: string
21
+ args: unknown
22
+ }
23
+
24
+ // ホスト → ビュー
25
+ export type HostToView =
26
+ | { tag: typeof VIEW_MESSAGE_TAG; type: "ctx"; ctx: unknown; mode: ThemeMode }
27
+ | {
28
+ tag: typeof VIEW_MESSAGE_TAG
29
+ type: "response"
30
+ id: number
31
+ ok: true
32
+ value: unknown
33
+ }
34
+ | {
35
+ tag: typeof VIEW_MESSAGE_TAG
36
+ type: "response"
37
+ id: number
38
+ ok: false
39
+ error: string
40
+ }
@@ -0,0 +1,132 @@
1
+ import { Component, type ReactNode } from "react"
2
+ import { createRoot } from "react-dom/client"
3
+ import {
4
+ type ApprovalActions,
5
+ ApprovalActionsProvider,
6
+ } from "./_approvalActions.js"
7
+ import { ViewBridgeProvider } from "./_bridge.js"
8
+ import type { FileRef } from "./_parts/fileRef.js"
9
+ import type { ApprovalView, ApprovalViewContext } from "./human.js"
10
+ import {
11
+ type HostToView,
12
+ type ThemeMode,
13
+ VIEW_MESSAGE_TAG,
14
+ type ViewToHost,
15
+ } from "./protocol.js"
16
+
17
+ // sandbox iframe の中の実行部分(docs/tasks/wip/承認ビューの顧客repo配置.md の「隔離」)。
18
+ // mawaru が顧客 repo のビューをまとめるとき、入口からここの runXxxView を呼ぶ。
19
+ // ホストとのメッセージの形は protocol.ts(公開 API ではない)
20
+
21
+ // ---- iframe の中 ----
22
+
23
+ // union のまま tag だけを外す(Omit は union を潰す)
24
+ type WithoutTag<T> = T extends unknown ? Omit<T, "tag"> : never
25
+
26
+ const post = (message: WithoutTag<ViewToHost>) =>
27
+ // 受け手は親(ホスト)の window だけ。不透明オリジンの iframe からは親のオリジンを指定できない
28
+ window.parent.postMessage({ tag: VIEW_MESSAGE_TAG, ...message }, "*")
29
+
30
+ const messageOf = (err: unknown) =>
31
+ err instanceof Error ? err.message : String(err)
32
+
33
+ let nextRequestId = 1
34
+ const pending = new Map<
35
+ number,
36
+ { resolve: (value: unknown) => void; reject: (err: Error) => void }
37
+ >()
38
+ const ctxListeners = new Set<(ctx: unknown, mode: ThemeMode) => void>()
39
+
40
+ const onHostMessage = (event: MessageEvent) => {
41
+ if (event.source !== window.parent) return
42
+ const message = event.data as HostToView | undefined
43
+ if (message?.tag !== VIEW_MESSAGE_TAG) return
44
+ if (message.type === "response") {
45
+ const waiting = pending.get(message.id)
46
+ if (!waiting) return
47
+ pending.delete(message.id)
48
+ if (message.ok) waiting.resolve(message.value)
49
+ else waiting.reject(new Error(message.error))
50
+ return
51
+ }
52
+ if (message.type === "ctx") {
53
+ document.documentElement.dataset.mode = message.mode
54
+ for (const listener of ctxListeners) listener(message.ctx, message.mode)
55
+ }
56
+ }
57
+
58
+ // ホストに頼む(返事が来るまで待つ)
59
+ export const requestHost = <T,>(method: string, args: unknown): Promise<T> =>
60
+ new Promise<T>((resolve, reject) => {
61
+ const id = nextRequestId++
62
+ pending.set(id, {
63
+ resolve: resolve as (value: unknown) => void,
64
+ reject,
65
+ })
66
+ post({ type: "request", id, method, args })
67
+ })
68
+
69
+ const bridge = {
70
+ fileUrl: (file: FileRef) => requestHost<string>("fileUrl", { file }),
71
+ uploadFile: (file: File) => requestHost<FileRef>("uploadFile", { file }),
72
+ copyText: (text: string) => requestHost<void>("copyText", { text }),
73
+ }
74
+
75
+ // 描画の例外をホストへ知らせる(ホストは標準ビューに差し替える)
76
+ class ReportErrors extends Component<
77
+ { children: ReactNode },
78
+ { failed: boolean }
79
+ > {
80
+ state = { failed: false }
81
+ static getDerivedStateFromError() {
82
+ return { failed: true }
83
+ }
84
+ componentDidCatch(error: unknown) {
85
+ post({ type: "error", message: messageOf(error) })
86
+ }
87
+ render() {
88
+ return this.state.failed ? null : this.props.children
89
+ }
90
+ }
91
+
92
+ // iframe の中の共通の準備:ホストからのメッセージ・例外の報告。
93
+ // 最初の ctx が届いたら render を呼ぶ(以降の ctx も同じ render に渡す)
94
+ const startView = (render: (ctx: unknown) => ReactNode) => {
95
+ window.addEventListener("message", onHostMessage)
96
+ window.addEventListener("error", (event) =>
97
+ post({ type: "error", message: event.message }),
98
+ )
99
+ window.addEventListener("unhandledrejection", (event) =>
100
+ post({ type: "error", message: messageOf(event.reason) }),
101
+ )
102
+ const container = document.getElementById("root")
103
+ if (!container) throw new Error("#root がありません")
104
+ const root = createRoot(container)
105
+ ctxListeners.add((ctx) =>
106
+ root.render(
107
+ <ViewBridgeProvider bridge={bridge}>
108
+ <ReportErrors>{render(ctx)}</ReportErrors>
109
+ </ViewBridgeProvider>,
110
+ ),
111
+ )
112
+ post({ type: "ready" })
113
+ }
114
+
115
+ // ---- 承認ビュー ----
116
+
117
+ const actions: ApprovalActions = {
118
+ decide: (port, value) => requestHost<void>("decide", { port, value }),
119
+ openRejectDialog: () => requestHost<void>("openRejectDialog", {}),
120
+ openExitDialog: (key, value) =>
121
+ requestHost<void>("openExitDialog", { key, value }),
122
+ close: () => requestHost<void>("close", {}),
123
+ }
124
+
125
+ // ビューはコンポーネント1つ。状態(直した値・選択など)はビューが useState で持ち、ctx が変わっても
126
+ // (局面・担当の更新)同じコンポーネントとして描き直すので保たれる
127
+ export const runApprovalView = (View: ApprovalView) =>
128
+ startView((ctx) => (
129
+ <ApprovalActionsProvider value={actions}>
130
+ <View ctx={ctx as ApprovalViewContext} />
131
+ </ApprovalActionsProvider>
132
+ ))
@@ -0,0 +1,40 @@
1
+ import { describe, expect, it } from "vitest"
2
+ import {
3
+ decisionActionId,
4
+ decisionFormActionId,
5
+ parseDecisionActionId,
6
+ } from "./slack.js"
7
+
8
+ // 決定ボタンのプロトコル:ビューは action_id だけで「どの出口ポートへ流すか」を宣言し、
9
+ // ホストは押下を action_id から読み戻す。両者が同じ関数を使うので、往復で一致することを固定する
10
+
11
+ describe("決定ボタンの action_id", () => {
12
+ it("そのまま確定は decision:<port_key>", () => {
13
+ expect(decisionActionId("approve")).toBe("decision:approve")
14
+ expect(parseDecisionActionId("decision:approve")).toEqual({
15
+ port_key: "approve",
16
+ form: false,
17
+ })
18
+ })
19
+
20
+ it("フォームで確定は decision-form:<port_key>", () => {
21
+ expect(decisionFormActionId("reject")).toBe("decision-form:reject")
22
+ expect(parseDecisionActionId("decision-form:reject")).toEqual({
23
+ port_key: "reject",
24
+ form: true,
25
+ })
26
+ })
27
+
28
+ it("port_key に区切り文字を含んでも往復で一致する", () => {
29
+ expect(parseDecisionActionId(decisionActionId("a:b"))).toEqual({
30
+ port_key: "a:b",
31
+ form: false,
32
+ })
33
+ })
34
+
35
+ it("決定ボタン以外の action_id は null", () => {
36
+ expect(parseDecisionActionId("approve")).toBeNull()
37
+ expect(parseDecisionActionId(undefined)).toBeNull()
38
+ expect(parseDecisionActionId("other:approve")).toBeNull()
39
+ })
40
+ })
@@ -1,6 +1,6 @@
1
- import type { JsonSchema, NodeKind } from "./_schemas/node.js"
1
+ import type { JsonSchema, NodeKind } from "../_schemas/node.js"
2
2
 
3
- // Slack ビューの契約(`@mawaru/sdk/slack-view`)。ノードの Slack ビュー
3
+ // Slack ビューの契約(`@mawaru/sdk/views/slack`)。ノードの Slack ビュー
4
4
  // (nodes.slack_view_key)が実装する関数と、ホスト(mawaru)が渡す ctx の形の正。
5
5
  // mawaru 内蔵のビュー(backend の _helpers/slackViews/)と、顧客の実行 repo の
6
6
  // views/slack/<name>/ のビューが同じ契約で書く
@@ -43,7 +43,7 @@ export type SlackViewMaterial<TIn = unknown, TOut = unknown> = {
43
43
  }
44
44
 
45
45
  // 実行中メッセージの ctx(全 kind 共通)。TIn は in ポートの型
46
- // (human なら承認ビューの宣言が転写されたポートの型)
46
+ // (human なら実装(nodes/human/)の宣言が転写されたポートの型)
47
47
  export type SlackViewContext<TIn = unknown> = {
48
48
  // ノードの kind。決定ボタン(承認・差し戻し)は human のときだけ意味を持つので、
49
49
  // 複数の kind で使うビューはこれを見てボタンを描くか決める
File without changes