@altnovatrail/spec 0.4.0 → 0.5.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/README.md CHANGED
@@ -8,6 +8,8 @@ The types of [NovaTrail](https://github.com/altnovatrail)'s module format for Ty
8
8
 
9
9
  They are generated from the format's schemas, [`format/schemas`](https://github.com/altnovatrail/spec/tree/main/format/schemas) in `altnovatrail/spec`, by its `tools/types`. Go code takes the same types from the Go module `github.com/altnovatrail/spec`, at the same tag.
10
10
 
11
+ From 0.5.0 on, the package also holds the types of the element `<novatrail-canvas>`: the contract between the canvas and the learner's app. These are written by hand rather than generated, and only TypeScript has them.
12
+
11
13
  The package holds declarations alone, with no code to run, so import them with `import type`:
12
14
 
13
15
  ```ts
@@ -26,6 +28,13 @@ function show(task: open.Task, manifest: open.Manifest) {
26
28
 
27
29
  A field the object always holds is required, and any other is optional. That includes each field that only some branches of a schema give.
28
30
  - **`@altnovatrail/spec/formatv0/open`** holds the open view of each part, which is what the learner's app gets ([the format's text](https://github.com/altnovatrail/spec/blob/main/format/README.md#6-open-fields), section 6). Each type keeps its name from `formatv0` and only the fields marked `x-open`, so an open view has no check, no template and no solver.
31
+ - **`@altnovatrail/spec/canvas`** holds the types of `<novatrail-canvas>`:
32
+ - `CanvasElement`, which describes the element's properties `tool`, `task`, `manifest`, `history` and `filled` and its method `exportWork()`;
33
+ - the events `toolchange` and `historychange`, with `CanvasEventMap` naming them;
34
+ - the history of writing;
35
+ - the work for checking, with the context of its submission.
36
+
37
+ Its unions are exact: an event of the history is told by its `kind`, and the work of a sheet or of a workbook by its key, `png` or `cells`. The declarations need the DOM's types, so the consumer's `lib` must include `dom`. The tutor's marks are not in it yet: their types come with the contract between the app and the platform's API. The package registers nothing. The canvas's own package puts the element in `HTMLElementTagNameMap` and its events in `GlobalEventHandlersEventMap`.
29
38
 
30
39
  A type gives the shape of a value and nothing more: the schemas decide what is valid. Code that reads a value from elsewhere checks it against its schema first.
31
40
 
@@ -44,6 +53,8 @@ npm pack @altnovatrail/spec@0.4.0
44
53
  gh release verify-asset v0.4.0 altnovatrail-spec-0.4.0.tgz -R altnovatrail/spec
45
54
  ```
46
55
 
56
+ The release publishes that file to npm itself, by trusted publishing, and npm gives each version it publishes that way a provenance attestation. The provenance names `.github/workflows/release.yml` of `altnovatrail/spec` and the commit of the run that published it. That commit is the tip of `main` when the run began, which is not always the commit the release was cut from. The release's attestation names the release's own commit, and `gh release verify-asset` is what ties the archive to it. 0.4.0 was published by hand and has no provenance. Not every release of the line has a version in npm, so take a tag whose version is there.
57
+
47
58
  ## License
48
59
 
49
60
  [Apache-2.0](https://github.com/altnovatrail/spec/blob/main/LICENSE).
@@ -0,0 +1,287 @@
1
+ // Written by hand, not generated: tools/types writes the folders of the module format's types
2
+ // beside this one, and leaves it as it is (R74).
3
+
4
+ /**
5
+ * The types of the element `<novatrail-canvas>`: the contract of canvas and app (R36). The element
6
+ * takes a task and its module's manifest, and the learner's history of writing, by properties; it
7
+ * gives the work for checking by a method, and tells what the learner did by events. The types of
8
+ * the task and of the manifest are the module format's open views, formatv0/open.
9
+ *
10
+ * The tutor's marks — the property `marks` and the types of a mark — wait for the contract of epic
11
+ * E3, and are canvas's own until then. The element's members beyond this contract, and its place in
12
+ * the DOM's maps of tags and events, are canvas's too: its package registers the element. The
13
+ * declarations take the DOM's types: Blob, CustomEvent and HTMLElement.
14
+ *
15
+ * @packageDocumentation
16
+ */
17
+
18
+ import type * as open from "../formatv0/open/index.js";
19
+
20
+ /**
21
+ * What a press of the primary button does on the sheet: the pen writes, the eraser erases. A task
22
+ * without the eraser has the pen alone.
23
+ */
24
+ export type Tool = "pen" | "eraser";
25
+
26
+ /** What a `toolchange` tells: the tool the learner chose with the element's own buttons. */
27
+ export interface ToolChange {
28
+ readonly tool: Tool;
29
+ }
30
+
31
+ /**
32
+ * The event the element sends from its host, bubbling and composed, when the learner changes the
33
+ * tool with its buttons — not when the consumer sets `tool`, nor when a new task puts the pen back.
34
+ * Its detail is frozen.
35
+ */
36
+ export type ToolChangeEvent = CustomEvent<ToolChange>;
37
+
38
+ /**
39
+ * A point of a stroke: where it lies on the sheet, in its logical units, when it was written and
40
+ * how hard the pointer pressed.
41
+ */
42
+ export interface InkPoint {
43
+ /** Across the sheet: 0 at its left edge, its width, 1000, at its right. */
44
+ readonly x: number;
45
+ /** Down the sheet: 0 at its top edge, its height, 1400, at its bottom. */
46
+ readonly y: number;
47
+ /** Milliseconds since the sheet's first event: the time of a sample has no date. */
48
+ readonly t: number;
49
+ /** The pressure, 0 to 1, as the pointer reported it. */
50
+ readonly p: number;
51
+ }
52
+
53
+ /** A stroke: one press of a pointer, from going down to coming up. */
54
+ export interface Stroke {
55
+ /**
56
+ * The stroke's ID on its sheet: 1, 2, … in the order the strokes began, never reused within one
57
+ * history. A sheet given another history numbers on from that history's highest ID.
58
+ */
59
+ readonly id: number;
60
+ /**
61
+ * The pointer's type as the browser reported it — "pen", "touch", "mouse" or whatever else it
62
+ * says. Only a pen's pressure changes the width of the line.
63
+ */
64
+ readonly kind: string;
65
+ /** The points in the order they were written, the first where the pointer went down. */
66
+ readonly points: readonly InkPoint[];
67
+ }
68
+
69
+ /**
70
+ * An event of the history of writing: what an action on the sheet did, at the sheet's time `t`
71
+ * when it landed, in the order the actions landed — so `t` never goes back along a history. Each
72
+ * event says which strokes it put on the sheet or took off, so whoever reads the history replays it
73
+ * without the rules of the stack of undo. The events are plain data, for JSON as they are; those
74
+ * the sheet hands out are frozen through. The eval protocol takes the history as it is (epic E3).
75
+ */
76
+ export type HistoryEvent =
77
+ /**
78
+ * A stroke written, and when it landed: its last point's time, or later, if it ended without its
79
+ * pointerup after an action that landed while it was under way.
80
+ */
81
+ | { readonly kind: "write"; readonly t: number; readonly stroke: Stroke }
82
+ /** One press of the eraser: the strokes it took off, in the sheet's order, when it lifted. */
83
+ | { readonly kind: "erase"; readonly t: number; readonly ids: readonly number[] }
84
+ /** The sheet cleared: every stroke that was on it, in the sheet's order. */
85
+ | { readonly kind: "clear"; readonly t: number; readonly ids: readonly number[] }
86
+ /**
87
+ * The last action still in force taken back: the stroke it took off, if it was a writing, or the
88
+ * strokes it brought back, if it was an erasing or a clearing.
89
+ */
90
+ | {
91
+ readonly kind: "undo";
92
+ readonly t: number;
93
+ readonly removed: readonly number[];
94
+ readonly restored: readonly number[];
95
+ };
96
+
97
+ /**
98
+ * What a `historychange` tells: the events the history of writing has gained, in its order, and
99
+ * where the first of them stands in it — `history` is what was there before, and these. A store
100
+ * whose length is not `from` has missed events, and reads `history` again.
101
+ */
102
+ export interface HistoryChange {
103
+ readonly events: readonly HistoryEvent[];
104
+ readonly from: number;
105
+ }
106
+
107
+ /**
108
+ * The event the element sends from its host, bubbling and composed, at once after every action on
109
+ * the sheet: a stroke, an erasing, a clearing and an undo. Its detail is frozen. Setting `history`
110
+ * sends none.
111
+ */
112
+ export type HistoryChangeEvent = CustomEvent<HistoryChange>;
113
+
114
+ /**
115
+ * An answer cell by its place, which has no ID: its item, and its place among the item's cells,
116
+ * each from 1. On a workbook a cell is a box.
117
+ */
118
+ export interface CellPlace {
119
+ readonly item: number;
120
+ readonly slot: number;
121
+ }
122
+
123
+ /**
124
+ * Which boxes of a workbook are filled: per item, per box, in the task's order, whether the box
125
+ * holds one of the strokes `exportWork()` would take.
126
+ */
127
+ export type Filled = readonly (readonly boolean[])[];
128
+
129
+ /** A filled box of a workbook and the IDs of its strokes, in ascending order. */
130
+ export interface Filling extends CellPlace {
131
+ readonly ids: readonly number[];
132
+ }
133
+
134
+ /** A rectangle on the sheet, as the whole numbers of a crop hold it: in tenths of a unit. */
135
+ export interface Box {
136
+ readonly minX: number;
137
+ readonly minY: number;
138
+ readonly maxX: number;
139
+ readonly maxY: number;
140
+ }
141
+
142
+ /**
143
+ * A group's frame on a crop: its label, and its ink in fractions 0–1000 of the crop — every pixel
144
+ * the ink of its strokes touches lies within it.
145
+ */
146
+ export interface Frame {
147
+ readonly label: string;
148
+ /** [ymin, xmin, ymax, xmax] in whole thousandths of the crop's height and width; min < max. */
149
+ readonly box: readonly [number, number, number, number];
150
+ }
151
+
152
+ /**
153
+ * A group of the strokes on a sheet: its label, by place, and the IDs of its strokes, in ascending
154
+ * order. The labels and the IDs are the table «label → stroke IDs» of one submission.
155
+ */
156
+ export interface Group {
157
+ readonly label: string;
158
+ readonly ids: readonly number[];
159
+ }
160
+
161
+ /**
162
+ * The numbers of a sheet's crop that the tutor's marks are placed by: where the crop lies on the
163
+ * sheet, the frames of the groups, and the whole numbers they come from.
164
+ */
165
+ export interface CropNumbers {
166
+ /**
167
+ * The height digits are drawn at, px: the height the manifest asks for, or less if the crop at it
168
+ * would be too large.
169
+ */
170
+ readonly drawn: number;
171
+ /** The width of the crop, px. */
172
+ readonly width: number;
173
+ /** The height of the crop, px. */
174
+ readonly height: number;
175
+ /** The margins above and to the left of the work, px; below and to the right is what is left. */
176
+ readonly margins: readonly [top: number, left: number];
177
+ /** Where the crop with its margins lies on the sheet: [ymin, xmin, ymax, xmax], in units. */
178
+ readonly sheetBox: readonly [number, number, number, number];
179
+ /** The frames of the groups, in the order of their labels. */
180
+ readonly frames: readonly Frame[];
181
+ /**
182
+ * The whole numbers all of it comes from, in tenths of a unit: the box of the points, and the
183
+ * median the scale takes.
184
+ */
185
+ readonly exact: { readonly box: Box; readonly median: number };
186
+ }
187
+
188
+ /**
189
+ * The context of one submission of a sheet, which the element gives with its crop and takes back
190
+ * with the tutor's marks: the crop's numbers, the groups of the strokes, and the digest of it all.
191
+ * app keeps it on the device as it is, through JSON, unrounded, and sends it nowhere.
192
+ */
193
+ export interface SheetSubmission {
194
+ readonly crop: CropNumbers;
195
+ readonly groups: readonly Group[];
196
+ /**
197
+ * What the submission was made of: 16 hex digits. It catches a context that went astray, not one
198
+ * forged.
199
+ */
200
+ readonly digest: string;
201
+ }
202
+
203
+ /**
204
+ * The context of one submission of a workbook: the table «box → stroke IDs», each filled box by its
205
+ * place, in the task's order, and the digest of its strokes, of every box of the page and of the
206
+ * table's rows. A context of another layout of the page the element does not take back. app keeps
207
+ * it on the device as it is, and sends it nowhere.
208
+ */
209
+ export interface WorkbookSubmission {
210
+ readonly boxes: readonly Filling[];
211
+ /** What the submission was made of: 16 hex digits. */
212
+ readonly digest: string;
213
+ }
214
+
215
+ /** The context of one submission, of a sheet or of a workbook: its keys tell which. */
216
+ export type Submission = SheetSubmission | WorkbookSubmission;
217
+
218
+ /** The work of a sheet for checking: the PNG of the crop, and the context of the submission. */
219
+ export interface ExportedSheet {
220
+ readonly png: Blob;
221
+ readonly submission: SheetSubmission;
222
+ }
223
+
224
+ /**
225
+ * The crop of one filled box of a workbook — by the box's strokes alone, ink only, at the height of
226
+ * a digit the manifest asks for — with its item and its slot.
227
+ */
228
+ export interface ExportedCell extends CellPlace {
229
+ readonly png: Blob;
230
+ }
231
+
232
+ /**
233
+ * The work of a workbook for checking: the crop of each filled box, in the task's order, and the
234
+ * context of the submission. The draft, the writing outside the boxes, goes nowhere.
235
+ */
236
+ export interface ExportedWorkbook {
237
+ readonly cells: readonly ExportedCell[];
238
+ readonly submission: WorkbookSubmission;
239
+ }
240
+
241
+ /** The work for checking, of a sheet or of a workbook: its key, `png` or `cells`, tells which. */
242
+ export type ExportedWork = ExportedSheet | ExportedWorkbook;
243
+
244
+ /** The events of the element, by their names, for a consumer's map of events. */
245
+ export interface CanvasEventMap {
246
+ historychange: HistoryChangeEvent;
247
+ toolchange: ToolChangeEvent;
248
+ }
249
+
250
+ /**
251
+ * The element `<novatrail-canvas>` as its contract with app has it. The language of its buttons is
252
+ * its own attribute `lang`.
253
+ */
254
+ export interface CanvasElement extends HTMLElement {
255
+ /**
256
+ * What a press of the primary button does. The learner switches it with the element's buttons,
257
+ * which a `toolchange` tells; the consumer may set it too, which tells nothing.
258
+ */
259
+ tool: Tool;
260
+ /** The open view of the task, given before the history and the marks. */
261
+ task: open.Task | undefined;
262
+ /**
263
+ * The open view of the task's module's manifest. The element reads its version of the module
264
+ * format, the height of a digit on the crop for checking, `canvas.digit_height`, and its
265
+ * explanation language. A canvas setting it does not know is a TypeError.
266
+ */
267
+ manifest: open.Manifest | undefined;
268
+ /**
269
+ * The history of writing: every stroke, erasing, clearing and undo on the sheet, frozen. The
270
+ * consumer keeps it, adding what `historychange` tells, and gives it back to open the sheet as it
271
+ * was. A history the sheet could not have written is a TypeError, and the sheet stays as it was;
272
+ * `undefined` is the empty one.
273
+ */
274
+ get history(): readonly HistoryEvent[];
275
+ set history(history: readonly HistoryEvent[] | undefined);
276
+ /**
277
+ * Which boxes of a workbook are filled, frozen; `null` on a sheet. An action on the sheet changes
278
+ * it before its `historychange`, and a task or a history given changes it with no event.
279
+ */
280
+ readonly filled: Filled | null;
281
+ /**
282
+ * The work for checking, as the sheet is when it is called: a sheet's crop or a workbook's boxes,
283
+ * with the context of the submission. A sheet without strokes, and a workbook with no box filled,
284
+ * give `null`.
285
+ */
286
+ exportWork(): Promise<ExportedWork | null>;
287
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@altnovatrail/spec",
3
- "version": "0.4.0",
4
- "description": "The types of NovaTrail's module format for TypeScript, generated from its schemas: the parts of a module and their open views, the canonical view, the inventory, the module index and the test vectors.",
3
+ "version": "0.5.0",
4
+ "description": "The types of NovaTrail's module format for TypeScript, generated from its schemas: the parts of a module and their open views, the canonical view, the inventory, the module index and the test vectors. And the types of the element <novatrail-canvas>, the contract of the canvas and the app.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
@@ -15,10 +15,14 @@
15
15
  "./formatv0/open": {
16
16
  "types": "./formatv0/open/index.d.ts"
17
17
  },
18
+ "./canvas": {
19
+ "types": "./canvas/index.d.ts"
20
+ },
18
21
  "./package.json": "./package.json"
19
22
  },
20
23
  "files": [
21
- "formatv0"
24
+ "formatv0",
25
+ "canvas"
22
26
  ],
23
27
  "sideEffects": false
24
28
  }