From 6a9ed35bcdb52c38070513b4f5ee022f05648396 Mon Sep 17 00:00:00 2001 From: jedi Date: Mon, 31 Aug 2026 19:09:58 +0200 Subject: [PATCH] stash --- docs/glossary.md | 3 +- docs/handles-and-shortids.md | 3 +- docs/implementation.md | 30 +-- frontend/src/label-layouts.js | 185 +++++++++---------- frontend/src/label.js | 57 ++---- frontend/src/router.js | 4 + frontend/src/views/Inventory.vue | 2 +- frontend/src/views/InventoryDetail.vue | 2 +- frontend/src/views/Print.vue | 60 +++--- frontend/src/views/StorageLocation.vue | 4 +- frontend/src/views/StorageLocationDetail.vue | 2 +- 11 files changed, 166 insertions(+), 186 deletions(-) diff --git a/docs/glossary.md b/docs/glossary.md index 90c3643..58df7d8 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -221,7 +221,8 @@ pair keeps resolving correctly even after being copied out of the instance it wa a [User-Qualified ID](#user-qualified-id) or [Item URL](#item-url), it isn't limited to owned kinds (any kind in the short id registry can be domain-qualified) and isn't meant to be openable with zero context, no scheme, no path, opaque bit-packed payload, it belongs inside the app or between things -that already speak its short-id format, not on a physical label. +that already speak its short-id format - including a physical label meant for the app's own scanner, +not a link or code meant to be opened by something with no idea what a Toolshed short id is. *See: [handles-and-shortids.md](handles-and-shortids.md#domain-qualified-short-id)* **Item Label** (Implemented) diff --git a/docs/handles-and-shortids.md b/docs/handles-and-shortids.md index db73635..16e181c 100644 --- a/docs/handles-and-shortids.md +++ b/docs/handles-and-shortids.md @@ -181,7 +181,8 @@ It isn't a URL and isn't meant to be openable by something that doesn't already Toolshed short id is: there's no scheme, no path, and the payload after `:~` is bit-packed base64, opaque to a human. It belongs to the same "context already makes clear it's Toolshed data" class as a compact User-Qualified ID, meant for use inside the app (or between things that already speak the -short-id format), not for a physical label or a link shared outside it. What it adds over a bare +short-id format, including a physical label meant for the app's own scanner) - not a link or code +meant to be opened by something with no idea what a Toolshed short id is. What it adds over a bare `~token` is that it no longer depends on "whichever backend I currently happen to be talking to" — the domain travels with it, so it keeps resolving to the same entity once copied elsewhere. Unlike a User-Qualified ID, it isn't limited to owned kinds: every kind in the short id registry can be diff --git a/docs/implementation.md b/docs/implementation.md index 848f7aa..04adb81 100644 --- a/docs/implementation.md +++ b/docs/implementation.md @@ -102,11 +102,14 @@ A template's `layout` is a tree as described in label.js, with leaves whose `typ ### Generated QR-Only Template Matrix `QR_ONLY_TEMPLATES` is the full "just the code" matrix: every `{symbology, error-correction level, [rMQR] size strategy}` combination label.js's `QR_LEAF_TYPES` supports, one template each. A plain `id` (no suffix) is always anyd's own defaults: ecc `"M"` and, for rMQR, size `"balanced"`. Coverage isn't uniform (see `QR_LEAF_TYPES`): full QR gets all four ecc grades L/M/Q/H; Micro QR swaps `"L"` (QR's actual lowest) for the even-lower, M1-only, detection-only `"Detection"`, and has no `"H"` at all; rMQR only ever supports ecc `"M"` or `"H"`, each crossed with all three size strategies (balanced/min/max). The matrix is generated (rather than hand-writing every near-duplicate entry) so that a symbology/level/size combination it's missing is one new row here, not a new block to keep in sync with its neighbors. `id` doubles as the layout's leaf `type`, since that's exactly what `QR_LEAF_TYPES` is keyed by. -### Derived Vars Shape And Ordering -A derived var (`DERIVED_VARS`) is a format string calculated from other vars rather than typed directly - it doesn't get its own input, just a read-only, live-recalculated display next to the ones that do (see Print.vue and `withDerivedVars`). Its `inputs` name every var (base or, in principle, another derived one - e.g. `itemUrl`/`itemHandle`, which both read the derived `userHandle`) that `calc` reads. `inputs` is declared up front rather than inferred from `calc`'s body so `BASE_VARS` can include a var like `"webdomain"` that only feeds a calculation and that no template ever references directly. Declaration order matters here: `withDerivedVars` runs these in a single pass, so a derived var must be declared after every other derived var it depends on. +### Content Kinds Registry +`CONTENT_KINDS`/`CONTENT_KINDS_BY_ID` (`label-layouts.js`) is the single registry for everything that varies per content kind (currently `item`/`storage-location`): the handles-and-shortids.md kind letter (`typedPrefix`), the short-id.js schema name to use for an individually- vs. group-owned thing of that kind (`shortIdKind`/`groupShortIdKind`), the field name short-id.js's `serializeShortId` expects for its local id (`localIdField`), and a builder for the kind's long-form URL (`buildUrl`, `undefined` for a future kind added with no such route - see the `url` `DERIVED_VARS` entry). Every place that used to special-case "item vs. storage location" - `label.js`'s prefill builders, `label-layouts.js`'s `DERIVED_VARS`, Print.vue's `shortId()` - reads this one table instead, keyed by the Content card's `kind` field (a ` + @@ -201,14 +208,15 @@ import { withDerivedVars, templateContent } from "@/label.js"; -import {LABEL_TEMPLATES, BASE_VARS, DERIVED_VARS} from "@/label-layouts.js"; +import {LABEL_TEMPLATES, BASE_VARS, DERIVED_VARS, CONTENT_KINDS, CONTENT_KINDS_BY_ID} from "@/label-layouts.js"; import {shortenedRoute} from "@/router"; -// Print.vue-local calculated fields on top of label-layouts.js's DERIVED_VARS. See -// docs/implementation.md#calculated-short-link-fields. +// Print.vue-local calculated fields on top of label-layouts.js's DERIVED_VARS. See docs/implementation.md#calculated-short-link-fields. const SHORT_URL_VAR = "shortUrl"; // See docs/implementation.md#calculated-short-link-fields. const SHORT_ID_VAR = "shortId"; +// See docs/implementation.md#calculated-short-link-fields. +const DOMAIN_SHORT_ID_VAR = "domainShortId"; // Served unbundled so its wasm sibling stays resolvable. See docs/implementation.md#libweblabel-served-unbundled. const BLOB_URL = "/vendor/libweblabel.js"; @@ -275,7 +283,7 @@ export default { // Scan-reliability messages from label.js's drawLabel (too few px per QR module, or too small in mm) - the label still rendered/prints fine, just flagged as a risk. warnings: [], - // One input per BASE_VARS entry; derived vars (userHandle/itemUrl/itemHandle) are calculated-only (see the `fields` computed), never stored here. Prefilled from query params but left editable. queryFields is applied last so an explicit ?text=... etc. always wins over a `kind` builder's computed value. + // One input per BASE_VARS entry; derived vars (userHandle/url/qualifiedHandle) are calculated-only (see the `fields` computed), never stored here. Prefilled from query params but left editable. queryFields is applied last so an explicit ?text=... etc. always wins over buildLabelFields' computed value. varValues: { ...Object.fromEntries(BASE_VARS.map(v => [v, ""])), text: buildLabelContent(this.prefill), @@ -308,10 +316,13 @@ export default { return at === -1 ? null : this.user.slice(at + 1); }, baseVars() { - return BASE_VARS.filter(v => v !== SHORT_URL_VAR && v !== SHORT_ID_VAR); + return BASE_VARS.filter(v => v !== SHORT_URL_VAR && v !== SHORT_ID_VAR && v !== DOMAIN_SHORT_ID_VAR); + }, + contentKinds() { + return CONTENT_KINDS; }, derivedVars() { - return [...Object.keys(DERIVED_VARS), SHORT_ID_VAR, SHORT_URL_VAR]; + return [...Object.keys(DERIVED_VARS), SHORT_ID_VAR, DOMAIN_SHORT_ID_VAR, SHORT_URL_VAR]; }, // Named content fields the templates draw from, dropping blank values. See docs/implementation.md#fields-computed-dropping-blank-values. fields() { @@ -325,9 +336,12 @@ export default { const shortId = this.shortId(derived); if (shortId) { derived[SHORT_ID_VAR] = shortId; + // Bare, same Home-Instance trust model as shortId - webdomain only picks a frontend, not a backend. See docs/implementation.md#calculated-short-link-fields. if (derived.webdomain) { - derived[SHORT_URL_VAR] = derived.webdomain + "/" - + (this.homeDomain ? this.homeDomain + ":" + shortId : shortId); + derived[SHORT_URL_VAR] = derived.webdomain + "/" + shortId; + } + if (this.homeDomain) { + derived[DOMAIN_SHORT_ID_VAR] = this.homeDomain + ":" + shortId; } } return derived; @@ -447,12 +461,14 @@ export default { methods: { ...mapActions(["fetchIdMap"]), - // camelCase -> Title Case (e.g. "itemHandle" -> "Item Handle") so a new template var needs no hand-written label. + // camelCase -> Title Case (e.g. "qualifiedHandle" -> "Qualified Handle") so a new template var needs no hand-written label. varLabel(v) { return v.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/^./, c => c.toUpperCase()); }, + // Which short-id.js kind applies depends on f.kind directly. See docs/implementation.md#content-kinds-registry. shortId(f) { - if (!f.userHandle) { + const entry = f.kind && CONTENT_KINDS_BY_ID[f.kind]; + if (!f.userHandle || !entry || !f.id) { return null; } if (f.userHandle.startsWith("#")) { @@ -460,29 +476,17 @@ export default { if (owner_group_id === undefined) { return null; } - if (f.itemId) { - return shortenedRoute({kind: "group_item", owner_group_id, item_local_id: f.itemId}).slice(1); - } - if (f.locationId) { - return shortenedRoute({ - kind: "group_storage_location", owner_group_id, storage_location_id: f.locationId - }).slice(1); - } - return null; + return shortenedRoute({ + kind: entry.groupShortIdKind, owner_group_id, [entry.localIdField]: f.id + }).slice(1); } const owner_identity_id = this.identityIdByHandle[f.userHandle]; if (owner_identity_id === undefined) { return null; } - if (f.itemId) { - return shortenedRoute({kind: "item", owner_identity_id, item_local_id: f.itemId}).slice(1); - } - if (f.locationId) { - return shortenedRoute({ - kind: "storage_location", owner_identity_id, storage_location_id: f.locationId - }).slice(1); - } - return null; + return shortenedRoute({ + kind: entry.shortIdKind, owner_identity_id, [entry.localIdField]: f.id + }).slice(1); }, // Ticks 0..totalMm via tapePxPerMm, shared by both ruler computeds; spacing comes from the shared rulerTier, so both rulers coarsen together. diff --git a/frontend/src/views/StorageLocation.vue b/frontend/src/views/StorageLocation.vue index ec7d9dc..b19db27 100644 --- a/frontend/src/views/StorageLocation.vue +++ b/frontend/src/views/StorageLocation.vue @@ -200,10 +200,10 @@ export default { return shortenedRoute({kind: 'storage_location', owner_identity_id, storage_location_id: location.id}) }, // Routes to Print.vue with this location's raw identity, same shape as Inventory.vue's - // printLinkFor. See docs/implementation.md#print-link-shape-for-storage-locations. + // printLinkFor. See docs/implementation.md#print-link-shape. printLinkFor(location) { const userHandle = location.owner_group || location.owner - return {path: '/print', query: {kind: 'storage-location', userHandle, location: location.id}} + return {path: '/print', query: {kind: 'storage-location', userHandle, id: location.id}} }, }, watch: { diff --git a/frontend/src/views/StorageLocationDetail.vue b/frontend/src/views/StorageLocationDetail.vue index 9c7b2bb..ff010fe 100644 --- a/frontend/src/views/StorageLocationDetail.vue +++ b/frontend/src/views/StorageLocationDetail.vue @@ -38,7 +38,7 @@ Delete