@protoboardai/ext-docs 0.0.0-stage → 0.2.1

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 (55) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +18 -0
  3. package/README.md +78 -2
  4. package/evals/build-datasheet/case.json +147 -0
  5. package/fonts/KodeMono-Bold.ttf +0 -0
  6. package/fonts/KodeMono-Medium.ttf +0 -0
  7. package/fonts/KodeMono-Regular.ttf +0 -0
  8. package/fonts/KodeMono-SemiBold.ttf +0 -0
  9. package/fonts/OFL.txt +93 -0
  10. package/lib/blocks.ts +268 -0
  11. package/lib/bom.ts +126 -0
  12. package/lib/build.ts +202 -0
  13. package/lib/charts.ts +241 -0
  14. package/lib/context.ts +122 -0
  15. package/lib/derived.ts +181 -0
  16. package/lib/docs/assembly.ts +674 -0
  17. package/lib/docs/common.ts +81 -0
  18. package/lib/docs/datasheet.ts +875 -0
  19. package/lib/docs/product.ts +45 -0
  20. package/lib/figures.ts +195 -0
  21. package/lib/format.ts +221 -0
  22. package/lib/html.ts +135 -0
  23. package/lib/model.ts +312 -0
  24. package/lib/package/assembly.ts +268 -0
  25. package/lib/package/common.ts +109 -0
  26. package/lib/package/plan.ts +432 -0
  27. package/lib/package/spec.ts +203 -0
  28. package/lib/png.ts +91 -0
  29. package/lib/render/cdp.ts +5 -0
  30. package/lib/render/page/render.html +17 -0
  31. package/lib/render/page/render.js +390 -0
  32. package/lib/render/renderer.ts +161 -0
  33. package/lib/render/server.ts +105 -0
  34. package/lib/standin.ts +36 -0
  35. package/lib/standins/multirotor.ts +283 -0
  36. package/lib/templates/doc.css +247 -0
  37. package/lib/templates/flow.js +131 -0
  38. package/lib/testdata.ts +85 -0
  39. package/lib/types.ts +120 -0
  40. package/lib/values.ts +309 -0
  41. package/lib/verify.ts +276 -0
  42. package/ops/docs.ts +52 -0
  43. package/ops/package.ts +9 -0
  44. package/package.json +62 -4
  45. package/protoboard-extension.json +94 -0
  46. package/skills/uhd-tech-docs/SKILL.md +234 -0
  47. package/skills/uhd-tech-docs/references/docspec.md +56 -0
  48. package/skills/uhd-tech-docs/references/house-style.md +50 -0
  49. package/skills/uhd-tech-docs/references/style-study.md +270 -0
  50. package/skills/uhd-tech-docs/references/test-data.md +49 -0
  51. package/skills/uhd-tech-docs/references/value-queries.md +67 -0
  52. package/skills/uhd-tech-docs/templates/test-data.example.json +33 -0
  53. package/skills/uhd-tech-docs/templates/test-data.schema.json +213 -0
  54. package/skills/uhd-tech-docs-verify/SKILL.md +104 -0
  55. package/skills/uhd-tech-docs-verify/references/visual-review.md +43 -0
@@ -0,0 +1,234 @@
1
+ ---
2
+ name: uhd-tech-docs
3
+ description: Produce presentation-grade technical documents (datasheet, assembly guide; later wiring guide, quick start, test report) for a Protoboard project's system with @protoboardai/ext-docs (protoboard run tech-docs), every value queried from the live UHD model, figures rendered from the CAD artifacts, stand-in test data watermarked until measured data replaces it. Use when asked for a datasheet, assembly/build guide, or other printed documentation of a system; follow with uhd-tech-docs-verify.
4
+ ---
5
+
6
+ # UHD technical documents
7
+
8
+ Generate HTML + PDF documents for the project's root system. The model is the only source of
9
+ values: the document is a *presentation* of queries into it. Nothing numeric is
10
+ typed into a document by hand.
11
+
12
+ ```
13
+ docs/docspec.ts ──► protoboard run tech-docs ──► docs/<id>-*.html, *.pdf, *.values.json, <id>-bom.json, images/
14
+ (presentation) │ model: the project's root system + definitions, assemble(), wiringChecklist, checkSystem
15
+ │ evidence: parts/evidence/<id>/, and the evidence a catalog revision vendored into parts/catalog/<id>/
16
+ │ test data: docs/test-data/<test-id>.json (standin | measured)
17
+ ▼
18
+ protoboard run tech-docs-verify (skill: uhd-tech-docs-verify) + visual review of page PNGs
19
+ ```
20
+
21
+ ## Purpose
22
+
23
+ Produce the datasheet and the assembly guide a person reads, built from the
24
+ model so they cannot disagree with it. Use it when asked for a datasheet,
25
+ assembly or build guide or other printed documentation of a system, and
26
+ whenever the model changes after documents were built (the verifier fails on
27
+ stale values). In **build-a-project** it is stage 10; the Manufacturing
28
+ Package's spec sheet and assembly guide sections come from the same
29
+ generator (**release**). **uhd-tech-docs-verify** follows.
30
+
31
+ ## Inputs
32
+
33
+ | Input | Where | From |
34
+ | --- | --- | --- |
35
+ | The system, clean | `npx protoboard check` with 0 errors | **system-compose** and the stages before |
36
+ | Placed CAD bodies | parts' body artifacts, designed modules' `out/` (`body.glb`, `board.glb`) | the providers, the parts |
37
+ | Part evidence | `parts/evidence/<id>/`, catalog evidence in `parts/catalog/<id>/` | the part skills, `parts add` |
38
+ | The product statement | `docs/product.json` (summary, features, functions, requirements; or the requirements table of `docs/brief.md`) | **system-compose** step 1. The datasheet's first page after the cover is built from it; what it leaves empty the page says is not stated. Never fill it with text the brief does not support |
39
+ | The doc spec (presentation only) | `docs/docspec.ts` (a `DocConfig`) | this SOP, step 2 |
40
+ | Test data | `docs/test-data/<test-id>.json` | stand-ins from the build; measured from **test-and-measure** |
41
+ | Tools | `npx protoboard doctor`: `chrome`, `poppler` ✓ | a Chromium-family browser, run headless with SwiftShader WebGL (Google Chrome, Chromium, Microsoft Edge or Brave, found where each platform installs them; `$CHROME` names another), poppler (`pdftoppm`, `pdfinfo`, `pdffonts`) |
42
+ | Fonts | Kode Mono (regular to bold) ships with ext-docs | nothing to install |
43
+
44
+ The project lists `@protoboardai/ext-docs` in `extensions`. three.js is a
45
+ dependency of the extension (`$PROTOBOARD_THREE_DIR` overrides it).
46
+
47
+ ## Steps
48
+
49
+ ### 1. Read the system
50
+
51
+ 1. Read the system: the root module (children, links, mates, harnesses), the hardware and
52
+ harness sources (`fastenerStack`), the scenarios:
53
+
54
+ ```bash
55
+ npx protoboard inspect --format json
56
+ npx protoboard doctor # chrome and poppler available?
57
+ ```
58
+
59
+ 2. Dump what the documents will draw on:
60
+ `assemble(root, lookup, { root })` — placements, mates, `hardware`, routes, unrouted, issues;
61
+ `wiringChecklist` (and sub-assemblies' links); `checkSystem`; each part's
62
+ `verification.json` / `sources.json`.
63
+ 3. Read [style-study](references/style-study.md) ("Rules we adopt") and
64
+ [house style](references/house-style.md).
65
+ 4. Note what the model **does not** say that the document type needs (torque, frame mass,
66
+ motor direction, thermal limits...). These become gap markers in the document and a list
67
+ for the model author — never invented values.
68
+
69
+ ### 2. Write the doc spec
70
+
71
+ `docs/docspec.ts` default-exports a `DocConfig` (`import type { DocConfig } from
72
+ "@protoboardai/ext-docs"`). It holds presentation only — see [docspec reference](references/docspec.md):
73
+
74
+ - `title`, `docId`, `revision`, `root` (assembly root), `camera` (house iso direction);
75
+ - `placementHints`: placements for strapped/free parts, from model constants (e.g. the frame's
76
+ `BATTERY_PAD`), never from typed coordinates;
77
+ - `datasheet.keyFigures` / `specGroups`: labels + value queries + format;
78
+ `mechanicalCallouts` (`<instance>.<interface>`), `hub` (module whose pinout is tabulated; a
79
+ board hub also gets its own architecture figure and its controller's pinout, and its rails
80
+ join the power-rails table);
81
+ - not a multirotor: `datasheet.envelope` (the Mechanical section's rows), `heroHints: true` (parts
82
+ held inside an enclosure on the cover), and `datasheet.performance` (`rows` of queries and bar
83
+ `charts` with target `refs`), with the figures stated as a `performance` trait on the system
84
+ (the watch states its battery life from its power budget);
85
+ - `assembly.steps`: ordered groups of mate ids / wiring link ids (wildcards), `place` for
86
+ unlinked parts, camera/explode per step, authored notes (may embed `{{query|format}}`), and
87
+ `actions` for work the model cannot generate (fitting printed bodies of a module, a fold).
88
+ A step with nothing in a release unit (no mate, link or placed part) is left out of that
89
+ unit's guide and work order: give a step that matters a mate (a fastener harness on the
90
+ joint it closes) or a part it places;
91
+ - `testData.standins`: the stand-in recipe (`lib/standins/<name>.ts` in the extension).
92
+
93
+ Anything the spec leaves out still appears: the builder appends automatic steps for uncovered
94
+ mates and links, and the verifier warns so you can order them.
95
+
96
+ ### 3. Values: queries, formats, markers
97
+
98
+ Every value goes through `doc.v(query, format)` and renders as
99
+ `<span class="v" data-q="…" data-f="…" data-s="status">`. The verifier re-resolves each one.
100
+ Query forms ([value-queries](references/value-queries.md)):
101
+
102
+ | Query | Example |
103
+ | --- | --- |
104
+ | `def:<moduleId>:<path>` | `def:cnhl-black-series-1100mah-6s-100c:domains[domain=mechanical].weight_g` |
105
+ | `inst:<instance path>:<path>` | `inst:stack/fc:interfaces[id=bec_5v].parameters[id=voltage].value` |
106
+ | `sys:<path>` | `sys:checks.diagnostics[rule=propulsion_current].details.totalPeak`, `sys:bom[line=3].quantity` |
107
+ | `derived:<name>` / `derived:<fn>(args)` | `derived:mass.all_up`, `derived:frameDistance(frame.motor_mount_fl,frame.motor_mount_fr)` |
108
+ | `ver:<partId>:<path>` | `ver:meps-neon-2207-v2-1950kv:audit.confirmed` |
109
+ | `test:<test-id>:<path>` | `test:hover-current:summary[id=hover_current_A].value` |
110
+
111
+ Formats (`lib/format.ts`): `d1` decimals, `s3` significant digits, `si` SI prefix,
112
+ `u=<unit>` unit override, `hex`, `pct`, `nounit`. Always value, space, unit (`22.2 V`);
113
+ ranges `3.7–13.2 V`; thousands grouped `30 342 rpm`.
114
+
115
+ Status markers (superscripts, legend auto-inserted): none = cited source or model design value;
116
+ **D** derived (formula listed in the provenance appendix); **A** the model marks it an
117
+ assumption; **S** stand-in data; **M** measured; **—** not in model (gap). Add a derivation in
118
+ `lib/derived.ts` (with its input queries) rather than computing in a builder.
119
+
120
+ ### 4. Figures from the CAD
121
+
122
+ `lib/render/` renders GLB artifacts placed by `assemble()` matrices in headless
123
+ Chrome (three.js), returning a PNG plus projected anchor points; callouts, leaders and
124
+ dimensions are drawn as vector SVG over the image (`blocks.ts: callouts, dimension`).
125
+
126
+ - **Modes:** `lineart` (flat light fills + screen-space outlines from object id, depth and
127
+ normal edges — assembly steps and drawings), `shaded` (PBR + soft outlines — covers).
128
+ - **Styles per body:** `normal` (fitted in this step), `context` (already assembled, grey),
129
+ `accent` (hardware / interface features to find, orange), `ghost`, `hidden`;
130
+ `accentNodes` highlights named GLB nodes (interface feature sub-shapes such as
131
+ `motor_mount_fr`, pad groups such as `motor_2_pads`).
132
+ - **Exploded views** (`figures.ts: explode`): mates sharing a structure interface form a joint;
133
+ every part on it (mounted modules at their mate gap, hardware at its `fastenerStack.atMm`) is
134
+ ranked by position along the structure normal and offset by rank × distance, so the explosion
135
+ order *is* the stacking order. Dashed accent axes run through each hole. Nested joints carry
136
+ their parent's offset.
137
+ - **Cameras:** orthographic, fitted to the projected geometry; one house iso direction held
138
+ across steps; top (`dir [0,0,1], up [1,0,0]`) and side views for drawings.
139
+ - Thumbnails of every part for the kit page and parts strips (`common.ts: thumbnail`).
140
+
141
+ A body whose CAD file is not in the checkout (vendor CAD whose redistribution
142
+ is not cleared, `PB_ARTIFACT_UNAVAILABLE`) is left out of the assembly
143
+ figures, shown as "CAD file not available" in the kit, never as a blank image.
144
+
145
+ ### 5. Test data and graphs
146
+
147
+ Convention: `docs/test-data/<test-id>.json`, one file per test, schema
148
+ [`templates/test-data.schema.json`](templates/test-data.schema.json), guide
149
+ [test-data](references/test-data.md). `status: "standin"` files are generated from the model by
150
+ the stand-in recipe (they list `provenance.derivedFrom` queries and every `assumption`) and are
151
+ drawn with a **STAND-IN DATA — NOT MEASURED** watermark and S markers. Drop in a file with
152
+ `status: "measured"` (same id, same columns) and the next build uses it unmarked; stand-in
153
+ generation never overwrites a measured file. Charts are inline SVG (`charts.ts`: line, bar,
154
+ meter) following the style rules; a chart records `data-test` and `data-status` for the verifier.
155
+
156
+ ### 6. Document types
157
+
158
+ | Type | Builder | Sections |
159
+ | --- | --- | --- |
160
+ | Datasheet | `lib/docs/datasheet.ts` | cover (shaded hero, key figures) · the product (what it is, features, functions, requirements with how each is shown, from `docs/product.json`) · overview + architecture diagram from links · specifications · mechanical (top/side drawings with dimensions, interface callouts + table with D1/D2/D3 binding) · interfaces and pinout (hub allocation pad to pad) · electrical (rails, supply budget, propulsion vs battery) · performance (maker data + test-data charts) · design checks per scenario · BOM (`docs/<id>-bom.json`) · evidence and assumptions · value provenance |
161
+ | Assembly guide | `lib/docs/assembly.ts` | cover (exploded) · before you begin (contents, tools from hardware tags, figure key, safety) · kit (thumbnails, BOM qty) · hardware (fastener stack sections to scale) · steps (parts strip with letters + ×qty, exploded figure with callouts, generated actions in stacking order, wiring tables/diagrams, torque line, model notes) · coverage appendix |
162
+ | Later | add `lib/docs/<type>.ts` + an entry in `build.ts DOC_TYPES` | **wiring guide** (from `wiringRows`/`wiringDiagram` per harness), **quick start** (key figures, battery/props steps, safety), **test report** (one page per test-data file, table + chart + conditions + provenance) |
163
+
164
+ Assembly steps are *derived*: parts = bodies the step's mates place + hardware on those mates +
165
+ wiring ends not yet introduced + cables; actions come from joints (see `jointActions`), wiring
166
+ tables from `wiringChecklist` connections (crossovers flagged), notes from harness
167
+ `usage_note`s, `operating_conditions.cooling`, mounting metadata, plus authored spec notes.
168
+
169
+ ### 7. Build
170
+
171
+ ```bash
172
+ npx protoboard run tech-docs # stand-ins, figures, HTML, PDF, page PNGs
173
+ npx protoboard run tech-docs --input only=datasheet --input pdf=false # fast iteration
174
+ ```
175
+
176
+ Figures are cached by content hash in `docs/images/.render-cache.json`, so a
177
+ rebuild only re-renders what changed. Each run leaves an operation record
178
+ listing every file the documents use. Fonts are copied to
179
+ `docs/assets/fonts/`, git-ignored (a build output).
180
+
181
+ The builder emits a flow of blocks; `templates/flow.js` paginates them into A4 frames in the
182
+ browser (keep-with-next headings, table splitting with repeated header, cover/full pages,
183
+ page numbers, contents), then the paginated DOM is saved as the final HTML and printed to PDF
184
+ with `preferCSSPageSize`. Theme: `templates/doc.css` (light pages, dark cover, Kode Mono body,
185
+ Kode Mono Bold display, orange accent, one type scale). Glyphs the fonts lack are mapped in
186
+ `format.ts: glyphSafe`.
187
+
188
+ ### 8. Finish
189
+
190
+ Run **uhd-tech-docs-verify**, fix every error, do the visual review of the page PNGs in
191
+ `docs/.review/<doc>/`, iterate. Report the model gaps the documents exposed (gap markers,
192
+ stand-in assumptions) to the model author. Keep everything local: no pushing or publishing.
193
+
194
+ ## Decision points
195
+
196
+ | Decision | Rule |
197
+ | --- | --- |
198
+ | A value the document needs and the model lacks | A gap marker (**—**) and an entry on the list for the model author; never a typed value. |
199
+ | A value computed from others | A derivation in `lib/derived.ts` with its input queries (**D** marker), not arithmetic in a builder. |
200
+ | Stand-in or measured | A measured file with the stand-in's id and columns replaces it; until then the stand-in carries its watermark. |
201
+ | Step order in the assembly guide | Authored in `assembly.steps`; anything uncovered is appended automatically and must be ordered before release. |
202
+ | Fast iteration | `--input only=<doc> --input pdf=false` while iterating; a full build before verifying. |
203
+
204
+ ## Outputs
205
+
206
+ | Output | Where |
207
+ | --- | --- |
208
+ | The doc spec | `docs/docspec.ts` |
209
+ | Documents (generated; never edit) | `docs/<system-id>-datasheet.{html,pdf,values.json}`, `docs/<system-id>-assembly-guide.{html,pdf,values.json}` |
210
+ | BOM used by the documents | `docs/<system-id>-bom.json` |
211
+ | Figures and render cache | `docs/images/` |
212
+ | Stand-in test data | `docs/test-data/` (stand-ins never overwrite measured files) |
213
+
214
+ ## Verification
215
+
216
+ ```bash
217
+ npx protoboard run tech-docs-verify # must succeed
218
+ ```
219
+
220
+ Then **uhd-tech-docs-verify**'s visual review of every page.
221
+
222
+ ## Failure handling
223
+
224
+ | Code or symptom | Means | Do |
225
+ | --- | --- | --- |
226
+ | `run` refused: tool `chrome` or `poppler` missing | No Chromium-family browser or no poppler (`doctor` says which, and where it looked) | Install them, or set `$CHROME` to a browser's executable |
227
+ | Verifier warns about fonts | Kode Mono did not load from `assets/fonts` | Rebuild the documents (`run tech-docs` copies the fonts); never commit them |
228
+ | `PB_ARTIFACT_UNAVAILABLE` | A body's CAD is not in the checkout | Expected for non-redistributable vendor CAD; it is shown as not available |
229
+ | `UHD_UNPLACED` | A part nothing mates | A `placementHints` entry from a model constant, or a display placement |
230
+ | `*_STALE`, `PB_OUTPUT_NOT_EXPORTED` | Figures would show outdated CAD | Regenerate the CAD (**frame-cad**, **enclosure-cad**, **circuit-board**, **harness-cad**) before building |
231
+ | A value renders as "—" | The model does not have it | Report it to the model author; do not type it |
232
+ | Coverage warnings (automatic steps) | The doc spec's steps miss mates or links | Add them to `assembly.steps` in order |
233
+
234
+ Every other code: **system-compose** [diagnostic codes](../system-compose/references/diagnostic-codes.md).
@@ -0,0 +1,56 @@
1
+ # Doc spec (`docs/docspec.ts`)
2
+
3
+ Type: `DocConfig` from `@protoboardai/ext-docs` (`lib/types.ts`). Presentation only. Worked
4
+ example: the quadcopter project's `examples/quadcopter-5in/docs/docspec.ts`.
5
+
6
+ ```ts
7
+ import type { DocConfig } from "@protoboardai/ext-docs";
8
+ import { BATTERY_PAD } from "../src/modules/frame/frame.uhd.ts"; // model constants are fine
9
+
10
+ const config: DocConfig = {
11
+ title: "5-inch 6S Quadcopter", docId: "UHD-QUAD5", revision: "A",
12
+ root: ["frame"], // assemble() root
13
+ camera: { dir: [1, -1.18, 0.82] }, // house iso: front-right-above (X forward, Z up)
14
+ placementHints: [{ path: ["battery"], matrix: [/* from BATTERY_PAD */], note: "frame BATTERY_PAD" }],
15
+ datasheet: {
16
+ keyFigures: [{ label: "Static thrust", q: "derived:propulsion.thrust_full_total", f: "si|d1", sub: "…" }],
17
+ specGroups: [{ title: "Power", rows: [{ label: "Capacity", q: "def:…:interfaces[id=battery_out].parameters[id=capacity].value" }] }],
18
+ mechanicalCallouts: ["frame.stack_mount", "top_plate.gps_mount"],
19
+ hub: "stack/fc",
20
+ },
21
+ assembly: {
22
+ explode: 16,
23
+ steps: [
24
+ { id: "motors", title: "Mount the motors", mates: ["arm_*_mount"], camera: { dir: [1, -1.3, 0.42] } },
25
+ { id: "stack", title: "Build the FC and ESC stack", mates: ["stack_mount", "fc_mount"], wiring: ["stack/*"] },
26
+ { id: "power", title: "Solder the battery lead and capacitor", wiring: ["battery_*", "bulk_cap_*"], figure: "wiring" },
27
+ { id: "battery", title: "Strap on the battery", place: ["battery", "strap"] },
28
+ { id: "props", title: "Fit the propellers", mates: ["arm_*/prop_on_shaft"],
29
+ notes: [{ kind: "caution", text: "A pack holds {{def:hqprop-ethix-s5:traits[type=performance].params.pack_split.cw}} CW …" }] },
30
+ ],
31
+ },
32
+ testData: { standins: "multirotor" },
33
+ };
34
+ export default config;
35
+ ```
36
+
37
+ ## Step rules
38
+
39
+ - `mates` match `assemble().mates[].linkId` (sub-assembly mates are prefixed: `arm_fl/prop_on_shaft`).
40
+ - `wiring` match `wiringChecklist` link ids; sub-assembly links are prefixed with the instance
41
+ (`stack/fc_esc_cable`).
42
+ - A mate or link is used by the first step that matches it. Leftovers become automatic steps
43
+ (the verifier warns).
44
+ - Order steps the way a person builds: structure first, parts that later parts cover early,
45
+ wiring right after the parts it joins are fixed, props last.
46
+ - `figure: "wiring"` uses a 3D route figure when every link in the step has geometry on both
47
+ ends (assemble().routes), else pad-to-pad diagrams.
48
+ - Notes are for guidance the model cannot express (safety, order); any value in a note goes in
49
+ `{{query|format}}`. Model notes (harness `usage_note`, `operating_conditions.cooling`,
50
+ mounting metadata) are added automatically.
51
+
52
+ ## Cameras
53
+
54
+ Directions are from the target toward the camera, world Z up. Keep one direction for
55
+ consecutive steps; change only when the step's parts are hidden (e.g. a low angle for screws
56
+ from below: `[1, -1.3, 0.42]`). Top view: `{ dir: [0,0,1], up: [1,0,0] }` (front up).
@@ -0,0 +1,50 @@
1
+ # House style (as implemented)
2
+
3
+ Applies the adopted rules in [style-study](style-study.md) to the Protoboard brand. Source of
4
+ truth: `lib/templates/doc.css`, `lib/charts.ts`, `lib/render/page/render.js` in the extension.
5
+
6
+ ## Page
7
+
8
+ - A4 portrait, printed with `preferCSSPageSize`. Frame: header band at 9 mm (accent square,
9
+ product name in Kode Mono Bold, doc type, doc id), body 20–280 mm, 16 mm side margins (178 mm
10
+ measure), footer at 8 mm (brand, doc id · Rev · date, page `NN / NN`).
11
+ - Cover: dark top (#111, 8 mm grid), kicker in accent caps, title in Kode Mono Bold with the last word
12
+ in accent, rev/date/id tags, shaded hero render; light bottom with key figures and the legend.
13
+ - Sections: `NN TITLE` in Kode Mono Bold 13.5 pt with a 0.5 pt rule; sub-heads 7.6 pt caps with an
14
+ accent square; each major section starts a page.
15
+
16
+ ## Type
17
+
18
+ Kode Mono for text and numbers (tabular), Kode Mono Bold for display. One scale:
19
+ 5.5 (markers) · 5.8 (micro) · 6.4 (small, captions) · 6.9 (tables) · 7.6 (body) · 8.6 (lead) ·
20
+ 12 (step titles) · 13.5 (section) · 15 (key figures) · 25 pt (cover). The verifier warns above 16
21
+ sizes and fails below 5 pt. Glyphs the fonts lack are mapped (`glyphSafe`: µ → μ, Φ → Ø, Ω → ohm).
22
+
23
+ ## Colour
24
+
25
+ Ink #1b1b1b, muted #6f6f6f, rules #d6d6d6, tint #f3f3f3, accent #ff5c00 (fills, bubbles, axes,
26
+ highlights) and #d94e00 for small accent text; red #c62828 only for warnings and errors.
27
+
28
+ ## Figures
29
+
30
+ - Line art (`lineart`): flat light fills, 1.5 px-equivalent silhouettes, lighter creases;
31
+ context parts grey (#f5f5f5 fill, #a8a8a8 lines); new parts natural tints with dark lines;
32
+ hardware and interface features accent (#ffb584 fill, #d24a00 lines); dashed accent axes.
33
+ - Shaded (`shaded`) for covers only.
34
+ - Orthographic cameras, fitted to the geometry; transparent background.
35
+ - Callouts: 4.5 mm bubbles in columns at the figure edges, 0.5 pt straight leaders ending in a
36
+ dot; letters in steps (match the parts strip), numbers in drawings (match the table); accent
37
+ bubbles for hardware / interfaces.
38
+ - Dimensions: accent 0.4 pt lines with arrowheads, value from a query, `(unit: mm)` in the caption.
39
+
40
+ ## Tables and charts
41
+
42
+ Header row on tint with a 0.5 pt ink rule, 0.25 pt row rules, no vertical rules, numbers right
43
+ aligned, groups as caps rows. Charts: hairline grid, one accent series, ink/grey for others,
44
+ legend row under the axis, stand-in watermark + badge.
45
+
46
+ ## Markers and notes
47
+
48
+ Status superscripts in accent (A, D, S, M) or grey (—), legend on the cover and spec page.
49
+ Admonitions: WARNING (red triangle), CAUTION (accent triangle), TIP (bulb), NOTE (info); hairline
50
+ above and below, no filled boxes.
@@ -0,0 +1,270 @@
1
+ # Style study: Bambu Lab and DJI hardware documentation
2
+
3
+ Purpose: extract the *visual system* behind two companies whose hardware docs read as clean and
4
+ trustworthy, so we can build a CSS print template for UHD datasheets and assembly guides.
5
+
6
+ **We learn the style only.** We do not copy their text, illustrations, icons, color identity,
7
+ typefaces under the maker's own licence (Dji-Book/Dji-Demi), logos, or layouts verbatim. Our docs use our
8
+ own content, our own renders, and our own brand.
9
+
10
+ ## Sources (downloaded 2026-09-26, stored git-ignored in `docs/reference-docs/`)
11
+
12
+ | # | Document | URL |
13
+ |---|---|---|
14
+ | B1 | Bambu Lab A1 Quick Start (24 pp) | https://cdn1.bambulab.com/documentation/quick-start-f507128172bdf/Quick%20start%20guide%20-%20A1-EN.pdf |
15
+ | B2 | Bambu Lab AMS Quick Start Guide (10 pp) | https://cdn1.bambulab.com/documentation/quick-start-0fa3c8ddd3d3f/AMS/English%20version-Quick%20Start%20Guide%20for%20AMS.pdf |
16
+ | B3 | Bambu Lab Wiki, P1S Quick Start (web page printed to PDF) | https://wiki.bambulab.com/en/p1/manual/p1s-quick-start-guide |
17
+ | D1 | DJI O4 Air Unit Series User Manual v1.0 2025.01 (23 pp) | https://dl.djicdn.com/downloads/DJI_O4_Air_Unit_Series/UM/DJI_O4_Air_Unit_Series_User_Manual_v1.0_en.pdf |
18
+ | D2 | DJI O4 Air Unit Series Product Information v1.0 (2 pp fold-out) | https://dl.djicdn.com/downloads/DJI_O4_Air_Unit_Series/PI/20250422/DJI_O4_Air_Unit_Series_Product_Information_v1.0.pdf |
19
+
20
+ Observations below come from rendered pages in `docs/reference-docs/pages/` (e.g. `hi-a1-03.png`,
21
+ `hi-a1-12.png`, `hi-a1-22.png`, `hi-djium-05.png`, `hi-djium-06.png`, `djium-10.png`,
22
+ `crop-djipi-1.png`).
23
+
24
+ ---
25
+
26
+ ## 1. Two archetypes
27
+
28
+ The two vendors represent the two documents we need:
29
+
30
+ - **Bambu quick start = assembly guide archetype.** Picture-first. One full-bleed-ish line drawing
31
+ per page, one to four numbered sentences underneath. Almost no prose.
32
+ - **DJI user manual = datasheet / reference archetype.** Text-first with numbered chapters,
33
+ gray section bars, callout-numbered product overview, fully dimensioned drawings, pinout table.
34
+
35
+ UHD should ship both from one template: shared tokens, two page layouts.
36
+
37
+ ## 2. Page size, margins, grid
38
+
39
+ | | Bambu B1/B2 | DJI D1 | DJI D2 |
40
+ |---|---|---|---|
41
+ | Trim | 120 x 120 mm square | ~138 x 205 mm (A5-ish, narrow) | 390 x 240 mm fold-out |
42
+ | Outer margin | ~9 mm | ~12 mm | ~8 mm |
43
+ | Columns | 1 | 1 (2 for callout legend lists) | 4-5 narrow language columns |
44
+
45
+ - **Single column dominates.** DJI switches to 2 columns only for short enumerations (the
46
+ numbered part legend under the overview drawing: 1-4 left, 5-8 right).
47
+ - **Bambu page anatomy** (fixed, every step page): title top-left -> thin full-width hairline
48
+ rule -> illustration zone (~65% of page height, centered) -> instruction text zone anchored
49
+ to the bottom (~20%). The text never floats up to meet the drawing; empty space sits between.
50
+ - **DJI page anatomy:** running header at top right/left (alternating, mirrored for recto/verso)
51
+ with a 0.5 pt rule under it; body; folio + copyright at the bottom outside corner.
52
+ - Figures are centered on the text column; text is left-aligned (ragged right, no justify, no
53
+ hyphenation) in both.
54
+ - Our target: A4 / US Letter print with a 1-col text measure of ~110-120 mm (DJI's measure) is
55
+ the readable width; on A4 use 20 mm margins and let figures span the full text block.
56
+
57
+ ## 3. Typography
58
+
59
+ - **Families:** both use a single humanist/geometric sans for everything (Bambu: HarmonyOS Sans
60
+ in Regular/Medium/Bold; DJI: Open Sans + Semibold with a house display face for titles). No
61
+ serif, no second family. We use one open-licensed sans (e.g. Inter, Open Sans, or IBM Plex Sans)
62
+ plus its tabular-figure feature.
63
+ - **Scale (relative to body = 1.0):**
64
+ - Chapter number + title (DJI "1 Product Overview"): ~1.6x, semibold, number set in the
65
+ margin gutter with a wide tab before the title.
66
+ - Section bar (DJI "1.1 Overview"): ~1.3x, semibold, dark text on a mid-gray (~#BDBDBD) full
67
+ width bar, ~1.6x line height tall, text inset ~2 mm.
68
+ - Sub-heading (DJI "DJI O4 Air Unit" above a drawing): ~1.1x semibold, no bar.
69
+ - Page/step title (Bambu "Install Purge Wiper"): ~1.15x bold, on a hairline rule.
70
+ - Body: ~9 pt DJI, ~7.5 pt Bambu (small trim); leading ~1.45.
71
+ - Captions/labels in drawings: ~0.8x body, regular weight, gray (~#555) not black.
72
+ - **Weights:** only two in running text: regular and semibold/bold. Emphasis is by weight, never
73
+ italic, never underline (links are color only, DJI uses light blue #1FA5E0-ish).
74
+ - **Numbers & units:**
75
+ - DJI dimension drawings: one decimal consistently (`25.5`, `30.0`, `13.98` only where the
76
+ tolerance warrants), units stated once in the caption: "(unit: mm)". No unit on each dimension.
77
+ - Ranges use en-dash-like hyphen: `3.7-13.2 V`, `-10°C to 40°C (14° to 104°F)`.
78
+ - Diameter uses Ø; thread callouts `4-M2.0` (quantity-thread) with depth `▽MAX3.0`.
79
+ - **Anti-pattern observed (B1 spec table):** inconsistent unit spacing ("8.3kg" vs "300 ℃"
80
+ vs "256*256*256 mm³", "*" used as multiplication sign). We fix this: always a thin/normal
81
+ space between value and unit, `×` for multiplication, `°C` as two glyphs.
82
+ - Part-quantity notation (B1 accessory page): `ST3-23 Screw (×13)` with the `(×13)` in the
83
+ accent color; the part's purpose in a second line in parentheses: `(For Base Housing)`.
84
+
85
+ ## 4. Illustrations: renders, line art, exploded views
86
+
87
+ - **Style: monochrome line art, not photoreal renders.** Both vendors draw products as clean
88
+ vector outlines: ~0.5-0.75 pt dark gray (#333-#444) outer silhouette, ~0.25 pt internal edges,
89
+ no gradients, no shadows, white fill. DJI adds a flat light-gray fill on some faces; Bambu
90
+ stays pure white.
91
+ - **Highlight by flat accent fill.** Bambu fills the *part being acted on* (build plate, table
92
+ area, screw holes, zip-tied regions) with a flat semi-transparent brand green (~#8FD19E fill,
93
+ darker green outline). Everything else stays neutral line art. This is the single most
94
+ effective device in the set: the eye goes to the colored region instantly.
95
+ - **Context parts** are never removed: the whole printer is drawn, only the target is colored.
96
+ There is no ghosting via transparency; context is conveyed by keeping it uncolored and
97
+ equally thin-lined. (For our exploded views: context parts at 40% gray lines, active part at
98
+ full weight + accent fill.)
99
+ - **Camera angles:** a consistent 3/4 isometric-ish view from front-right-above for full
100
+ assemblies (B1 p12, B2 AMS pages); pure orthographic front/top/side for dimensioned views
101
+ (D1 p6). The same product is shown from the same angle across consecutive steps so the reader
102
+ does not re-orient.
103
+ - **Detail insets:** Bambu uses rounded-rectangle inset boxes (thin gray border, ~2 mm radius)
104
+ with a zoomed detail, connected to the main drawing by a thin leader to the area, each inset
105
+ tagged with a circled step number (1), (2) at its lower right. DJI uses similar insets with
106
+ a circular magnifier.
107
+ - **Motion arrows:** broad, solid, flat accent-colored arrows (Bambu green block arrows for
108
+ "slide in", "lift out"); DJI uses thin black arrows plus a numbered black disc for sequence.
109
+ Arrows are the only filled, heavy shapes in the drawing.
110
+ - **Background:** white page, no backdrop, no floor shadow. Bambu B2 sometimes places the
111
+ drawing on a very light gray (#F3F3F3) rounded panel; that is the only tint behind art.
112
+ - **Dimension drawings (D1 p6):** orthographic views, dimensions and extension lines in a light
113
+ blue accent (not black) at ~0.35 pt, values in the same blue, so dimensions read as an overlay
114
+ on the gray part geometry. Grouped per variant under a semibold sub-heading.
115
+
116
+ ## 5. Callouts and leader lines
117
+
118
+ - **DJI numbered callouts (D1 p5):** plain numerals (no bubble) at the end of a straight leader,
119
+ leader and number both in light blue, leader ~0.35 pt ending in a small dot on the part.
120
+ Leaders are horizontal or vertical only, never diagonal; numbers sit outside the part
121
+ silhouette, aligned in columns left and right of the drawing. The legend is a 2-column
122
+ numbered list directly under the figure.
123
+ - **Bambu labeled callouts (B1 p4, B2 p4, B3):** text label directly at the end of a thin
124
+ horizontal leader, no number indirection. Label text is ~0.8x body; leader in the same gray
125
+ as the label (or accent green when naming the highlighted part). Labels align to a common
126
+ left or right edge outside the art (B3 "Component Introduction": all left labels flush left,
127
+ all right labels flush right).
128
+ - **Circled step numbers** (①②) are used in Bambu for linking an inset to its instruction line;
129
+ ~4 mm circle, 0.5 pt stroke, regular-weight numeral, accent green when the step is on screen.
130
+ - Never more than ~8 callouts per figure; if more are needed they split the figure.
131
+
132
+ ## 6. Steps and step layout (assembly archetype)
133
+
134
+ - **One page = one operation**, titled with a verb phrase in Title Case ("Install Build Plate",
135
+ "Unlock Toolhead", "Lock Base Housing", "Place On Table").
136
+ - Under the art, 1-4 **circled-number instructions**, each one sentence, each one action.
137
+ Sub-notes in parentheses indented under the sentence. Numbered markers are ①② glyphs, not
138
+ "1." decimals, to distinguish steps from list items.
139
+ - **Quantity + fastener spec inline in the step:** "Install 2*ST3-23 Screws (For Base
140
+ Housing) in the holes highlighted in green." The holes are colored in the drawing, so text
141
+ and art cross-reference by color. (Use "2 × ST3-23" in ours.)
142
+ - **Parts-in-box pages first:** "What's In The Box" and "Accessory Box" pages show every item as
143
+ a small isolated line drawing on a loose grid (3-4 per row), name centered underneath, quantity
144
+ `(×n)` in accent. Items at consistent visual scale per row, not true scale.
145
+ - Tools needed appear as parts on the accessory page (Allen key H2, H1.5) rather than as icons;
146
+ the step text names the tool. No torque values are given in either vendor's consumer docs;
147
+ DJI covers it with "DO NOT overtighten the screws to avoid stripping." We will add explicit
148
+ torque (N·m) since our audience builds from parts.
149
+ - Emphasized safety-relevant step line (B1 p12) is set entirely in accent green, below the
150
+ numbered steps, not in a box.
151
+
152
+ ## 7. Spec tables
153
+
154
+ - **D2 (DJI product info):** 3 columns (parameter | variant A | variant B), header row on
155
+ a light gray fill, rows separated by 0.25 pt light-gray horizontal rules only, **no vertical
156
+ rules**, no zebra striping. Parameter column is regular weight, left aligned, wraps to two
157
+ lines ("Operating Temperature"). Values carry their own units. Footnotes marked `[1]` in the
158
+ cell, explained in small gray text under the table.
159
+ - **D1 pinout table (p10):** 3 columns (signal | wire color | function), subtle alternate row
160
+ tinting (#EFEFEF) as the only grouping device, multi-line values stacked in one cell.
161
+ - **B1 spec table:** grouped rows: a merged left "group" cell (Body, Toolhead, Heatbed, Speed...)
162
+ spanning its item rows, then Item | Specification. Full grid in thin accent-green rules, header
163
+ text in accent green, everything centered. Grouping is excellent; the full grid and centered
164
+ numbers are busier than DJI's rules-only style. We take the grouping, drop the full grid.
165
+ - Neither uses a separate units column; we will (right-aligned value, left-aligned unit column)
166
+ for datasheet electrical tables where numbers must compare vertically.
167
+
168
+ ## 8. Iconography
169
+
170
+ - DJI defines a **legend on page 3** before content: three outline icons: triangle-! (Important),
171
+ light bulb (Hints and Tips), open book (Reference). Monochrome, stroke ~0.75 pt, ~4 mm,
172
+ same line style as the illustrations. Every later admonition uses one of these three only.
173
+ - Bambu uses almost no icons: a circle-slash "prohibited" overlay (TPU, damp PVA) in red on the
174
+ AMS warning page, and QR codes. Color and the drawings do the work.
175
+ - Takeaway: a tiny closed icon set (3-5), outline style, declared in a legend.
176
+
177
+ ## 9. Warnings, cautions, notes
178
+
179
+ - **DJI:** icon at left margin, text indented to a hanging position, bullets inside. Hint blocks
180
+ get a hairline rule above and below spanning the text width (p5 note under the legend list).
181
+ No colored boxes, no fills. "DO NOT" is set in capitals inline as the emphasis.
182
+ - **Bambu B2 p2:** heading "*Warning:" in bold, then small bulleted lines; the critical line is
183
+ set in red text. No box.
184
+ - Both avoid heavy colored callout boxes. Severity is carried by icon + a single color of text.
185
+
186
+ ## 10. Headers, footers, page numbers, revision
187
+
188
+ - **DJI running header:** product name in semibold + doc type in regular ("**DJI O4 Air Unit**
189
+ User Manual"), outside-aligned, 0.5 pt rule beneath running the full text width.
190
+ - **DJI footer:** page number at the outside corner (~1.1x body, regular), copyright line in
191
+ ~6 pt gray immediately inboard of it.
192
+ - **Revision:** cover shows a boxed version tag `v1.0` next to the date `2025.01`; the same
193
+ appears on the cover's text layer and the filename (`..._v1.0_en.pdf`). Bambu prints a small
194
+ document code on the cover bottom (e.g. part number with revision letter "-A").
195
+ - Bambu step pages carry no header/footer at all; the title rule is the only chrome.
196
+ - Back cover (both): logo, URL, "content subject to change", nothing else.
197
+
198
+ ## 11. What makes them clean
199
+
200
+ 1. **One idea per page / section.** Bambu literally one operation per page.
201
+ 2. **Color restraint:** exactly one accent (Bambu green, DJI blue) plus grays; accent is
202
+ reserved for "look here" (highlighted part, dimension overlay, quantity, link). Red only for
203
+ prohibitions.
204
+ 3. **Line art over photos:** uniform stroke weights make every figure look like one family.
205
+ 4. **Consistent alignment:** titles, rules, body text, and table edges share the same left edge;
206
+ figures are centered; callout labels align to vertical columns.
207
+ 5. **Generous whitespace:** illustration zones are ~40-60% empty; text never crowds art.
208
+ 6. **Predictable page anatomy:** same title/rule/figure/text positions on every page.
209
+ 7. **Units stated once** (DJI "unit: mm") rather than repeated on every value.
210
+ 8. Weak spots to avoid: B1's inconsistent unit spacing and full-grid tables; D1's reliance on
211
+ external URLs for specs.
212
+
213
+ ---
214
+
215
+ ## Rules we adopt
216
+
217
+ Style is learned, not copied: our content, drawings, colors, fonts, and branding are our own.
218
+
219
+ 1. **Page:** A4 / Letter, 20 mm margins, single text column max 120 mm measure; 2 columns only
220
+ for callout legends and short lists.
221
+ 2. **One sans family**, two weights in text (400, 600); headings 600-700. No italics, no
222
+ underlines; tabular figures in tables.
223
+ 3. **Type scale:** body 9.5 pt / 1.45; captions & drawing labels 7.5 pt in #555; section title
224
+ 12 pt; chapter title 16 pt with number hung in the gutter.
225
+ 4. **Section headers:** full-width light-gray bar (#D9D9D9), 600 weight, 2 mm inset, or a
226
+ 0.5 pt hairline under the title for step pages. Pick per doc type; never both on one page.
227
+ 5. **One accent color** (UHD accent) used only for: highlighted active part, dimensions,
228
+ quantities, links, and step markers. Red only for prohibitions/danger.
229
+ 6. **Illustrations are line art:** 0.6 pt silhouette, 0.25 pt internal edges, #333, white
230
+ fill, no shadows/gradients/backdrops.
231
+ 7. **Active part = flat accent fill at ~45% opacity + accent outline;** context parts drawn in
232
+ full but in 40% gray lines. Never delete context.
233
+ 8. **Consistent camera:** one iso 3/4 front-right-above view per assembly, held across
234
+ consecutive steps; orthographic views only for dimensioned drawings.
235
+ 9. **Callout numbers:** 5 mm circle, 0.5 pt stroke, 7 pt bold numeral; leader 0.5 pt, straight,
236
+ horizontal/vertical only, ending in a 1 mm dot on the part; bubbles aligned in columns
237
+ outside the silhouette; max 8 per figure.
238
+ 10. **Named labels** instead of numbers when there are 5 or fewer parts; labels flush to a
239
+ shared left/right edge outside the art.
240
+ 11. **Dimensions:** accent color 0.35 pt, one decimal consistently, units once in the caption
241
+ "(unit: mm)"; Ø, `4 × M2` thread notation, depth as "▽ 3.0".
242
+ 12. **Assembly step page:** verb-phrase title -> figure zone (~65% height) -> 1-4 numbered
243
+ one-action sentences anchored to the bottom. One operation per page/step block.
244
+ 13. **Step markers** are circled numerals (① style, 4 mm) distinct from decimal list numbers;
245
+ insets tagged with the same marker.
246
+ 14. **Parts strip** at the top of every step: small isolated line drawings of consumed parts,
247
+ name beneath, quantity as bold accent `×4`; tool icons in the same strip, right-aligned.
248
+ 15. **Fasteners in text:** `4 × M3×8 SHCS`, with torque in N·m where applicable
249
+ (`0.6 N·m`), always value-space-unit.
250
+ 16. **Opening "In the box / BOM" page** laid out as a 3-4 up grid, items at consistent visual
251
+ scale, quantities in accent.
252
+ 17. **Spec tables:** header row on #F0F0F0, 0.25 pt horizontal rules only, no vertical rules,
253
+ no zebra (optional light tint only for pinout tables); left-aligned labels, right/decimal-
254
+ aligned numbers, separate unit column, grouped with a merged left group cell.
255
+ 18. **Footnotes** as `[1]` in-cell, explained in 7 pt gray beneath the table.
256
+ 19. **Admonitions:** three icons max (Warning triangle, Tip bulb, Reference book) declared in a
257
+ legend on page 2-3; icon in margin, hanging-indented text, hairline rules above/below; no
258
+ filled colored boxes. "DO NOT" in caps as emphasis.
259
+ 20. **Units & numbers:** always a space between value and unit (`8.3 kg`, `300 °C`), `×` for
260
+ dimensions (`256 × 256 × 256 mm`), ranges `3.7-13.2 V`, dual units in parentheses when
261
+ needed.
262
+ 21. **Running header:** product name 600 + doc type 400, outside-aligned, 0.5 pt rule; footer
263
+ has page number at outside corner and doc ID + revision + date in 6.5 pt gray.
264
+ 22. **Revision identity** on the cover as a boxed tag `Rev A` + ISO date, repeated in footer
265
+ and filename.
266
+ 23. **Whitespace budget:** figures may occupy at most ~60% of their zone; never shrink art to
267
+ fit extra text, add a page instead.
268
+ 24. **Alignment:** titles, rules, body, and table edges share one left edge; figures centered.
269
+ 25. **No copying:** no vendor text, drawings, icons, colors, fonts, or layouts reproduced;
270
+ reference PDFs stay git-ignored and are used only for visual comparison.