partforge 0.112.0 → 0.114.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/docs/AUTHORING-PARTS.md +141 -2
- package/docs/ERROR-PATTERNS.md +6 -0
- package/package.json +8 -1
- package/src/framework/app.css +50 -2
- package/src/framework/geometry/polygon.js +67 -5
- package/src/framework/lint/rules-schema.js +56 -0
- package/src/framework/lint/source-scan.js +116 -2
- package/src/framework/mount.js +21 -2
- package/src/framework/panel/author.js +5 -0
- package/src/framework/panel/json-value.js +55 -0
- package/src/framework/panel/render.js +119 -36
- package/src/framework/panel/scoped-params.js +52 -0
- package/src/framework/panel/widget-specs.js +2 -0
- package/src/framework/panel/widgets/custom.js +253 -0
- package/src/framework/panel/widgets/index.js +2 -0
- package/src/panel-values.js +6 -0
- package/types/geometry.d.ts +2 -0
- package/types/index.d.ts +66 -0
- package/types/panel-values.d.ts +18 -0
- package/types/part.d.ts +10 -1
|
@@ -7,6 +7,7 @@ import { makeSelect, makeRadio } from "./select.js";
|
|
|
7
7
|
import { makeFont } from "./font.js";
|
|
8
8
|
import { makeImage } from "./image.js";
|
|
9
9
|
import { makeVector } from "./vector.js";
|
|
10
|
+
import { makeCustom } from "./custom.js";
|
|
10
11
|
// Side-effect imports: font-picker.js / image-picker.js call setFontPicker() /
|
|
11
12
|
// setImagePicker() at module scope, so each widget's button finds a picker to
|
|
12
13
|
// open. They live HERE and not in font.js/image.js because the dependency has
|
|
@@ -27,4 +28,5 @@ export const WIDGET_FACTORIES = {
|
|
|
27
28
|
font: makeFont,
|
|
28
29
|
image: makeImage,
|
|
29
30
|
vector: makeVector,
|
|
31
|
+
custom: makeCustom,
|
|
30
32
|
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Public entry for `partforge/panel-values`: the value contract of a
|
|
2
|
+
// `type: "custom"` control, for hosts that persist panel settings (partforge-
|
|
3
|
+
// cloud rewrites `defaults` with these). Dependency-free, DOM-free, tiny — a
|
|
4
|
+
// host imports this without pulling partforge/lint's rule set into its bundle.
|
|
5
|
+
export { isJsonValue, jsonValueProblem, CUSTOM_VALUE_MAX_BYTES, CUSTOM_VALUE_MAX_DEPTH } from "./framework/panel/json-value.js";
|
|
6
|
+
export { readJsonLiteral, writeJsonLiteral } from "./framework/lint/source-scan.js";
|
package/types/geometry.d.ts
CHANGED
|
@@ -86,6 +86,8 @@ export interface PathProfileBuilder {
|
|
|
86
86
|
lineTo(to: Point2): PathProfileBuilder;
|
|
87
87
|
/** A circular arc to `to` passing through `via`. */
|
|
88
88
|
arcTo(to: Point2, via: Point2): PathProfileBuilder;
|
|
89
|
+
/** A circular arc to `to` of radius `r`; `via` is computed from the current point. */
|
|
90
|
+
arcTo(to: Point2, arc: { r: number; sweep?: "ccw" | "cw"; large?: boolean }): PathProfileBuilder;
|
|
89
91
|
/** A cubic Bézier to `to` with control points `c1`/`c2`. */
|
|
90
92
|
cubicTo(to: Point2, c1: Point2, c2: Point2): PathProfileBuilder;
|
|
91
93
|
/** Close the contour and return it. Needs at least one segment. */
|
package/types/index.d.ts
CHANGED
|
@@ -199,6 +199,17 @@ export interface MountOptions {
|
|
|
199
199
|
* part cannot support is dropped, never fatal.
|
|
200
200
|
*/
|
|
201
201
|
viewerState?: ViewerState | null;
|
|
202
|
+
/**
|
|
203
|
+
* A previous mount's `runtime.getPanelState()`: the transient state of the
|
|
204
|
+
* part's custom controls (a selected tile), keyed by param. Same remount
|
|
205
|
+
* story as `viewerState`; omit on a first mount. Never persisted by partforge.
|
|
206
|
+
*/
|
|
207
|
+
panelState?: PanelState | null;
|
|
208
|
+
/**
|
|
209
|
+
* The part's own source tree as text, for custom controls' `host.file(path)`.
|
|
210
|
+
* Omit and `host.file` answers null.
|
|
211
|
+
*/
|
|
212
|
+
files?: Record<string, string>;
|
|
202
213
|
/**
|
|
203
214
|
* A provider backing every `type: "font"` control in the part. partforge
|
|
204
215
|
* ships none — without one, a font control renders as a plain URL field.
|
|
@@ -256,6 +267,57 @@ export interface ViewerState {
|
|
|
256
267
|
cutaway: CutawayState | null;
|
|
257
268
|
}
|
|
258
269
|
|
|
270
|
+
/** Custom controls' transient state, keyed by the param each control owns. Plain JSON. */
|
|
271
|
+
export type PanelState = Record<string, Record<string, unknown>>;
|
|
272
|
+
|
|
273
|
+
export interface PanelError {
|
|
274
|
+
/** The param the failing control owns. */
|
|
275
|
+
key: string;
|
|
276
|
+
label: string;
|
|
277
|
+
phase: "create" | "update" | "event" | "dispose" | "state";
|
|
278
|
+
message: string;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** What a `type: "custom"` control's `widget(host)` receives. */
|
|
282
|
+
export interface CustomControlHost {
|
|
283
|
+
/** The slot to draw into, inside the rail. */
|
|
284
|
+
el: HTMLElement;
|
|
285
|
+
doc: Document;
|
|
286
|
+
/** The param this control owns. */
|
|
287
|
+
key: string;
|
|
288
|
+
/** A structured clone of the current value of `key` (default: the owned key). */
|
|
289
|
+
get(key?: string): unknown;
|
|
290
|
+
/**
|
|
291
|
+
* Replace a value. Refused (throws TypeError) for a key the control does not
|
|
292
|
+
* own or a value outside the contract (owned key: a JSON value; `keys`: a
|
|
293
|
+
* scalar). Stores a clone, schedules a rebuild, and commits unless
|
|
294
|
+
* `commit: false` — then call `commit()` when the gesture ends.
|
|
295
|
+
*/
|
|
296
|
+
set(value: unknown, opts?: { key?: string; commit?: boolean }): void;
|
|
297
|
+
commit(keys?: string[]): void;
|
|
298
|
+
/** The latest `derive()` output. */
|
|
299
|
+
derived: Record<string, unknown>;
|
|
300
|
+
/** Transient JSON state that survives a remount; never a param. */
|
|
301
|
+
state: Record<string, unknown>;
|
|
302
|
+
setState(patch: Record<string, unknown>): void;
|
|
303
|
+
/** Mount built-in controls bound at `path` inside the owned value. Returns a disposer. */
|
|
304
|
+
controls(container: HTMLElement, controls: import("./part.js").PanelEntry[], opts?: { path?: string }): () => void;
|
|
305
|
+
/** Element builder; SVG tags get the SVG namespace, `on*` attrs become listeners. */
|
|
306
|
+
h(tag: string, attrs?: Record<string, unknown>, ...children: unknown[]): Element;
|
|
307
|
+
/** Parse an SVG string to its root element, or null. */
|
|
308
|
+
svg(text: string): SVGSVGElement | null;
|
|
309
|
+
/** Render a partforge-vector document to an inline `<svg>`, or null. */
|
|
310
|
+
svgFromVector(doc: unknown): SVGSVGElement | null;
|
|
311
|
+
/** Text of one of the part's own files, by path or `pfc-tree://` token, or null. */
|
|
312
|
+
file(pathOrToken: string): string | null;
|
|
313
|
+
readonly disabled: boolean;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export interface CustomControlInstance {
|
|
317
|
+
update?(ctx: { reason: "sync" | "derived" | "restore"; disabled: boolean }): void;
|
|
318
|
+
dispose?(): void;
|
|
319
|
+
}
|
|
320
|
+
|
|
259
321
|
export interface ExportPartsOptions {
|
|
260
322
|
/** Sub-part names, as `listExportableParts()` reports them. */
|
|
261
323
|
parts: string[];
|
|
@@ -472,6 +534,10 @@ export interface PartRuntime {
|
|
|
472
534
|
* view-cube click, Reframe, or an animation cue).
|
|
473
535
|
*/
|
|
474
536
|
getViewerState(): ViewerState;
|
|
537
|
+
/** Every custom control's non-empty transient state, keyed by param. Hand back as `panelState`. */
|
|
538
|
+
getPanelState(): PanelState;
|
|
539
|
+
/** What custom controls reported failing this mount, in order. */
|
|
540
|
+
getPanelErrors(): PanelError[];
|
|
475
541
|
/**
|
|
476
542
|
* Subscribe to WebGL context loss — i.e. the GPU or the OS gave up — so a host
|
|
477
543
|
* can say so rather than showing a dead canvas. The listener takes no
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// partforge/panel-values — the value contract of a `type: "custom"` control.
|
|
2
|
+
|
|
3
|
+
export const CUSTOM_VALUE_MAX_BYTES: 16384;
|
|
4
|
+
export const CUSTOM_VALUE_MAX_DEPTH: 8;
|
|
5
|
+
|
|
6
|
+
export interface JsonValueLimits { maxBytes?: number; maxDepth?: number }
|
|
7
|
+
|
|
8
|
+
/** A string, boolean, finite number, or an array / plain object of those. */
|
|
9
|
+
export type JsonValue = string | number | boolean | JsonValue[] | { [key: string]: JsonValue };
|
|
10
|
+
|
|
11
|
+
/** Why `v` is not a JSON value within the caps (a fragment such as `is null at tiles[2].h`), or null. */
|
|
12
|
+
export function jsonValueProblem(v: unknown, limits?: JsonValueLimits): string | null;
|
|
13
|
+
export function isJsonValue(v: unknown, limits?: JsonValueLimits): v is JsonValue;
|
|
14
|
+
|
|
15
|
+
/** Parse an array/object literal's source text (bare or quoted keys, trailing commas, JS string escapes). Null on anything else. */
|
|
16
|
+
export function readJsonLiteral(text: string): { value: JsonValue } | null;
|
|
17
|
+
/** Source text for a value: compact under 80 chars, else indented JSON relative to `indent`. */
|
|
18
|
+
export function writeJsonLiteral(value: JsonValue, opts?: { indent?: string }): string;
|
package/types/part.d.ts
CHANGED
|
@@ -58,7 +58,7 @@ export interface PartMeta {
|
|
|
58
58
|
export type ControlKind = "slider" | "number" | "text" | "textarea";
|
|
59
59
|
|
|
60
60
|
/** Every control type the panel can render. */
|
|
61
|
-
export type ControlType = "slider" | "number" | "text" | "textarea" | "checkbox" | "select" | "radio";
|
|
61
|
+
export type ControlType = "slider" | "number" | "text" | "textarea" | "checkbox" | "select" | "radio" | "font" | "image" | "vector" | "custom";
|
|
62
62
|
|
|
63
63
|
/** A declarative visibility condition, evaluated against raw parameters. */
|
|
64
64
|
export type WhenCondition =
|
|
@@ -94,6 +94,15 @@ export interface PanelControlEntry {
|
|
|
94
94
|
snap?: boolean;
|
|
95
95
|
/** slider: [lo, hi] band drawn on the track; outside it the value box takes a warning tint. */
|
|
96
96
|
recommended?: [number, number];
|
|
97
|
+
/**
|
|
98
|
+
* custom: the widget function. Called once per mount with a host object
|
|
99
|
+
* (see `CustomControlHost` in index.d.ts); draws into `host.el`. The key
|
|
100
|
+
* this control owns may hold a JSON value (arrays and plain objects of
|
|
101
|
+
* primitives, ≤16 KB, depth ≤8).
|
|
102
|
+
*/
|
|
103
|
+
widget?: (host: import("./index.js").CustomControlHost) => import("./index.js").CustomControlInstance | void;
|
|
104
|
+
/** custom: further scalar params the widget may write besides `key`. */
|
|
105
|
+
keys?: string[];
|
|
97
106
|
hidden?: boolean;
|
|
98
107
|
when?: WhenCondition;
|
|
99
108
|
whenFalse?: "disable";
|