@onioneko/boardkit-blocks 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +49 -0
- package/dist/chart.d.ts +19 -0
- package/dist/chart.d.ts.map +1 -0
- package/dist/chart.js +24 -0
- package/dist/chart.js.map +1 -0
- package/dist/checklist.d.ts +24 -0
- package/dist/checklist.d.ts.map +1 -0
- package/dist/checklist.js +97 -0
- package/dist/checklist.js.map +1 -0
- package/dist/form.d.ts +26 -0
- package/dist/form.d.ts.map +1 -0
- package/dist/form.js +74 -0
- package/dist/form.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/metric.d.ts +22 -0
- package/dist/metric.d.ts.map +1 -0
- package/dist/metric.js +34 -0
- package/dist/metric.js.map +1 -0
- package/dist/rule.d.ts +19 -0
- package/dist/rule.d.ts.map +1 -0
- package/dist/rule.js +44 -0
- package/dist/rule.js.map +1 -0
- package/dist/status.d.ts +22 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +82 -0
- package/dist/status.js.map +1 -0
- package/package.json +64 -0
- package/src/chart.ts +43 -0
- package/src/checklist.ts +123 -0
- package/src/form.ts +101 -0
- package/src/index.ts +45 -0
- package/src/metric.ts +58 -0
- package/src/rule.ts +64 -0
- package/src/status.ts +103 -0
package/dist/status.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export const statusBlock = {
|
|
2
|
+
type: "status",
|
|
3
|
+
schema: {
|
|
4
|
+
type: "object",
|
|
5
|
+
required: ["id", "states", "value"],
|
|
6
|
+
properties: {
|
|
7
|
+
id: { type: "string" },
|
|
8
|
+
title: { type: "string" },
|
|
9
|
+
states: { type: "array", items: { type: "string" }, minItems: 1 },
|
|
10
|
+
value: { type: "string" },
|
|
11
|
+
note: { type: "string" },
|
|
12
|
+
},
|
|
13
|
+
additionalProperties: false,
|
|
14
|
+
},
|
|
15
|
+
// Declared `attrs: unknown` on purpose, wider than `Readonly<StatusAttrs>`
|
|
16
|
+
// (a function accepting `unknown` is assignable wherever a narrower-param
|
|
17
|
+
// function is expected, so this still satisfies `BlockType<StatusAttrs>`).
|
|
18
|
+
// `validate` is called directly by hosts and tests with arbitrary/possibly
|
|
19
|
+
// malformed data (its own doc contract says "never throws" — see
|
|
20
|
+
// BlockType.validate in core), so the null/non-object case is guarded
|
|
21
|
+
// before any property access, not just trusted from the generic.
|
|
22
|
+
validate: (attrs) => {
|
|
23
|
+
if (typeof attrs !== "object" || attrs === null)
|
|
24
|
+
return [];
|
|
25
|
+
const { value, states } = attrs;
|
|
26
|
+
if (Array.isArray(states) && typeof value === "string" && !states.includes(value)) {
|
|
27
|
+
return [{ code: "E_STATUS_VALUE", message: `value "${value}" is not in states` }];
|
|
28
|
+
}
|
|
29
|
+
return [];
|
|
30
|
+
},
|
|
31
|
+
transitions: [{ attr: "value", event: "status.changed" }],
|
|
32
|
+
affordances: [
|
|
33
|
+
{
|
|
34
|
+
name: "transition",
|
|
35
|
+
params: {
|
|
36
|
+
type: "object",
|
|
37
|
+
required: ["to"],
|
|
38
|
+
properties: { to: { type: "string" } },
|
|
39
|
+
additionalProperties: false,
|
|
40
|
+
},
|
|
41
|
+
patch: (_attrs, params) => ({ value: params.to }),
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
project: {
|
|
45
|
+
text: (attrs) => {
|
|
46
|
+
const value = String(attrs.value);
|
|
47
|
+
const title = String(attrs.title ?? "").trim();
|
|
48
|
+
return title === "" ? `**STATUS**: ${value}` : `**STATUS**: ${value} — ${title}`;
|
|
49
|
+
},
|
|
50
|
+
html: (attrs) => {
|
|
51
|
+
const states = attrs.states;
|
|
52
|
+
const value = String(attrs.value);
|
|
53
|
+
const buttons = states
|
|
54
|
+
.filter((state) => state !== value)
|
|
55
|
+
.map((state) => ({
|
|
56
|
+
type: "element",
|
|
57
|
+
tagName: "button",
|
|
58
|
+
properties: {
|
|
59
|
+
"data-intent": JSON.stringify({ affordance: "transition", params: { to: state } }),
|
|
60
|
+
},
|
|
61
|
+
children: [{ type: "text", value: state }],
|
|
62
|
+
}));
|
|
63
|
+
return {
|
|
64
|
+
type: "element",
|
|
65
|
+
tagName: "div",
|
|
66
|
+
// The state is data, not styling: one attribute holds it whatever it
|
|
67
|
+
// contains, and the class list stays fixed.
|
|
68
|
+
properties: { className: ["status"], "data-state": value },
|
|
69
|
+
children: [
|
|
70
|
+
{
|
|
71
|
+
type: "element",
|
|
72
|
+
tagName: "span",
|
|
73
|
+
properties: { className: ["badge"] },
|
|
74
|
+
children: [{ type: "text", value }],
|
|
75
|
+
},
|
|
76
|
+
...buttons,
|
|
77
|
+
],
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAuBA,MAAM,CAAC,MAAM,WAAW,GAA2B;IACjD,IAAI,EAAE,QAAQ;IACd,MAAM,EAAE;QACN,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC;QACnC,UAAU,EAAE;YACV,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACtB,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE;YACjE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;SACzB;QACD,oBAAoB,EAAE,KAAK;KAC5B;IACD,2EAA2E;IAC3E,0EAA0E;IAC1E,2EAA2E;IAC3E,2EAA2E;IAC3E,iEAAiE;IACjE,sEAAsE;IACtE,iEAAiE;IACjE,QAAQ,EAAE,CAAC,KAAc,EAAE,EAAE;QAC3B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,EAAE,CAAC;QAC3D,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,KAA8C,CAAC;QACzE,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAClF,OAAO,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,UAAU,KAAK,oBAAoB,EAAE,CAAC,CAAC;QACpF,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,WAAW,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC;IACzD,WAAW,EAAE;QACX;YACE,IAAI,EAAE,YAAY;YAClB,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,QAAQ,EAAE,CAAC,IAAI,CAAC;gBAChB,UAAU,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;gBACtC,oBAAoB,EAAE,KAAK;aAC5B;YACD,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAG,MAAyB,CAAC,EAAE,EAAE,CAAC;SACtE;KACF;IACD,OAAO,EAAE;QACP,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE;YACd,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAClC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YAC/C,OAAO,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,eAAe,KAAK,EAAE,CAAC,CAAC,CAAC,eAAe,KAAK,MAAM,KAAK,EAAE,CAAC;QACnF,CAAC;QACD,IAAI,EAAE,CAAC,KAAK,EAAwB,EAAE;YACpC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;YAC5B,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAClC,MAAM,OAAO,GAAoC,MAAM;iBACpD,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,KAAK,CAAC;iBAClC,GAAG,CAAC,CAAC,KAAK,EAAiC,EAAE,CAAC,CAAC;gBAC9C,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,QAAQ;gBACjB,UAAU,EAAE;oBACV,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC;iBACnF;gBACD,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;aAC3C,CAAC,CAAC,CAAC;YACN,OAAO;gBACL,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,KAAK;gBACd,qEAAqE;gBACrE,4CAA4C;gBAC5C,UAAU,EAAE,EAAE,SAAS,EAAE,CAAC,QAAQ,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE;gBAC1D,QAAQ,EAAE;oBACR;wBACE,IAAI,EAAE,SAAS;wBACf,OAAO,EAAE,MAAM;wBACf,UAAU,EAAE,EAAE,SAAS,EAAE,CAAC,OAAO,CAAC,EAAE;wBACpC,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;qBACpC;oBACD,GAAG,OAAO;iBACX;aACF,CAAC;QACJ,CAAC;KACF;CACF,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@onioneko/boardkit-blocks",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "BoardKit's reference block pack: checklist, status, metric, chart, form and rule block types for the BoardKit engine.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"markdown",
|
|
7
|
+
"board",
|
|
8
|
+
"blocks",
|
|
9
|
+
"checklist",
|
|
10
|
+
"agents",
|
|
11
|
+
"boardkit"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/onioneko/boardkit/tree/main/packages/blocks#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/onioneko/boardkit/issues"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/onioneko/boardkit.git",
|
|
20
|
+
"directory": "packages/blocks"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"author": {
|
|
24
|
+
"name": "onioneko",
|
|
25
|
+
"url": "https://github.com/onioneko"
|
|
26
|
+
},
|
|
27
|
+
"type": "module",
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"import": "./dist/index.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist",
|
|
37
|
+
"src",
|
|
38
|
+
"!src/**/*.test.ts",
|
|
39
|
+
"!src/**/__fixtures__"
|
|
40
|
+
],
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=22"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"@onioneko/boardkit-core": ">=0.1.0 <1.0.0"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/hast": "^3.0.0",
|
|
53
|
+
"hast-util-from-html": "^2.0.0",
|
|
54
|
+
"typescript": "^5.8.0",
|
|
55
|
+
"vitest": "^3.0.0",
|
|
56
|
+
"@onioneko/boardkit-core": "^0.1.0",
|
|
57
|
+
"@onioneko/boardkit-html": "^0.1.0"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "tsc -p tsconfig.build.json",
|
|
61
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
62
|
+
"test": "vitest run"
|
|
63
|
+
}
|
|
64
|
+
}
|
package/src/chart.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { BlockType } from "@onioneko/boardkit-core";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* starter block: chart — an opaque chart source. The text hook emits the spec
|
|
5
|
+
* verbatim (machine-readable); the html hook falls back to a `<pre>` because
|
|
6
|
+
* the library does not know chart libraries — hosts register their own block
|
|
7
|
+
* type (or renderer) to replace this fallback.
|
|
8
|
+
*
|
|
9
|
+
* Usage: a chart body carries `{ id, type, source, spec }`; `spec` is the
|
|
10
|
+
* chart's own DSL and is rendered untouched.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** Attrs for the `chart` block, mirroring its JSON Schema. */
|
|
14
|
+
export type ChartAttrs = {
|
|
15
|
+
readonly id: string;
|
|
16
|
+
readonly type: string;
|
|
17
|
+
readonly source: string;
|
|
18
|
+
readonly spec: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const chartBlock: BlockType<ChartAttrs> = {
|
|
22
|
+
type: "chart",
|
|
23
|
+
schema: {
|
|
24
|
+
type: "object",
|
|
25
|
+
required: ["id", "type", "source", "spec"],
|
|
26
|
+
properties: {
|
|
27
|
+
id: { type: "string" },
|
|
28
|
+
type: { type: "string" },
|
|
29
|
+
source: { type: "string" },
|
|
30
|
+
spec: { type: "string" },
|
|
31
|
+
},
|
|
32
|
+
additionalProperties: false,
|
|
33
|
+
},
|
|
34
|
+
project: {
|
|
35
|
+
text: (attrs) => String(attrs.spec),
|
|
36
|
+
html: (attrs): import("hast").Nodes => ({
|
|
37
|
+
type: "element",
|
|
38
|
+
tagName: "pre",
|
|
39
|
+
properties: { className: ["chart"], "data-source": String(attrs.source) },
|
|
40
|
+
children: [{ type: "text", value: String(attrs.spec) }],
|
|
41
|
+
}),
|
|
42
|
+
},
|
|
43
|
+
};
|
package/src/checklist.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { BlockType } from "@onioneko/boardkit-core";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* starter block: checklist — a list of labeled checkbox items. Each item is
|
|
5
|
+
* `{ id, label, done }`; the `toggle` affordance flips one item's `done` flag.
|
|
6
|
+
*
|
|
7
|
+
* The item-toggle transition names events by direction: checking a box emits
|
|
8
|
+
* `checklist.item.done` (false→true), unchecking it emits
|
|
9
|
+
* `checklist.item.undone` (true→false); both carry the `from`/`to` booleans.
|
|
10
|
+
*
|
|
11
|
+
* Usage: include in `createEngine({ blocks })` and add `"checklist"` to the
|
|
12
|
+
* parse options' `blockTypes` set so checklist fences are parsed as blocks.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** Attrs for the `checklist` block, mirroring its JSON Schema. */
|
|
16
|
+
export type ChecklistAttrs = {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly title?: string;
|
|
19
|
+
readonly items: readonly {
|
|
20
|
+
readonly id: string;
|
|
21
|
+
readonly label: string;
|
|
22
|
+
readonly done: boolean;
|
|
23
|
+
}[];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const checklistBlock: BlockType<ChecklistAttrs> = {
|
|
27
|
+
type: "checklist",
|
|
28
|
+
schema: {
|
|
29
|
+
type: "object",
|
|
30
|
+
required: ["id", "items"],
|
|
31
|
+
properties: {
|
|
32
|
+
id: { type: "string" },
|
|
33
|
+
title: { type: "string" },
|
|
34
|
+
items: {
|
|
35
|
+
type: "array",
|
|
36
|
+
items: {
|
|
37
|
+
type: "object",
|
|
38
|
+
required: ["id", "label", "done"],
|
|
39
|
+
properties: {
|
|
40
|
+
id: { type: "string" },
|
|
41
|
+
label: { type: "string" },
|
|
42
|
+
done: { type: "boolean" },
|
|
43
|
+
},
|
|
44
|
+
additionalProperties: false,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
additionalProperties: false,
|
|
49
|
+
},
|
|
50
|
+
transitions: [
|
|
51
|
+
{
|
|
52
|
+
attr: "items[].done",
|
|
53
|
+
events: {
|
|
54
|
+
"false→true": "checklist.item.done",
|
|
55
|
+
"true→false": "checklist.item.undone",
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
affordances: [
|
|
60
|
+
{
|
|
61
|
+
name: "toggle",
|
|
62
|
+
params: {
|
|
63
|
+
type: "object",
|
|
64
|
+
required: ["itemId"],
|
|
65
|
+
properties: { itemId: { type: "string" } },
|
|
66
|
+
additionalProperties: false,
|
|
67
|
+
},
|
|
68
|
+
patch: (attrs, params) => {
|
|
69
|
+
const itemId = (params as { itemId: string }).itemId;
|
|
70
|
+
const items = attrs.items.map((item) =>
|
|
71
|
+
item.id === itemId ? { ...item, done: !item.done } : item,
|
|
72
|
+
);
|
|
73
|
+
return { items };
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
project: {
|
|
78
|
+
text: (attrs) => {
|
|
79
|
+
const items = attrs.items;
|
|
80
|
+
return items.map((item) => `- [${item.done ? "x" : " "}] ${item.label}`).join("\n");
|
|
81
|
+
},
|
|
82
|
+
html: (attrs): import("hast").Nodes => {
|
|
83
|
+
const items = attrs.items;
|
|
84
|
+
return {
|
|
85
|
+
type: "element",
|
|
86
|
+
tagName: "ul",
|
|
87
|
+
properties: { className: ["checklist"] },
|
|
88
|
+
// Each item's input and label text are wrapped in a `<label>` so a
|
|
89
|
+
// click anywhere on the item — not just the tiny box — toggles it
|
|
90
|
+
// natively; `data-intent` stays on the `<input>` itself (the existing
|
|
91
|
+
// contract the page's `closest('[data-intent]')` handler reads).
|
|
92
|
+
children: items.map((item) => ({
|
|
93
|
+
type: "element",
|
|
94
|
+
tagName: "li",
|
|
95
|
+
properties: {},
|
|
96
|
+
children: [
|
|
97
|
+
{
|
|
98
|
+
type: "element",
|
|
99
|
+
tagName: "label",
|
|
100
|
+
properties: {},
|
|
101
|
+
children: [
|
|
102
|
+
{
|
|
103
|
+
type: "element",
|
|
104
|
+
tagName: "input",
|
|
105
|
+
properties: {
|
|
106
|
+
type: "checkbox",
|
|
107
|
+
...(item.done ? { checked: true } : {}),
|
|
108
|
+
"data-intent": JSON.stringify({
|
|
109
|
+
affordance: "toggle",
|
|
110
|
+
params: { itemId: item.id },
|
|
111
|
+
}),
|
|
112
|
+
},
|
|
113
|
+
children: [],
|
|
114
|
+
},
|
|
115
|
+
{ type: "text", value: item.label },
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
})),
|
|
120
|
+
};
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
};
|
package/src/form.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { BlockType } from "@onioneko/boardkit-core";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* starter block: form — a question with typed fields. Answering is the `answer`
|
|
5
|
+
* affordance; the view adapter assembles entered values into the intent params
|
|
6
|
+
* at interaction time (outside the block's concern).
|
|
7
|
+
*
|
|
8
|
+
* Usage: a form has `fields` (each with `id`, `label`, `type`, and optional
|
|
9
|
+
* `options`) and a `status` of `open` or `answered`. The `answer` affordance
|
|
10
|
+
* stores the assembled values in `answer`, flips `status` to `answered`, and
|
|
11
|
+
* emits `form.answered`.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** Attrs for the `form` block, mirroring its JSON Schema. */
|
|
15
|
+
export type FormAttrs = {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly title?: string;
|
|
18
|
+
readonly fields: readonly {
|
|
19
|
+
readonly id: string;
|
|
20
|
+
readonly label: string;
|
|
21
|
+
readonly type: "text" | "select" | "number";
|
|
22
|
+
readonly options?: readonly string[];
|
|
23
|
+
}[];
|
|
24
|
+
readonly status: "open" | "answered";
|
|
25
|
+
readonly answer?: Record<string, unknown>;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const formBlock: BlockType<FormAttrs> = {
|
|
29
|
+
type: "form",
|
|
30
|
+
schema: {
|
|
31
|
+
type: "object",
|
|
32
|
+
required: ["id", "fields", "status"],
|
|
33
|
+
properties: {
|
|
34
|
+
id: { type: "string" },
|
|
35
|
+
title: { type: "string" },
|
|
36
|
+
fields: {
|
|
37
|
+
type: "array",
|
|
38
|
+
items: {
|
|
39
|
+
type: "object",
|
|
40
|
+
required: ["id", "label", "type"],
|
|
41
|
+
properties: {
|
|
42
|
+
id: { type: "string" },
|
|
43
|
+
label: { type: "string" },
|
|
44
|
+
type: { enum: ["text", "select", "number"] },
|
|
45
|
+
options: { type: "array", items: { type: "string" } },
|
|
46
|
+
},
|
|
47
|
+
additionalProperties: false,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
status: { enum: ["open", "answered"] },
|
|
51
|
+
answer: { type: "object" },
|
|
52
|
+
},
|
|
53
|
+
additionalProperties: false,
|
|
54
|
+
},
|
|
55
|
+
transitions: [{ attr: "status", event: "form.answered" }],
|
|
56
|
+
affordances: [
|
|
57
|
+
{
|
|
58
|
+
name: "answer",
|
|
59
|
+
params: { type: "object" },
|
|
60
|
+
patch: (_attrs, params) => ({
|
|
61
|
+
answer: params as Record<string, unknown>,
|
|
62
|
+
status: "answered",
|
|
63
|
+
}),
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
project: {
|
|
67
|
+
text: (attrs) => {
|
|
68
|
+
const fields = attrs.fields;
|
|
69
|
+
const lines = fields.map((field) => {
|
|
70
|
+
const suffix =
|
|
71
|
+
field.options !== undefined && field.options.length > 0
|
|
72
|
+
? ` (${field.options.join("/")})`
|
|
73
|
+
: "";
|
|
74
|
+
return `- ${field.label}${suffix}`;
|
|
75
|
+
});
|
|
76
|
+
return lines.join("\n");
|
|
77
|
+
},
|
|
78
|
+
html: (attrs): import("hast").Nodes => {
|
|
79
|
+
const fields = attrs.fields;
|
|
80
|
+
return {
|
|
81
|
+
type: "element",
|
|
82
|
+
tagName: "div",
|
|
83
|
+
properties: { className: ["form"] },
|
|
84
|
+
children: fields.map((field) => ({
|
|
85
|
+
type: "element",
|
|
86
|
+
tagName: "label",
|
|
87
|
+
properties: {},
|
|
88
|
+
children: [
|
|
89
|
+
{ type: "text", value: field.label },
|
|
90
|
+
{
|
|
91
|
+
type: "element",
|
|
92
|
+
tagName: "input",
|
|
93
|
+
properties: { type: field.type === "number" ? "number" : "text", name: field.id },
|
|
94
|
+
children: [],
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
})),
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The starter block pack: six ready-made block types, exported together for
|
|
3
|
+
* convenience. Hosts that do not want every block may build their own array
|
|
4
|
+
* from the individual exports.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export type { ChartAttrs } from "./chart.js";
|
|
8
|
+
export { chartBlock } from "./chart.js";
|
|
9
|
+
export type { ChecklistAttrs } from "./checklist.js";
|
|
10
|
+
export { checklistBlock } from "./checklist.js";
|
|
11
|
+
export type { FormAttrs } from "./form.js";
|
|
12
|
+
export { formBlock } from "./form.js";
|
|
13
|
+
export type { MetricAttrs } from "./metric.js";
|
|
14
|
+
export { metricBlock } from "./metric.js";
|
|
15
|
+
export type { RuleAttrs } from "./rule.js";
|
|
16
|
+
export { ruleBlock } from "./rule.js";
|
|
17
|
+
export type { StatusAttrs } from "./status.js";
|
|
18
|
+
export { statusBlock } from "./status.js";
|
|
19
|
+
|
|
20
|
+
import type { AnyBlockType } from "@onioneko/boardkit-core";
|
|
21
|
+
import { chartBlock } from "./chart.js";
|
|
22
|
+
import { checklistBlock } from "./checklist.js";
|
|
23
|
+
import { formBlock } from "./form.js";
|
|
24
|
+
import { metricBlock } from "./metric.js";
|
|
25
|
+
import { ruleBlock } from "./rule.js";
|
|
26
|
+
import { statusBlock } from "./status.js";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* All six starter blocks, ready to pass to `createEngine({ blocks })`.
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* const engine = createEngine({
|
|
33
|
+
* storage: createMemStorage(),
|
|
34
|
+
* blocks: starterBlocks,
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export const starterBlocks: readonly AnyBlockType[] = [
|
|
39
|
+
checklistBlock,
|
|
40
|
+
statusBlock,
|
|
41
|
+
metricBlock,
|
|
42
|
+
chartBlock,
|
|
43
|
+
formBlock,
|
|
44
|
+
ruleBlock,
|
|
45
|
+
];
|
package/src/metric.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { BlockType } from "@onioneko/boardkit-core";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* starter block: metric — a labeled live value backed by a source. Hooks
|
|
5
|
+
* receive resolved values keyed by source id, so the metric's value is injected
|
|
6
|
+
* at projection time and never persisted.
|
|
7
|
+
*
|
|
8
|
+
* Usage: a metric's `source` field names a source id; the block declares it
|
|
9
|
+
* through `sources`, so the value is resolved from the block alone — the
|
|
10
|
+
* document needs no `{{source:…}}` prose ref. The resolved value (optionally
|
|
11
|
+
* annotated by `view`/`thresholds`) is rendered next to its `label`; an
|
|
12
|
+
* unresolved (absent or stale) value falls back to the source id.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** Attrs for the `metric` block, mirroring its JSON Schema. */
|
|
16
|
+
export type MetricAttrs = {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly label: string;
|
|
19
|
+
readonly source: string;
|
|
20
|
+
readonly view?: "value" | "sparkline" | "chart";
|
|
21
|
+
readonly thresholds?: readonly number[];
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const metricBlock: BlockType<MetricAttrs> = {
|
|
25
|
+
type: "metric",
|
|
26
|
+
schema: {
|
|
27
|
+
type: "object",
|
|
28
|
+
required: ["id", "label", "source"],
|
|
29
|
+
properties: {
|
|
30
|
+
id: { type: "string" },
|
|
31
|
+
label: { type: "string" },
|
|
32
|
+
source: { type: "string" },
|
|
33
|
+
view: { enum: ["value", "sparkline", "chart"] },
|
|
34
|
+
thresholds: { type: "array", items: { type: "number" } },
|
|
35
|
+
},
|
|
36
|
+
additionalProperties: false,
|
|
37
|
+
},
|
|
38
|
+
sources: (attrs) =>
|
|
39
|
+
typeof attrs.source === "string" ? [{ kind: "source", source: attrs.source, params: {} }] : [],
|
|
40
|
+
project: {
|
|
41
|
+
text: (attrs, values) =>
|
|
42
|
+
`${String(attrs.label)}: ${values[String(attrs.source)] ?? String(attrs.source)}`,
|
|
43
|
+
html: (attrs, values): import("hast").Nodes => ({
|
|
44
|
+
type: "element",
|
|
45
|
+
tagName: "span",
|
|
46
|
+
properties: { className: ["metric"] },
|
|
47
|
+
children: [
|
|
48
|
+
{
|
|
49
|
+
type: "element",
|
|
50
|
+
tagName: "strong",
|
|
51
|
+
properties: {},
|
|
52
|
+
children: [{ type: "text", value: String(attrs.label) }],
|
|
53
|
+
},
|
|
54
|
+
{ type: "text", value: ` ${values[String(attrs.source)] ?? String(attrs.source)}` },
|
|
55
|
+
],
|
|
56
|
+
}),
|
|
57
|
+
},
|
|
58
|
+
};
|
package/src/rule.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { BlockType } from "@onioneko/boardkit-core";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* starter block: rule — an opaque when/do automation with an enabled switch.
|
|
5
|
+
* The library validates only presence and type; the semantics of `when`/`do`
|
|
6
|
+
* belong to the host.
|
|
7
|
+
*
|
|
8
|
+
* Usage: the `toggle` affordance flips `enabled`, emitting `rule.enabled`; the
|
|
9
|
+
* host observes that event (or reads the block) to execute the rule.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Attrs for the `rule` block, mirroring its JSON Schema. */
|
|
13
|
+
export type RuleAttrs = {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
readonly title?: string;
|
|
16
|
+
readonly when: string;
|
|
17
|
+
readonly do: string;
|
|
18
|
+
readonly enabled: boolean;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const ruleBlock: BlockType<RuleAttrs> = {
|
|
22
|
+
type: "rule",
|
|
23
|
+
schema: {
|
|
24
|
+
type: "object",
|
|
25
|
+
required: ["id", "when", "do", "enabled"],
|
|
26
|
+
properties: {
|
|
27
|
+
id: { type: "string" },
|
|
28
|
+
title: { type: "string" },
|
|
29
|
+
when: { type: "string" },
|
|
30
|
+
do: { type: "string" },
|
|
31
|
+
enabled: { type: "boolean" },
|
|
32
|
+
},
|
|
33
|
+
additionalProperties: false,
|
|
34
|
+
},
|
|
35
|
+
transitions: [{ attr: "enabled", event: "rule.enabled" }],
|
|
36
|
+
affordances: [
|
|
37
|
+
{
|
|
38
|
+
name: "toggle",
|
|
39
|
+
patch: (attrs) => ({ enabled: !attrs.enabled }),
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
project: {
|
|
43
|
+
text: (attrs) =>
|
|
44
|
+
`- rule ${attrs.enabled === true ? "(enabled)" : "(disabled)"}: when ${String(attrs.when)} do ${String(attrs.do)}`,
|
|
45
|
+
html: (attrs): import("hast").Nodes => ({
|
|
46
|
+
type: "element",
|
|
47
|
+
tagName: "div",
|
|
48
|
+
properties: { className: ["rule"] },
|
|
49
|
+
children: [
|
|
50
|
+
{
|
|
51
|
+
type: "element",
|
|
52
|
+
tagName: "input",
|
|
53
|
+
properties: {
|
|
54
|
+
type: "checkbox",
|
|
55
|
+
...(attrs.enabled === true ? { checked: "" } : {}),
|
|
56
|
+
"data-intent": JSON.stringify({ affordance: "toggle" }),
|
|
57
|
+
},
|
|
58
|
+
children: [],
|
|
59
|
+
},
|
|
60
|
+
{ type: "text", value: ` when ${String(attrs.when)} do ${String(attrs.do)}` },
|
|
61
|
+
],
|
|
62
|
+
}),
|
|
63
|
+
},
|
|
64
|
+
};
|