vintage-frames 0.5.0 → 0.5.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.
- package/custom-elements.json +38 -1
- package/dist/components/vf-container.d.ts +22 -2
- package/dist/components/vf-container.js +21 -14
- package/dist/components/vf-menu-bar.js +30 -25
- package/dist/components/vf-window.js +44 -42
- package/dist/index.d.ts +8 -1
- package/dist/index.js +57 -56
- package/dist/styles/base.d.ts +2 -0
- package/dist/styles/recipes/rule.d.ts +41 -0
- package/dist/styles/recipes/rule.js +32 -0
- package/docs/SPEC.md +6 -5
- package/editor/vscode.html-custom-data.json +6 -1
- package/editor/web-types.json +12 -2
- package/package.json +3 -1
package/custom-elements.json
CHANGED
|
@@ -1974,7 +1974,7 @@
|
|
|
1974
1974
|
"declarations": [
|
|
1975
1975
|
{
|
|
1976
1976
|
"kind": "class",
|
|
1977
|
-
"description": "`<vf-container>` — a box that is nothing but its declared size.\n\nThe kit's positioned-placement story (src/position.ts) ends with one line of\nCSS it can't write for you: children placed with `top`/`left` need a\npositioned ancestor, and while every kit container is one — a window body, a\nstack, a fieldset — a region of *your own* needs `position: relative` in a\nstylesheet. This component is that region as an element: declare `width` and\n`height` in whole system px, slot anything into it, place children against\nits origin. A DITL's enclosing rectangle, with nothing drawn in it.\n\n```html\n<vf-container width=\"200\" height=\"120\">\n <vf-icon label=\"System\" width=\"64\" top=\"8\" left=\"12\">…</vf-icon>\n <vf-icon label=\"Finder\" width=\"64\" top=\"8\" left=\"104\">…</vf-icon>\n</vf-container>\n```\n\nThe rectangle is the whole API — `width`/`height` here, plus the `top`/`left`\npair nearly every component takes (VfPositioned), so a container is\nitself placeable: inside a window, a desktop, or another container, at whole\nsystem px that keep its box on the device-pixel grid by construction.\n\n**It is not a `vf-stack`.** The stack is a flexbox with opinions — it\ndistributes children along an axis, compiles `fill-width`/`fill-height` into\nflex, defaults a cross-axis alignment. This box has no layout opinion at\nall: in-flow children get normal flow, placed children get a coordinate\nsystem, and that is the whole API. Reach for it when the stack's opinions\nare the thing in the way — a field of placed icons, a fixed stage for\nabsolutely-positioned art, a consumer's own composition that brings its\nlayout with it.\n\n**The declared size is the layout.** `width`/`height` land on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)` (VfSized), so the box scales\nwith the display and sits on the device-pixel grid by construction. Content\nthat outgrows the box overflows it rather than growing it — the number is\nthe layout, and content that doesn't fit is a number to raise. Leave a\ndimension off and that axis shrink-wraps: `fit-content`, not the parent's\nwidth, because a layout box that silently claimed a size nobody declared\nwould be inventing one (the `vf-stack` rule, held here too).\n\n**It paints nothing and means nothing** — unless `pattern`
|
|
1977
|
+
"description": "`<vf-container>` — a box that is nothing but its declared size.\n\nThe kit's positioned-placement story (src/position.ts) ends with one line of\nCSS it can't write for you: children placed with `top`/`left` need a\npositioned ancestor, and while every kit container is one — a window body, a\nstack, a fieldset — a region of *your own* needs `position: relative` in a\nstylesheet. This component is that region as an element: declare `width` and\n`height` in whole system px, slot anything into it, place children against\nits origin. A DITL's enclosing rectangle, with nothing drawn in it.\n\n```html\n<vf-container width=\"200\" height=\"120\">\n <vf-icon label=\"System\" width=\"64\" top=\"8\" left=\"12\">…</vf-icon>\n <vf-icon label=\"Finder\" width=\"64\" top=\"8\" left=\"104\">…</vf-icon>\n</vf-container>\n```\n\nThe rectangle is the whole API — `width`/`height` here, plus the `top`/`left`\npair nearly every component takes (VfPositioned), so a container is\nitself placeable: inside a window, a desktop, or another container, at whole\nsystem px that keep its box on the device-pixel grid by construction.\n\n**It is not a `vf-stack`.** The stack is a flexbox with opinions — it\ndistributes children along an axis, compiles `fill-width`/`fill-height` into\nflex, defaults a cross-axis alignment. This box has no layout opinion at\nall: in-flow children get normal flow, placed children get a coordinate\nsystem, and that is the whole API. Reach for it when the stack's opinions\nare the thing in the way — a field of placed icons, a fixed stage for\nabsolutely-positioned art, a consumer's own composition that brings its\nlayout with it.\n\n**The declared size is the layout.** `width`/`height` land on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)` (VfSized), so the box scales\nwith the display and sits on the device-pixel grid by construction. Content\nthat outgrows the box overflows it rather than growing it — the number is\nthe layout, and content that doesn't fit is a number to raise. Leave a\ndimension off and that axis shrink-wraps: `fit-content`, not the parent's\nwidth, because a layout box that silently claimed a size nobody declared\nwould be inventing one (the `vf-stack` rule, held here too).\n\n**It paints nothing and means nothing** — unless `pattern` or `rule` say\nwhat to paint. No role, keyboard behavior or selection; what it holds\ndecides what it is.\n\n**`rule` draws the 1px rule on the edges it names** — `rule=\"bottom\"` is\nthe menu bar's anatomy (the box's rows over one row of ink), `rule=\"top\"`\na status strip's, all four a framed box. The rule is the box's own border\n(vfRule), inside the declared size, so a 24px `rule=\"bottom\"`\nstrip is 23 rows of box over the line; content, `fill-width` children and\nplaced children begin inside it, as a rectangle's interior begins inside\nFrameRect's line.\n\n**`pattern` fills the box with a 1-bit pattern**: one of the 38 standard\nMacPaint patterns by name (`pattern=\"bricks\"`, `pattern=\"gray-50\"` —\ndocs/PATTERNS.md has the table), or sixteen hex digits stating a custom\n8×8 pattern row by row, the way a PAT resource did. It is painted as the\nbox's own background — black ink on a `--vf-white` ground, anchored at\nthe box's top-left corner, under the content — by the same whole-surface\nraster mechanism as the desktop dither, so it is 1-bit at every density\nand zoom (src/pattern-fill.ts). A declared `width`/`height` sizes the\nraster exactly; an undeclared axis (`fill-width`, a shrink-wrapped\nheight) is measured. Under forced colors the pattern goes flat Canvas.\n\n**It holds its box on the device-pixel grid** — with a `GridSnapController`.\nA container's box is itself the consumer's coordinate system, including for\nnon-`vf` content that cannot correct itself, so the box is the thing to\nhold on the grid. The shadow box below owns the `position: relative` anchor\nand the `vf-snap` class together, so the correction\nmoves the whole coordinate system — everything placed against it rides\nalong instead of being re-corrected child by child. (`vf-stack` shipped\nwithout a controller on the theory that slotted `vf-*` children correct\ntheir own origins; this component is where that theory's gap — consumer\ncontent — became visible, and the stack has since adopted the same\narrangement.)\n\nLike the stack it is **typographically transparent**: `vfBase`'s chrome\ndress is returned to `inherit` on the host, so wrapping content in a sized\nbox changes nothing about how that content reads.\n\n`fill-width` / `fill-height` work here the way they do everywhere: read\nabout the host (be as big as *its* parent allows), and compiled for slotted\nchildren — `width: 100%` in normal flow, so a child filling the cross of a\ndeclared box needs no stylesheet. A height fill needs a declared `height`\nto resolve against; with none it is inert, not an error.",
|
|
1978
1978
|
"name": "VfContainer",
|
|
1979
1979
|
"slots": [
|
|
1980
1980
|
{
|
|
@@ -2012,6 +2012,35 @@
|
|
|
2012
2012
|
"default": "false",
|
|
2013
2013
|
"description": "One warning per element for an unrecognized `pattern`, not per render."
|
|
2014
2014
|
},
|
|
2015
|
+
{
|
|
2016
|
+
"kind": "field",
|
|
2017
|
+
"name": "rule",
|
|
2018
|
+
"type": {
|
|
2019
|
+
"text": "string | null | undefined"
|
|
2020
|
+
},
|
|
2021
|
+
"description": "The 1px rule on the box's edges: edge names separated by spaces —\n`\"bottom\"`, `\"top bottom\"`, up to all four in any order. Drawn as the\nbox's own border inside the declared size, in `--vf-black`, scaled with\nthe display like every kit frame's border; content and placed children\nbegin inside it.\nUnset, no rule; a value naming anything but an edge draws none and\nwarns once.",
|
|
2022
|
+
"attribute": "rule"
|
|
2023
|
+
},
|
|
2024
|
+
{
|
|
2025
|
+
"kind": "field",
|
|
2026
|
+
"name": "_rule",
|
|
2027
|
+
"type": {
|
|
2028
|
+
"text": "RuleEdge[]"
|
|
2029
|
+
},
|
|
2030
|
+
"privacy": "private",
|
|
2031
|
+
"default": "[]",
|
|
2032
|
+
"description": "`rule`, resolved — the edges drawn; empty draws nothing."
|
|
2033
|
+
},
|
|
2034
|
+
{
|
|
2035
|
+
"kind": "field",
|
|
2036
|
+
"name": "#warnedRule",
|
|
2037
|
+
"privacy": "private",
|
|
2038
|
+
"type": {
|
|
2039
|
+
"text": "boolean"
|
|
2040
|
+
},
|
|
2041
|
+
"default": "false",
|
|
2042
|
+
"description": "One warning per element for an unrecognized `rule`, not per render."
|
|
2043
|
+
},
|
|
2015
2044
|
{
|
|
2016
2045
|
"kind": "field",
|
|
2017
2046
|
"name": "box",
|
|
@@ -2108,6 +2137,14 @@
|
|
|
2108
2137
|
"description": "A 1-bit fill for the box: a library pattern by name (`bricks`,\n`gray-50`, … — the 38 standard MacPaint patterns, docs/PATTERNS.md) or\nsixteen hex digits stating a custom 8×8 pattern row by row, bit 7 the\nleftmost pixel, 1 = ink (`\"DD 77 DD 77 DD 77 DD 77\"`). Painted in black\non a `--vf-white` ground under the content, anchored at the box's\ntop-left. Unset, the container paints nothing; an unrecognized value\npaints nothing and warns once.",
|
|
2109
2138
|
"fieldName": "pattern"
|
|
2110
2139
|
},
|
|
2140
|
+
{
|
|
2141
|
+
"name": "rule",
|
|
2142
|
+
"type": {
|
|
2143
|
+
"text": "string | null | undefined"
|
|
2144
|
+
},
|
|
2145
|
+
"description": "The 1px rule on the box's edges: edge names separated by spaces —\n`\"bottom\"`, `\"top bottom\"`, up to all four in any order. Drawn as the\nbox's own border inside the declared size, in `--vf-black`, scaled with\nthe display like every kit frame's border; content and placed children\nbegin inside it.\nUnset, no rule; a value naming anything but an edge draws none and\nwarns once.",
|
|
2146
|
+
"fieldName": "rule"
|
|
2147
|
+
},
|
|
2111
2148
|
{
|
|
2112
2149
|
"name": "width",
|
|
2113
2150
|
"type": {
|
|
@@ -41,10 +41,18 @@ declare const VfContainer_base: (new (...args: any[]) => import("../size.js").Vf
|
|
|
41
41
|
* width, because a layout box that silently claimed a size nobody declared
|
|
42
42
|
* would be inventing one (the `vf-stack` rule, held here too).
|
|
43
43
|
*
|
|
44
|
-
* **It paints nothing and means nothing** — unless `pattern`
|
|
45
|
-
* paint. No
|
|
44
|
+
* **It paints nothing and means nothing** — unless `pattern` or `rule` say
|
|
45
|
+
* what to paint. No role, keyboard behavior or selection; what it holds
|
|
46
46
|
* decides what it is.
|
|
47
47
|
*
|
|
48
|
+
* **`rule` draws the 1px rule on the edges it names** — `rule="bottom"` is
|
|
49
|
+
* the menu bar's anatomy (the box's rows over one row of ink), `rule="top"`
|
|
50
|
+
* a status strip's, all four a framed box. The rule is the box's own border
|
|
51
|
+
* ({@link vfRule}), inside the declared size, so a 24px `rule="bottom"`
|
|
52
|
+
* strip is 23 rows of box over the line; content, `fill-width` children and
|
|
53
|
+
* placed children begin inside it, as a rectangle's interior begins inside
|
|
54
|
+
* FrameRect's line.
|
|
55
|
+
*
|
|
48
56
|
* **`pattern` fills the box with a 1-bit pattern**: one of the 38 standard
|
|
49
57
|
* MacPaint patterns by name (`pattern="bricks"`, `pattern="gray-50"` —
|
|
50
58
|
* docs/PATTERNS.md has the table), or sixteen hex digits stating a custom
|
|
@@ -97,6 +105,18 @@ export declare class VfContainer extends VfContainer_base {
|
|
|
97
105
|
pattern?: string | null;
|
|
98
106
|
/** `pattern`, resolved — what the fill paints; null paints nothing. */
|
|
99
107
|
private _pattern;
|
|
108
|
+
/**
|
|
109
|
+
* The 1px rule on the box's edges: edge names separated by spaces —
|
|
110
|
+
* `"bottom"`, `"top bottom"`, up to all four in any order. Drawn as the
|
|
111
|
+
* box's own border inside the declared size, in `--vf-black`, scaled with
|
|
112
|
+
* the display like every kit frame's border; content and placed children
|
|
113
|
+
* begin inside it.
|
|
114
|
+
* Unset, no rule; a value naming anything but an edge draws none and
|
|
115
|
+
* warns once.
|
|
116
|
+
*/
|
|
117
|
+
rule?: string | null;
|
|
118
|
+
/** `rule`, resolved — the edges drawn; empty draws nothing. */
|
|
119
|
+
private _rule;
|
|
100
120
|
/** The shadow box the fill paints on; exists from the first render. */
|
|
101
121
|
private readonly box;
|
|
102
122
|
/**
|
|
@@ -4,15 +4,16 @@ import n from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js
|
|
|
4
4
|
import { VfPositioned as r } from "../position.js";
|
|
5
5
|
import { vfBase as i } from "../styles/recipes/host.js";
|
|
6
6
|
import { parsePattern as a } from "../patterns.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
7
|
+
import { parseRule as o, ruleClasses as s, vfRule as c } from "../styles/recipes/rule.js";
|
|
8
|
+
import { PatternFillController as l, vfPatternFill as u } from "../pattern-fill.js";
|
|
9
|
+
import { GridSnapController as d } from "../grid-snap.js";
|
|
10
|
+
import { VfSized as f } from "../size.js";
|
|
11
|
+
import { LitElement as p, css as m, html as h } from "lit";
|
|
12
|
+
import { property as g, query as _ } from "lit/decorators.js";
|
|
12
13
|
//#region src/components/vf-container.ts
|
|
13
|
-
var
|
|
14
|
+
var v = class extends f(r(p)) {
|
|
14
15
|
constructor(...e) {
|
|
15
|
-
super(...e), this._pattern = null, this.#e = !1, this.scale = new t(this), this.gridSnap = new
|
|
16
|
+
super(...e), this._pattern = null, this.#e = !1, this._rule = [], this.#t = !1, this.scale = new t(this), this.gridSnap = new d(this), this.patternFill = new l(this, {
|
|
16
17
|
getBox: () => this.box,
|
|
17
18
|
getPattern: () => this._pattern,
|
|
18
19
|
getSize: () => ({
|
|
@@ -24,8 +25,9 @@ var h = class extends l(r(u)) {
|
|
|
24
25
|
static {
|
|
25
26
|
this.styles = [
|
|
26
27
|
i,
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
u,
|
|
29
|
+
c,
|
|
30
|
+
m`
|
|
29
31
|
:host {
|
|
30
32
|
display: block;
|
|
31
33
|
/* Shrink-wrap an undeclared axis rather than claim the parent's
|
|
@@ -92,17 +94,22 @@ var h = class extends l(r(u)) {
|
|
|
92
94
|
];
|
|
93
95
|
}
|
|
94
96
|
#e;
|
|
97
|
+
#t;
|
|
95
98
|
willUpdate(e) {
|
|
96
|
-
super.willUpdate(e), e.has("pattern") && (this._pattern = a(this.pattern), this._pattern === null && this.pattern?.trim() && !this.#e && (this.#e = !0, console.warn(`vf-container: unknown pattern "${this.pattern}" — a library name (docs/PATTERNS.md) or sixteen hex digits. Painting nothing.`)))
|
|
99
|
+
if (super.willUpdate(e), e.has("pattern") && (this._pattern = a(this.pattern), this._pattern === null && this.pattern?.trim() && !this.#e && (this.#e = !0, console.warn(`vf-container: unknown pattern "${this.pattern}" — a library name (docs/PATTERNS.md) or sixteen hex digits. Painting nothing.`))), e.has("rule")) {
|
|
100
|
+
let e = o(this.rule);
|
|
101
|
+
this._rule = e ?? [], e === null && !this.#t && (this.#t = !0, console.warn(`vf-container: unknown rule "${this.rule}" — edge names (top, right, bottom, left) separated by spaces. Drawing none.`));
|
|
102
|
+
}
|
|
97
103
|
}
|
|
98
104
|
render() {
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
let e = s(this._rule);
|
|
106
|
+
return h`<div
|
|
107
|
+
class="vf-snap box vf-pattern-fill${this._pattern ? " vf-patterned" : ""}${e ? ` ${e}` : ""}"
|
|
101
108
|
>
|
|
102
109
|
<slot></slot>
|
|
103
110
|
</div>`;
|
|
104
111
|
}
|
|
105
112
|
};
|
|
106
|
-
n([
|
|
113
|
+
n([g()], v.prototype, "pattern", void 0), n([g()], v.prototype, "rule", void 0), n([_(".box")], v.prototype, "box", void 0), v = n([e("vf-container")], v);
|
|
107
114
|
//#endregion
|
|
108
|
-
export {
|
|
115
|
+
export { v as VfContainer };
|
|
@@ -3,17 +3,18 @@ import { ScaleController as t } from "../scale.js";
|
|
|
3
3
|
import n from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
|
|
4
4
|
import { VfPositioned as r } from "../position.js";
|
|
5
5
|
import { vfBase as i } from "../styles/recipes/host.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
6
|
+
import { vfRule as a } from "../styles/recipes/rule.js";
|
|
7
|
+
import { GridSnapController as o } from "../grid-snap.js";
|
|
8
|
+
import { DocumentListenersController as s } from "../document-listeners.js";
|
|
9
|
+
import { SCREEN_CORNER as c, steppedCornerClip as l } from "../pixel-frame.js";
|
|
10
|
+
import { TypeAheadBuffer as u } from "../type-ahead.js";
|
|
11
|
+
import { MenuPressController as d } from "../menu-press.js";
|
|
12
|
+
import { LitElement as f, css as p, html as m, unsafeCSS as h } from "lit";
|
|
13
|
+
import { property as g, queryAssignedElements as _ } from "lit/decorators.js";
|
|
13
14
|
//#region src/components/vf-menu-bar.ts
|
|
14
|
-
var
|
|
15
|
+
var v = class extends r(f) {
|
|
15
16
|
constructor(...e) {
|
|
16
|
-
super(...e), this.scale = new t(this), this.gridSnap = new
|
|
17
|
+
super(...e), this.scale = new t(this), this.gridSnap = new o(this), this.label = "", this.rounded = !1, this.shortcuts = !1, this.#e = null, this.#t = this.attachInternals(), this.#n = new u(), this.#r = null, this.#i = new s(this, () => [[
|
|
17
18
|
document,
|
|
18
19
|
"pointerdown",
|
|
19
20
|
this.#h,
|
|
@@ -23,7 +24,7 @@ var _ = class extends r(d) {
|
|
|
23
24
|
"keydown",
|
|
24
25
|
this.#g,
|
|
25
26
|
!0
|
|
26
|
-
]]), this.#a = new
|
|
27
|
+
]]), this.#a = new d(this, {
|
|
27
28
|
menus: () => this._menus,
|
|
28
29
|
open: (e) => this.#p(e),
|
|
29
30
|
close: () => this.#m()
|
|
@@ -101,7 +102,10 @@ var _ = class extends r(d) {
|
|
|
101
102
|
};
|
|
102
103
|
}
|
|
103
104
|
static {
|
|
104
|
-
this.styles = [
|
|
105
|
+
this.styles = [
|
|
106
|
+
i,
|
|
107
|
+
a,
|
|
108
|
+
p`
|
|
105
109
|
:host {
|
|
106
110
|
display: block;
|
|
107
111
|
/* 19 white system px over the 1px black rule — Menus.png's bar strip
|
|
@@ -112,9 +116,9 @@ var _ = class extends r(d) {
|
|
|
112
116
|
}
|
|
113
117
|
.bar {
|
|
114
118
|
/* Paint lives here rather than on the host so it rides the snap offset
|
|
115
|
-
(see .vf-snap in base.ts); border-box sizing keeps the geometry.
|
|
119
|
+
(see .vf-snap in base.ts); border-box sizing keeps the geometry. The
|
|
120
|
+
rule is vfRule's vf-rule-bottom on the element. */
|
|
116
121
|
background: var(--vf-white, #fff);
|
|
117
|
-
border-bottom: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
|
|
118
122
|
display: flex;
|
|
119
123
|
align-items: stretch;
|
|
120
124
|
height: 100%;
|
|
@@ -146,20 +150,21 @@ var _ = class extends r(d) {
|
|
|
146
150
|
content: '';
|
|
147
151
|
position: absolute;
|
|
148
152
|
top: 0;
|
|
149
|
-
width: calc(var(--vf-scale, 1) * ${
|
|
150
|
-
height: calc(var(--vf-scale, 1) * ${
|
|
153
|
+
width: calc(var(--vf-scale, 1) * ${c[0]}px);
|
|
154
|
+
height: calc(var(--vf-scale, 1) * ${c.length}px);
|
|
151
155
|
background: var(--vf-black, #000);
|
|
152
156
|
pointer-events: none;
|
|
153
157
|
}
|
|
154
158
|
:host([rounded]) .bar::before {
|
|
155
159
|
left: 0;
|
|
156
|
-
clip-path: ${
|
|
160
|
+
clip-path: ${h(l(c, "left"))};
|
|
157
161
|
}
|
|
158
162
|
:host([rounded]) .bar::after {
|
|
159
163
|
right: 0;
|
|
160
|
-
clip-path: ${
|
|
164
|
+
clip-path: ${h(l(c, "right"))};
|
|
161
165
|
}
|
|
162
|
-
`
|
|
166
|
+
`
|
|
167
|
+
];
|
|
163
168
|
}
|
|
164
169
|
#e;
|
|
165
170
|
#t;
|
|
@@ -181,8 +186,8 @@ var _ = class extends r(d) {
|
|
|
181
186
|
e.has("label") && (this.#t.ariaLabel = this.label || null);
|
|
182
187
|
}
|
|
183
188
|
render() {
|
|
184
|
-
return
|
|
185
|
-
<div class="bar vf-snap" part="bar" role="presentation">
|
|
189
|
+
return m`
|
|
190
|
+
<div class="bar vf-snap vf-rule-bottom" part="bar" role="presentation">
|
|
186
191
|
<slot @slotchange=${this.#s}></slot>
|
|
187
192
|
</div>
|
|
188
193
|
`;
|
|
@@ -225,15 +230,15 @@ var _ = class extends r(d) {
|
|
|
225
230
|
r = n < 0 ? e === 1 ? 0 : t.length - 1 : (n + e + t.length) % t.length, t[r]?.focus();
|
|
226
231
|
}
|
|
227
232
|
};
|
|
228
|
-
n([
|
|
233
|
+
n([g()], v.prototype, "label", void 0), n([g({
|
|
229
234
|
type: Boolean,
|
|
230
235
|
reflect: !0
|
|
231
|
-
})],
|
|
236
|
+
})], v.prototype, "rounded", void 0), n([g({
|
|
232
237
|
type: Boolean,
|
|
233
238
|
reflect: !0
|
|
234
|
-
})],
|
|
239
|
+
})], v.prototype, "shortcuts", void 0), n([_({
|
|
235
240
|
selector: "vf-menu",
|
|
236
241
|
flatten: !0
|
|
237
|
-
})],
|
|
242
|
+
})], v.prototype, "_menus", void 0), v = n([e("vf-menu-bar")], v);
|
|
238
243
|
//#endregion
|
|
239
|
-
export {
|
|
244
|
+
export { v as VfMenuBar };
|
|
@@ -6,26 +6,27 @@ import { PlacementController as s, VfPositioned as c, warnMovableContract as l }
|
|
|
6
6
|
import { vfBase as u } from "../styles/recipes/host.js";
|
|
7
7
|
import { DOT_MOTIF as d, DOT_RECTS as f, DOT_SPAN as p, vfDots as m, vfStripes as h } from "../styles/recipes/pattern.js";
|
|
8
8
|
import { vfChromeFrame as g } from "../styles/recipes/surface.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
9
|
+
import { vfRule as _ } from "../styles/recipes/rule.js";
|
|
10
|
+
import { vfTitleBar as v, vfWindowWidgets as y } from "../styles/recipes/title-bar.js";
|
|
11
|
+
import { vfFocus as b } from "../styles/recipes/focus.js";
|
|
12
|
+
import { TileRasterCache as x, patternOverride as S, tileGrid as C, vfTileGrid as w } from "../tile-grid.js";
|
|
13
|
+
import { GridSnapController as T } from "../grid-snap.js";
|
|
14
|
+
import { VfSized as E } from "../size.js";
|
|
15
|
+
import { DragController as D } from "../drag.js";
|
|
16
|
+
import { TitleCenterController as O, chromeTitleBar as k, closeBox as A, widgetLabel as j, zoomBox as M } from "../chrome.js";
|
|
16
17
|
import "./vf-scroll-area.js";
|
|
17
|
-
import { LitElement as
|
|
18
|
-
import { property as
|
|
18
|
+
import { LitElement as N, css as P, html as F, nothing as I } from "lit";
|
|
19
|
+
import { property as L, state as R } from "lit/decorators.js";
|
|
19
20
|
//#region src/components/vf-window.ts
|
|
20
|
-
var
|
|
21
|
+
var z = 80, B = 54, V = 24, H = 8, U = class extends E(c(N)) {
|
|
21
22
|
constructor(...e) {
|
|
22
|
-
super(...e), this.heading = "", this.active = !0, this.closable = !0, this.zoomable = !1, this.movable = !1, this.resizable = !1, this.flush = !1, this._hasStatus = !1, this.scale = new n(this), this.gridSnap = new
|
|
23
|
+
super(...e), this.heading = "", this.active = !0, this.closable = !0, this.zoomable = !1, this.movable = !1, this.resizable = !1, this.flush = !1, this._hasStatus = !1, this.scale = new n(this), this.gridSnap = new T(this), this.titleCenter = new O(this), this._dotsPattern = "", this.#e = new x(), this.#t = (e, t, n) => {
|
|
23
24
|
let r = a(this.offsetWidth, this);
|
|
24
25
|
return {
|
|
25
|
-
x: Math.min(Math.max(e,
|
|
26
|
-
y: Math.min(Math.max(t, 0), Math.max(0, n.height -
|
|
26
|
+
x: Math.min(Math.max(e, V - r), n.width - V),
|
|
27
|
+
y: Math.min(Math.max(t, 0), Math.max(0, n.height - V))
|
|
27
28
|
};
|
|
28
|
-
}, this._placement = new s(this, (e, t, n) => this.#t(e, t, n)), this._drag = new
|
|
29
|
+
}, this._placement = new s(this, (e, t, n) => this.#t(e, t, n)), this._drag = new D(this, {
|
|
29
30
|
onDragStart: (e) => !this.movable || e.button !== 0 || e.composedPath().some((e) => e instanceof HTMLElement && e.classList.contains("box")) ? null : (this.#i(), this._placement.seed()),
|
|
30
31
|
onDrag: (e, t) => this._placement.moveTo(e, t)
|
|
31
32
|
}), this._resizeState = null, this.#n = !1, this.#r = !1;
|
|
@@ -35,12 +36,13 @@ var R = 80, z = 54, B = 24, V = 8, H = class extends T(c(M)) {
|
|
|
35
36
|
u,
|
|
36
37
|
h,
|
|
37
38
|
m,
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
w,
|
|
40
|
+
b,
|
|
40
41
|
g,
|
|
41
42
|
_,
|
|
42
43
|
v,
|
|
43
|
-
|
|
44
|
+
y,
|
|
45
|
+
P`
|
|
44
46
|
:host {
|
|
45
47
|
display: block;
|
|
46
48
|
position: relative;
|
|
@@ -221,7 +223,7 @@ var R = 80, z = 54, B = 24, V = 8, H = class extends T(c(M)) {
|
|
|
221
223
|
display: flex;
|
|
222
224
|
align-items: center;
|
|
223
225
|
height: calc(var(--vf-scale, 1) * var(--vf-status-bar-height, 15px));
|
|
224
|
-
|
|
226
|
+
/* The rule is vfRule's vf-rule-top on the element. */
|
|
225
227
|
background: var(--vf-white, #ffffff);
|
|
226
228
|
padding-inline: calc(var(--vf-scale, 1) * 6px);
|
|
227
229
|
line-height: calc(var(--vf-scale, 1) * var(--vf-line-height, 12px));
|
|
@@ -281,18 +283,18 @@ var R = 80, z = 54, B = 24, V = 8, H = class extends T(c(M)) {
|
|
|
281
283
|
}
|
|
282
284
|
#e;
|
|
283
285
|
willUpdate(e) {
|
|
284
|
-
super.willUpdate(e), this.variant === "utility" && (this._dotsPattern =
|
|
286
|
+
super.willUpdate(e), this.variant === "utility" && (this._dotsPattern = S(this, "--vf-dots-pattern"));
|
|
285
287
|
}
|
|
286
288
|
_dotsTexture() {
|
|
287
289
|
if (this.width == null) return;
|
|
288
290
|
let e = Math.max(1, this.width - 2);
|
|
289
|
-
if (this._dotsPattern) return
|
|
291
|
+
if (this._dotsPattern) return C({
|
|
290
292
|
cols: Math.ceil(e / p),
|
|
291
293
|
rows: 1,
|
|
292
294
|
tile: p
|
|
293
295
|
});
|
|
294
|
-
let t = Math.ceil((e + d) / p) * p, n =
|
|
295
|
-
return
|
|
296
|
+
let t = Math.ceil((e + d) / p) * p, n = H + d;
|
|
297
|
+
return F`<div
|
|
296
298
|
class="vf-tile-raster"
|
|
297
299
|
style="width:${i(t)};height:${i(n)};background-image:${this.#e.for(d, d, f, t, n)}"
|
|
298
300
|
></div>`;
|
|
@@ -346,7 +348,7 @@ var R = 80, z = 54, B = 24, V = 8, H = class extends T(c(M)) {
|
|
|
346
348
|
_onGrowPointerMove(e) {
|
|
347
349
|
let t = this._resizeState;
|
|
348
350
|
if (!t || e.pointerId !== t.pointerId) return;
|
|
349
|
-
let n = r(Math.max(
|
|
351
|
+
let n = r(Math.max(z, t.baseWidth + a(e.clientX - t.startX, this)), this), i = r(Math.max(B, t.baseHeight + a(e.clientY - t.startY, this)), this);
|
|
350
352
|
(n !== this.width || i !== this.height) && (t.emitPending = !0, t.resized = !0), this.width = n, this.height = i;
|
|
351
353
|
}
|
|
352
354
|
_onGrowPointerEnd(t) {
|
|
@@ -361,34 +363,34 @@ var R = 80, z = 54, B = 24, V = 8, H = class extends T(c(M)) {
|
|
|
361
363
|
});
|
|
362
364
|
}
|
|
363
365
|
render() {
|
|
364
|
-
return
|
|
366
|
+
return F`
|
|
365
367
|
<div
|
|
366
368
|
class="vf-frame vf-snap"
|
|
367
369
|
part="frame"
|
|
368
370
|
role="group"
|
|
369
|
-
aria-labelledby=${this.heading ? "title" :
|
|
371
|
+
aria-labelledby=${this.heading ? "title" : I}
|
|
370
372
|
>
|
|
371
|
-
${
|
|
372
|
-
${this.closable ?
|
|
373
|
+
${k(this._drag, F`
|
|
374
|
+
${this.closable ? A(j("Close", this.heading), this._onCloseClick) : I}
|
|
373
375
|
<span class="vf-title" part="title" id="title">${this.heading}</span>
|
|
374
|
-
${this.zoomable ? j(
|
|
376
|
+
${this.zoomable ? M(j("Zoom", this.heading), this._onZoomClick) : I}
|
|
375
377
|
`, this.variant === "utility" ? "vf-dots" : "vf-stripes", this.variant === "utility" ? this._dotsTexture() : void 0)}
|
|
376
378
|
<div class="body" part="body">
|
|
377
|
-
${this.scrollbars ?
|
|
379
|
+
${this.scrollbars ? F`
|
|
378
380
|
<vf-scroll-area
|
|
379
381
|
class="edge-scroll"
|
|
380
382
|
axis=${this.scrollbars}
|
|
381
|
-
label=${this.heading ||
|
|
383
|
+
label=${this.heading || I}
|
|
382
384
|
exportparts="viewport"
|
|
383
385
|
>
|
|
384
386
|
<slot></slot>
|
|
385
387
|
</vf-scroll-area>
|
|
386
|
-
` :
|
|
388
|
+
` : F`<slot></slot>`}
|
|
387
389
|
</div>
|
|
388
|
-
<div class="status ${this._hasStatus ? "" : "empty"}" part="status-bar">
|
|
390
|
+
<div class="status vf-rule-top ${this._hasStatus ? "" : "empty"}" part="status-bar">
|
|
389
391
|
<slot name="status" @slotchange=${this._onStatusSlotChange}></slot>
|
|
390
392
|
</div>
|
|
391
|
-
${this.resizable ?
|
|
393
|
+
${this.resizable ? F`
|
|
392
394
|
<div
|
|
393
395
|
class="grow"
|
|
394
396
|
part="grow-box"
|
|
@@ -398,29 +400,29 @@ var R = 80, z = 54, B = 24, V = 8, H = class extends T(c(M)) {
|
|
|
398
400
|
@pointercancel=${this._onGrowPointerEnd}
|
|
399
401
|
@lostpointercapture=${this._onGrowPointerEnd}
|
|
400
402
|
></div>
|
|
401
|
-
` :
|
|
403
|
+
` : I}
|
|
402
404
|
</div>
|
|
403
405
|
`;
|
|
404
406
|
}
|
|
405
407
|
};
|
|
406
|
-
o([
|
|
408
|
+
o([L({ reflect: !0 })], U.prototype, "variant", void 0), o([L()], U.prototype, "heading", void 0), o([L({
|
|
407
409
|
type: Boolean,
|
|
408
410
|
reflect: !0
|
|
409
|
-
})],
|
|
411
|
+
})], U.prototype, "active", void 0), o([L({
|
|
410
412
|
type: Boolean,
|
|
411
413
|
reflect: !0
|
|
412
|
-
})],
|
|
414
|
+
})], U.prototype, "closable", void 0), o([L({
|
|
413
415
|
type: Boolean,
|
|
414
416
|
reflect: !0
|
|
415
|
-
})],
|
|
417
|
+
})], U.prototype, "zoomable", void 0), o([L({
|
|
416
418
|
type: Boolean,
|
|
417
419
|
reflect: !0
|
|
418
|
-
})],
|
|
420
|
+
})], U.prototype, "movable", void 0), o([L({
|
|
419
421
|
type: Boolean,
|
|
420
422
|
reflect: !0
|
|
421
|
-
})],
|
|
423
|
+
})], U.prototype, "resizable", void 0), o([L({
|
|
422
424
|
type: Boolean,
|
|
423
425
|
reflect: !0
|
|
424
|
-
})],
|
|
426
|
+
})], U.prototype, "flush", void 0), o([L({ reflect: !0 })], U.prototype, "scrollbars", void 0), o([R()], U.prototype, "_hasStatus", void 0), U = o([t("vf-window")], U);
|
|
425
427
|
//#endregion
|
|
426
|
-
export {
|
|
428
|
+
export { U as VfWindow };
|
package/dist/index.d.ts
CHANGED
|
@@ -88,8 +88,15 @@ export { CURSOR_ARROW, CURSOR_I_BEAM, CURSOR_CROSSHAIR, CURSOR_WAIT, } from './c
|
|
|
88
88
|
* `static styles` to inherit the kit's type, panel frame, window chrome, focus
|
|
89
89
|
* ring, field well and System 7 scroll-rail skin.
|
|
90
90
|
*/
|
|
91
|
-
export { vfBase, vfBodyDecls, vfDisplay, vfDisplayDecls, vfStaticText, vfPanel, vfChromeFrame, vfModalFrame, vfTitleBar, vfWindowWidgets, vfHardShadowDecls, vfStripes, vfDots, vfFocus, vfFocusRing, vfFocusUnderline, vfToggle, vfField, vfScrollRail, vfTileSize, vfTileMaskSize, tileImage, tileRaster, tileRects, tileSpan, TILE_LATTICE, } from './styles/base.js';
|
|
91
|
+
export { vfBase, vfBodyDecls, vfDisplay, vfDisplayDecls, vfStaticText, vfPanel, vfChromeFrame, vfModalFrame, vfRule, vfTitleBar, vfWindowWidgets, vfHardShadowDecls, vfStripes, vfDots, vfFocus, vfFocusRing, vfFocusUnderline, vfToggle, vfField, vfScrollRail, vfTileSize, vfTileMaskSize, tileImage, tileRaster, tileRects, tileSpan, TILE_LATTICE, } from './styles/base.js';
|
|
92
92
|
export type { TileRect } from './styles/base.js';
|
|
93
|
+
/**
|
|
94
|
+
* The 1px rule's attribute grammar (`vf-container rule="top bottom"`): the
|
|
95
|
+
* edge names, the parser, and the class list a resolved rule puts on a box
|
|
96
|
+
* styled by `vfRule`.
|
|
97
|
+
*/
|
|
98
|
+
export { RULE_EDGES, parseRule, ruleClasses } from './styles/base.js';
|
|
99
|
+
export type { RuleEdge } from './styles/base.js';
|
|
93
100
|
/**
|
|
94
101
|
* The exact tiled fill (TILE-GRID-PLAN.md): a surface's own art as one
|
|
95
102
|
* whole-surface raster (`tileRaster` + the `vf-tile-raster` rules), and a
|
package/dist/index.js
CHANGED
|
@@ -15,59 +15,60 @@ import { PATTERNS as G, PATTERN_NAMES as K, PATTERN_SIZE as q, parsePattern as J
|
|
|
15
15
|
import { vfDots as Q, vfStripes as $ } from "./styles/recipes/pattern.js";
|
|
16
16
|
import { vfHardShadowDecls as ee } from "./styles/recipes/shadow.js";
|
|
17
17
|
import { vfChromeFrame as te, vfModalFrame as ne, vfPanel as re } from "./styles/recipes/surface.js";
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
44
|
-
import {
|
|
45
|
-
import {
|
|
46
|
-
import {
|
|
47
|
-
import {
|
|
48
|
-
import {
|
|
49
|
-
import {
|
|
50
|
-
import {
|
|
51
|
-
import {
|
|
52
|
-
import {
|
|
53
|
-
import {
|
|
54
|
-
import {
|
|
55
|
-
import {
|
|
56
|
-
import {
|
|
57
|
-
import {
|
|
58
|
-
import {
|
|
59
|
-
import {
|
|
60
|
-
import {
|
|
61
|
-
import {
|
|
62
|
-
import {
|
|
63
|
-
import {
|
|
64
|
-
import {
|
|
65
|
-
import {
|
|
66
|
-
import {
|
|
67
|
-
import {
|
|
68
|
-
import {
|
|
69
|
-
import {
|
|
70
|
-
import {
|
|
71
|
-
import {
|
|
72
|
-
import {
|
|
73
|
-
|
|
18
|
+
import { RULE_EDGES as ie, parseRule as ae, ruleClasses as oe, vfRule as se } from "./styles/recipes/rule.js";
|
|
19
|
+
import { vfTitleBar as ce, vfWindowWidgets as le } from "./styles/recipes/title-bar.js";
|
|
20
|
+
import { vfFocus as ue, vfFocusRing as de, vfFocusUnderline as fe } from "./styles/recipes/focus.js";
|
|
21
|
+
import { vfToggle as pe } from "./styles/recipes/toggle.js";
|
|
22
|
+
import { vfField as me } from "./styles/recipes/field.js";
|
|
23
|
+
import { vfScrollRail as he } from "./styles/recipes/scroll-rail.js";
|
|
24
|
+
import { TileRasterCache as ge, patternOverride as _e, tileGrid as ve, vfTileGrid as ye } from "./tile-grid.js";
|
|
25
|
+
import { TrackWidthController as be } from "./track-width.js";
|
|
26
|
+
import { PatternFillController as xe, vfPatternFill as Se } from "./pattern-fill.js";
|
|
27
|
+
import { GridSnapController as Ce, requestGridSnap as we } from "./grid-snap.js";
|
|
28
|
+
import { DocumentListenersController as Te } from "./document-listeners.js";
|
|
29
|
+
import { BUTTON_FACE as Ee, BUTTON_FRAME as De, RING_FRAME as Oe, RING_HOLE as ke, RING_INSET as Ae, SCREEN_CORNER as je, steppedCornerClip as Me, steppedRectClip as Ne, steppedRingClip as Pe } from "./pixel-frame.js";
|
|
30
|
+
import { VfDesktop as Fe } from "./components/vf-desktop.js";
|
|
31
|
+
import { VfSized as Ie } from "./size.js";
|
|
32
|
+
import { DragController as Le } from "./drag.js";
|
|
33
|
+
import { ScrollStateController as Re } from "./scroll-state.js";
|
|
34
|
+
import { BLINK_FLIPS as ze, BLINK_INTERVAL_MS as Be, PRESS_HOLD_MS as Ve, RENAME_DELAY_MS as He, prefersReducedMotion as Ue, runSelectionBlink as We } from "./motion.js";
|
|
35
|
+
import { CARET_DOWN as Ge, CHECKBOX_X as Ke, CHECKMARK as qe, RADIO_DOT as Je, RADIO_FACE as Ye, RADIO_RING as Xe, RADIO_RING_PRESSED as Ze, SLIDER_THUMB as Qe, SLIDER_THUMB_FACE as $e, STEPPER as et, STEPPER_DOWN_FILL as tt, STEPPER_UP_FILL as nt, glyphSvg as rt } from "./glyphs.js";
|
|
36
|
+
import { ScrollRailController as it, renderScrollRail as at } from "./scroll-rail.js";
|
|
37
|
+
import { VfScrollArea as ot } from "./components/vf-scroll-area.js";
|
|
38
|
+
import { VfWindow as st } from "./components/vf-window.js";
|
|
39
|
+
import { VfModalDialog as ct, modalDialogStyles as lt } from "./modal-dialog.js";
|
|
40
|
+
import { VfButtonGroup as ut } from "./components/vf-button-group.js";
|
|
41
|
+
import { VfDialog as dt } from "./components/vf-dialog.js";
|
|
42
|
+
import { VfSeparator as ft } from "./components/vf-separator.js";
|
|
43
|
+
import { VfFormControl as pt } from "./form-control.js";
|
|
44
|
+
import { VfButton as mt } from "./components/vf-button.js";
|
|
45
|
+
import { VfSwatch as ht } from "./components/vf-swatch.js";
|
|
46
|
+
import { VfToggleControl as gt } from "./toggle-control.js";
|
|
47
|
+
import { VfCheckbox as _t } from "./components/vf-checkbox.js";
|
|
48
|
+
import { VfRadio as vt } from "./components/vf-radio.js";
|
|
49
|
+
import { VfRadioGroup as yt } from "./components/vf-radio-group.js";
|
|
50
|
+
import { FocusRuleController as bt, focusModality as xt, trackFocusModality as St } from "./focus-modality.js";
|
|
51
|
+
import { VfTextControlBase as Ct } from "./text-control.js";
|
|
52
|
+
import { VfTextField as wt } from "./components/vf-text-field.js";
|
|
53
|
+
import { VfTextArea as Tt } from "./components/vf-text-area.js";
|
|
54
|
+
import { VfNumberField as Et } from "./components/vf-number-field.js";
|
|
55
|
+
import { VfOption as Dt } from "./components/vf-option.js";
|
|
56
|
+
import { VfSelect as Ot } from "./components/vf-select.js";
|
|
57
|
+
import { VfProgressBar as kt } from "./components/vf-progress-bar.js";
|
|
58
|
+
import { VfSlider as At } from "./components/vf-slider.js";
|
|
59
|
+
import { VfLabel as jt } from "./components/vf-label.js";
|
|
60
|
+
import { VfParagraph as Mt } from "./components/vf-paragraph.js";
|
|
61
|
+
import { VfImg as Nt } from "./components/vf-img.js";
|
|
62
|
+
import { VfIcon as Pt } from "./components/vf-icon.js";
|
|
63
|
+
import { VfMenuBar as Ft } from "./components/vf-menu-bar.js";
|
|
64
|
+
import { VfMenu as It } from "./components/vf-menu.js";
|
|
65
|
+
import { VfMenuItem as Lt } from "./components/vf-menu-item.js";
|
|
66
|
+
import { VfList as Rt } from "./components/vf-list.js";
|
|
67
|
+
import { VfListItem as zt } from "./components/vf-list-item.js";
|
|
68
|
+
import { VfFieldset as Bt } from "./components/vf-fieldset.js";
|
|
69
|
+
import { VfGrid as Vt } from "./components/vf-grid.js";
|
|
70
|
+
import { VfStack as Ht } from "./components/vf-stack.js";
|
|
71
|
+
import { VfContainer as Ut } from "./components/vf-container.js";
|
|
72
|
+
import { CURSOR_ARROW as Wt, CURSOR_CROSSHAIR as Gt, CURSOR_I_BEAM as Kt, CURSOR_WAIT as qt } from "./cursor-art.js";
|
|
73
|
+
import { applyCursor as Jt } from "./cursor.js";
|
|
74
|
+
export { ze as BLINK_FLIPS, Be as BLINK_INTERVAL_MS, Ee as BUTTON_FACE, De as BUTTON_FRAME, Ge as CARET_DOWN, Ke as CHECKBOX_X, qe as CHECKMARK, r as CLASSIC_DPI, i as CSS_REFERENCE_DPI, Wt as CURSOR_ARROW, Gt as CURSOR_CROSSHAIR, Kt as CURSOR_I_BEAM, qt as CURSOR_WAIT, Te as DocumentListenersController, Le as DragController, bt as FocusRuleController, Ce as GridSnapController, G as PATTERNS, K as PATTERN_NAMES, q as PATTERN_SIZE, D as PIXEL_GRID_METRICS, Ve as PRESS_HOLD_MS, xe as PatternFillController, T as PlacementController, Je as RADIO_DOT, Ye as RADIO_FACE, Xe as RADIO_RING, Ze as RADIO_RING_PRESSED, He as RENAME_DELAY_MS, Oe as RING_FRAME, ke as RING_HOLE, Ae as RING_INSET, ie as RULE_EDGES, je as SCREEN_CORNER, Qe as SLIDER_THUMB, $e as SLIDER_THUMB_FACE, et as STEPPER, tt as STEPPER_DOWN_FILL, nt as STEPPER_UP_FILL, a as SYSTEM_PX_IN_CSS_PX, d as ScaleController, it as ScrollRailController, Re as ScrollStateController, R as TILE_LATTICE, ge as TileRasterCache, be as TrackWidthController, k as VF_BODY_FAMILY, M as VF_DISPLAY_FAMILY, mt as VfButton, ut as VfButtonGroup, _t as VfCheckbox, Ut as VfContainer, Fe as VfDesktop, dt as VfDialog, Bt as VfFieldset, pt as VfFormControl, Vt as VfGrid, Pt as VfIcon, Nt as VfImg, jt as VfLabel, Rt as VfList, zt as VfListItem, It as VfMenu, Ft as VfMenuBar, Lt as VfMenuItem, ct as VfModalDialog, Et as VfNumberField, Dt as VfOption, Mt as VfParagraph, E as VfPositioned, kt as VfProgressBar, vt as VfRadio, yt as VfRadioGroup, ot as VfScrollArea, Ot as VfSelect, ft as VfSeparator, Ie as VfSized, At as VfSlider, Ht as VfStack, ht as VfSwatch, Tt as VfTextArea, Ct as VfTextControlBase, wt as VfTextField, gt as VfToggleControl, st as VfWindow, Jt as applyCursor, f as applyScale, t as defineElement, o as devicePxPerSystemPx, p as effectiveScale, e as emit, xt as focusModality, m as getScale, s as getZoom, rt as glyphSvg, lt as modalDialogStyles, h as onScaleChange, c as onZoomChange, J as parsePattern, ae as parseRule, Y as patternHex, X as patternMotif, _e as patternOverride, Z as patternRects, Ue as prefersReducedMotion, A as registerBodyFace, N as registerDisplayFace, O as registerEmbeddedFont, at as renderScrollRail, we as requestGridSnap, l as resetZoomBaseline, oe as ruleClasses, We as runSelectionBlink, g as snapSys, _ as snapToDevicePx, v as snapToSystemPx, Me as steppedCornerClip, Ne as steppedRectClip, Pe as steppedRingClip, y as sys, b as sysLength, x as sysLengths, S as systemPxQuantum, ve as tileGrid, z as tileImage, B as tileRaster, V as tileRects, H as tileSpan, C as toSys, w as toSysExact, St as trackFocusModality, u as truePixelRatio, I as vfBase, j as vfBodyDecls, te as vfChromeFrame, P as vfDisplay, F as vfDisplayDecls, Q as vfDots, n as vfElement, me as vfField, ue as vfFocus, de as vfFocusRing, fe as vfFocusUnderline, ee as vfHardShadowDecls, ne as vfModalFrame, re as vfPanel, Se as vfPatternFill, se as vfRule, he as vfScrollRail, L as vfStaticText, $ as vfStripes, ye as vfTileGrid, U as vfTileMaskSize, W as vfTileSize, ce as vfTitleBar, pe as vfToggle, le as vfWindowWidgets };
|
package/dist/styles/base.d.ts
CHANGED
|
@@ -29,6 +29,8 @@ export { vfStaticText } from './recipes/static-text.js';
|
|
|
29
29
|
export { vfDots, vfStripes } from './recipes/pattern.js';
|
|
30
30
|
export { vfHardShadowDecls } from './recipes/shadow.js';
|
|
31
31
|
export { vfChromeFrame, vfModalFrame, vfPanel } from './recipes/surface.js';
|
|
32
|
+
export { RULE_EDGES, parseRule, ruleClasses, vfRule } from './recipes/rule.js';
|
|
33
|
+
export type { RuleEdge } from './recipes/rule.js';
|
|
32
34
|
export { vfTitleBar, vfWindowWidgets } from './recipes/title-bar.js';
|
|
33
35
|
export { vfFocus, vfFocusRing, vfFocusUnderline } from './recipes/focus.js';
|
|
34
36
|
export { vfToggle } from './recipes/toggle.js';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The four edges a rule can sit on, in CSS shorthand order.
|
|
3
|
+
*/
|
|
4
|
+
export declare const RULE_EDGES: readonly ["top", "right", "bottom", "left"];
|
|
5
|
+
export type RuleEdge = (typeof RULE_EDGES)[number];
|
|
6
|
+
/**
|
|
7
|
+
* The 1px rule — the single black line that is the 1-bit art's only edge —
|
|
8
|
+
* drawn on one side of a box. One class per edge, composable: the menu bar's
|
|
9
|
+
* floor is `.vf-rule-bottom`, a window's status strip's ceiling is
|
|
10
|
+
* `.vf-rule-top`, and `vf-container rule="…"` takes the edges by name.
|
|
11
|
+
*
|
|
12
|
+
* A border, not a pseudo-element or a painted line, so it lives inside the
|
|
13
|
+
* box's own `border-box` size (vfBase) and insets the padding box the way
|
|
14
|
+
* FrameRect insets a rectangle's interior: content, percentage fills and
|
|
15
|
+
* children placed against the box all begin inside the rule. Scales with
|
|
16
|
+
* `--vf-scale`, so it is one system px at every density, and paints in
|
|
17
|
+
* `--vf-black`, so it remaps under forced colors with the rest of the ink.
|
|
18
|
+
*
|
|
19
|
+
* One declaration per edge, so the kit cannot grow two rules — the
|
|
20
|
+
* {@link vfHardShadowDecls} principle, applied to the line.
|
|
21
|
+
*
|
|
22
|
+
* Being a border, it shares the kit's border-floor residual
|
|
23
|
+
* (docs/THREE-X-DISPLAYS.md): Chromium floors a fractional border-width to
|
|
24
|
+
* whole CSS px, so above 1× the line paints thinner than a system px (2
|
|
25
|
+
* device px of 3 at 2×) and the padding box begins that much inside — the
|
|
26
|
+
* same as every kit frame, and the same line the menu bar always drew.
|
|
27
|
+
* `npm run verify:rule` asserts a container's rule against the menu bar's.
|
|
28
|
+
*/
|
|
29
|
+
export declare const vfRule: import("lit").CSSResult;
|
|
30
|
+
/**
|
|
31
|
+
* The `rule` attribute grammar: edge names separated by whitespace, in any
|
|
32
|
+
* order (`"bottom"`, `"top bottom"`, all four for a framed box). Returns the
|
|
33
|
+
* edges in top/right/bottom/left order with repeats dropped; an empty array
|
|
34
|
+
* for an unset or blank value; and `null` for a value carrying any token that
|
|
35
|
+
* is not an edge name — the whole value is refused, not the one token, as
|
|
36
|
+
* `parsePattern` refuses an unrecognized pattern. Case-sensitive, like the
|
|
37
|
+
* pattern names.
|
|
38
|
+
*/
|
|
39
|
+
export declare function parseRule(value: string | null | undefined): RuleEdge[] | null;
|
|
40
|
+
/** The classes a resolved rule puts on its box: `vf-rule-top vf-rule-bottom`. */
|
|
41
|
+
export declare function ruleClasses(edges: readonly RuleEdge[]): string;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { css as e, unsafeCSS as t } from "lit";
|
|
2
|
+
//#region src/styles/recipes/rule.ts
|
|
3
|
+
var n = [
|
|
4
|
+
"top",
|
|
5
|
+
"right",
|
|
6
|
+
"bottom",
|
|
7
|
+
"left"
|
|
8
|
+
], r = t("calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000)"), i = e`
|
|
9
|
+
.vf-rule-top {
|
|
10
|
+
border-top: ${r};
|
|
11
|
+
}
|
|
12
|
+
.vf-rule-right {
|
|
13
|
+
border-right: ${r};
|
|
14
|
+
}
|
|
15
|
+
.vf-rule-bottom {
|
|
16
|
+
border-bottom: ${r};
|
|
17
|
+
}
|
|
18
|
+
.vf-rule-left {
|
|
19
|
+
border-left: ${r};
|
|
20
|
+
}
|
|
21
|
+
`;
|
|
22
|
+
function a(e) {
|
|
23
|
+
if (e == null) return [];
|
|
24
|
+
let t = e.trim().split(/\s+/).filter(Boolean), r = new Set(t);
|
|
25
|
+
for (let e of r) if (!n.includes(e)) return null;
|
|
26
|
+
return n.filter((e) => r.has(e));
|
|
27
|
+
}
|
|
28
|
+
function o(e) {
|
|
29
|
+
return e.map((e) => `vf-rule-${e}`).join(" ");
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { n as RULE_EDGES, a as parseRule, o as ruleClasses, i as vfRule };
|
package/docs/SPEC.md
CHANGED
|
@@ -148,6 +148,7 @@ The span construction stays load-bearing for the CSS-repeated underlays beneath
|
|
|
148
148
|
- `vfHardShadowDecls` — the hard 1-bit drop shadow on its own, for composing into a surface that supplies its own border: `box-shadow: var(--vf-shadow-offset, 2px) var(--vf-shadow-offset, 2px) 0 0 var(--vf-black, #000)`. No blur, no spread — System 7's only depth cue. Every raised surface in the kit composes this one declaration (`vfPanel`, `vfChromeFrame`; `vf-swatch` composes it without either surface class).
|
|
149
149
|
- `vfPanel` — a `.vf-panel` class for menus/popups: white bg, `border: 1px solid var(--vf-black, #000)`, `vfHardShadowDecls`.
|
|
150
150
|
- `vfChromeFrame` — a `.vf-frame` class for the desktop window (`vf-window`): the same white face, 1px black border and hard offset shadow as `vfPanel`, no bevels. Skin only — the component supplies its own layout (a full-size flex column). `vf-dialog` does not use it: both of its chromes are `vfModalFrame`.
|
|
151
|
+
- `vfRule` — the 1px rule on one edge of a box, one class per edge: `.vf-rule-top`, `.vf-rule-right`, `.vf-rule-bottom`, `.vf-rule-left`, each `border-<edge>: 1px solid var(--vf-black, #000)` scaled by `--vf-scale`. A border, so it sits inside the box's `border-box` size and insets the padding box — content, percentage fills and children placed against the box begin inside the rule, as a rectangle's interior begins inside FrameRect's line. One declaration per edge, the `vfHardShadowDecls` principle applied to the line: `vf-menu-bar`'s floor and `vf-window`'s status-strip ceiling are these classes, and `vf-container rule="…"` takes the edges by name. The attribute grammar ships beside it (`RULE_EDGES`, `parseRule`, `ruleClasses`): edge names separated by whitespace in any order, repeats dropped, returned in shorthand order; a blank or unset value is no edges; any other token refuses the whole value, as `parsePattern` refuses an unrecognized pattern. Being a border it shares the kit's border-floor residual (docs/THREE-X-DISPLAYS.md — thin above 1× in Chromium, the padding box that much inside), exactly as every kit frame does. `npm run verify:rule` asserts a container's rule against the menu bar's own at four densities.
|
|
151
152
|
- `vfModalFrame` — the dBoxProc modal-dialog frame, both `vf-dialog` chromes: `.vf-modal-frame` (white face, 1px black border, **no shadow**) around `.vf-modal-frame-inner` (`margin: 2px; border: 2px solid`). **The movable modal is this frame with the striped bar set into it** (traced from a 2× System 7 capture): a `.vf-title-bar` as the frame's first child sits directly under the outer rule, `margin: 0 2px` so its stripes — flush to the bar's own edge here, `inset: 3px 0` — keep the same 2px of white from the outer rule the band keeps; the inner box that follows it drops its top margin and thins its top border to 1px, so the bar's own 1px rule plus that border make the 2px band the bar's floor, continuing down the sides and along the bottom. System 7's alert box drew the mirror trace — 2px outer, 2px gap, 1px inner rule, *with* the hard shadow (`Windows/modal dialog.png` vs the alert reference) — a different chrome the kit does not ship: an alert box is a consumer composition over this frame, not a component.
|
|
152
153
|
- `vfWindowWidgets` — the title-bar window widgets (`.box` base, `.close` left / `.zoom` right at 11×11 with the 1px white patch ring, the pressed go-away sunburst, the nested zoom square), shared by `vf-window` and a `closable` `vf-dialog` so the two components' widgets match by construction. The templates that pair with it (`closeBox`/`zoomBox`/`widgetLabel`) live in `src/chrome.ts` with `chromeTitleBar`. Geometry is the standard bar's; `vf-window`'s utility variant overrides sizes under its own selector.
|
|
153
154
|
- `vfTitleBar` — the striped title bar shared by `vf-window` and `vf-dialog`: a `.vf-title-bar` row (`height: var(--vf-titlebar-height, 18px)`, 1px bottom rule, `overflow: hidden`) and the `.vf-title` patch inside it (display face, white bg, `padding: 0 6px`, `z-index: 1` over the stripes, `nowrap` + ellipsis). Put a `.vf-stripes` layer in as the bar's first child.
|
|
@@ -214,7 +215,7 @@ The desktop-window shell: the classic document window (see DragThing screenshot)
|
|
|
214
215
|
- Zoom box: RIGHT side, same box, plus a small box nested in its top-left corner (sharing the widget's own top/left border; only the right and bottom edges are drawn). `:active` (pressed) → shows the identical sunburst as the close box; the nested box gives way to it.
|
|
215
216
|
- Body: `padding: 12px` (0 if `flush` or `scrollbars`), and `overflow: hidden` — the window being a fixed box, content taller than it is clipped at the frame the way the classic content region was, rather than painting out over the desktop; `scrollbars` is how the user reaches the rest. Two deliberate exemptions: `[scrollbars]` restores `overflow: visible`, because the edge-rail composition below pulls the scroll area one system px *outside* the body on every side and clipping would shave exactly that overhang off (the scroll area does its own clipping); and a control's drop-open panel is not clipped — `vf-select`'s list is `position: fixed` off the control's rect precisely to escape clipping ancestors (§5 vf-select), and it still escapes, because nothing between it and the viewport establishes a containing block for fixed descendants (the grid-snap correction is a `position: relative` left/top offset, never a transform — see §7). A `vf-menu` panel is anchored `position: absolute` and *would* clip, but a menu bar belongs to the desktop, not inside a window body.
|
|
216
217
|
- Grow box (if `resizable`): 15×15 at bottom-right corner, white bg, 1px black top/left borders, containing two overlapping small square outlines. Inactive: the cell and its borders stay, the nested squares go — System 7 drew a deactivated window's size box hollow, with its blanked scroll rails.
|
|
217
|
-
- Status bar (`status` slot): the classic bottom readout strip ("40px x 40px") — a 1px black rule over a white interior, `--vf-status-bar-height` (15px) in all, the grow box's own height, so a `resizable` window's grow box sits flush in the strip's right end (its top/left borders take over the rule there; the strip reserves 21px of right padding under `resizable` so text clears the cell). Slotted content rides the body face's native 12px line (`--vf-line-height`), whole-pixel centered in the 14px interior, `padding-inline: 6px`, nowrap + clipped. Takes **no space** until the slot is populated (the dialog-footer pattern), and collapses again when it empties. Composes with `scrollbars`: the edge rails' bottom overhang lands its frame line exactly on the strip's rule, so the two never double up — though the grow box then sits in the strip rather than the rail corner cell.
|
|
218
|
+
- Status bar (`status` slot): the classic bottom readout strip ("40px x 40px") — a 1px black rule (`vfRule`'s `.vf-rule-top`) over a white interior, `--vf-status-bar-height` (15px) in all, the grow box's own height, so a `resizable` window's grow box sits flush in the strip's right end (its top/left borders take over the rule there; the strip reserves 21px of right padding under `resizable` so text clears the cell). Slotted content rides the body face's native 12px line (`--vf-line-height`), whole-pixel centered in the 14px interior, `padding-inline: 6px`, nowrap + clipped. Takes **no space** until the slot is populated (the dialog-footer pattern), and collapses again when it empties. Composes with `scrollbars`: the edge rails' bottom overhang lands its frame line exactly on the strip's rule, so the two never double up — though the grow box then sits in the strip rather than the rail corner cell.
|
|
218
219
|
- Edge scroll rails (if `scrollbars`): the body slot renders inside a shadow `vf-scroll-area` (its `axis` = the attribute's value, `label` = the heading, `viewport` part re-exported) carrying the TeachText composition internally — `calc(100% + 2px·scale)` with `margin: -1px·scale`, one system px under the frame on every side, so the area's own frame border repaints the window's border lines and a `resizable` window's grow box (z-index 1) lands exactly over the rail-corner cell. Same caveats as the slotted composition (see vf-scroll-area §5), which remains supported for inset wells.
|
|
219
220
|
- Utility variant (`variant="utility"`): the slim windoid bar traced from `Windows/utility-window.png` — `--vf-titlebar-height-utility` (12px = 11px interior + 1px rule), the `vfDots` dither instead of stripes (flush to the side borders — see §4 vfDots), 7×7 widgets (`top: 2px`; close `left: 7px`, zoom `right: 8px` — the art is asymmetric by that pixel) with a 2px patch ring where the striped bar's is 1px (`--vf-widget-ring`, internal geometry: the windoid sheet clears two px of dither beside its widgets), and the nested zoom square shrunk so its edges land at sprite col/row 3. No title patch: the display face's 16px line box can't sit in an 11px interior, so `.vf-title` is `display: none` under the variant (a retheming consumer can re-show it via `::part(title)`) and the heading names the widgets. A pressed windoid widget inverts whole — black interior under a white (invisible) borderline — rather than flashing the big bar's 9×9 sunburst, which can't land on a 5×5 interior.
|
|
220
221
|
- **A11y:** the close/zoom `aria-label`s are qualified by the title when there is one (`Close ${heading}` / `Zoom ${heading}`, falling back to bare `Close` / `Zoom`) — several windows are open at once by design, so a bare repeated "Close" gives an AT user no way to tell which window a widget belongs to. The frame is `role="group"`, named by the title patch via `aria-labelledby` when there is a heading (the utility variant's hidden patch still names it — AccName resolves hidden labelledby targets) — `group` rather than `region` deliberately, so a desktop of windows doesn't pollute landmark navigation; the title bar is a `<div>`, never a `<header>`, which would map to an unnamed `banner` landmark even inside the shadow root. An inactive window's widgets stay in the tree and the tab order but paint no ink (transparent border/background/patch ring — the bare System 7 bar): a background window whose body holds nothing focusable is still reachable, activated by `vf-desktop`'s `focusin` raise the moment Tab lands on a widget, and never drops focus to `<body>` when it deactivates. `npm run verify:window-a11y`.
|
|
@@ -354,7 +355,7 @@ The classic popup menu control ("Macintosh HD ▼").
|
|
|
354
355
|
|
|
355
356
|
#### `vf-menu-bar` (`VfMenuBar`, vf-menu-bar.ts)
|
|
356
357
|
- **Attributes/props:** `label: string` — accessible name for the menubar, mirrored as host `aria-label` (guarded: a consumer's own `aria-label`/`aria-labelledby` is left alone). `rounded: boolean` (reflected) — draws the System 7 screen-corner mask over the bar's top-left/top-right corners. `shortcuts: boolean` (reflected) — makes every slotted menu's item `shortcut`s live key equivalents, one declaration for the whole bar the way MenuKey() answered for every menu at once (see vf-menu-item for the claim contract). Off by default: key equivalents are page-global, and only *the* menu bar should own them.
|
|
357
|
-
- **Visual:** `display: block/flex`, height `var(--vf-menubar-height, 20px)` — 19 white system px over the 1px black rule, the Menus.png bar strip exactly — white bg, `
|
|
358
|
+
- **Visual:** `display: block/flex`, height `var(--vf-menubar-height, 20px)` — 19 white system px over the 1px black rule, the Menus.png bar strip exactly — white bg, the rule `vfRule`'s `.vf-rule-bottom` on the bar (§4), children laid out horizontally from left. Adjacent titles are pulled 5px into each other (`margin-inline-start: -5px` on every slotted menu, absorbed by 14px of bar start padding — 9px of bar before the first plate, as System 7 placed the Apple title, plus the first title's 5px share): Menus.png spaces title ink 14px apart while each title's plate runs 10px left / 9px right of its own ink, so neighboring plates *overlap* by 5px, as the originals did. With `rounded`, two 5×5 system-px corner overlays paint the traced stair-step mask (per-row runs 5/3/2/1/1 — `SCREEN_CORNER` + `steppedCornerClip` in `pixel-frame.ts`) in `--vf-black` *over* the bar: on the classic screen the rounding was the ROM's black corner mask sitting on top of the menu bar, not a shape of the bar's own, so it is ink over any backdrop rather than a cutout.
|
|
358
359
|
- **Behavior:** container/controller for slotted `vf-menu` children. Pressing a menu label → opens it (label inverts while open). While any menu is open, hovering another label switches to it (classic behavior). Escape / outside click / item selection closes. `role="menubar"`, behind a first-connect ownership latch so a consumer's own role survives upgrade; the shadow `.bar` is `role="presentation"` and each slotted `vf-menu` host `role="none"`, so the `menubar → menuitem` ownership chain has no generics in it. While a menu is open: ArrowLeft/Right move between menus, ArrowDown/Up walk the open menu's items, Home/End jump to its first/last enabled item, and printable keys run the shared Finder first-letter type-ahead over the items (`src/type-ahead.ts`; Space stays the focused item's activation key, and the prefix resets on menu switch or close). The bar also **owns the press-drag-release gesture** across its menus (`MenuPressController`, `src/menu-press.ts` — see `vf-menu`), since one press may travel over several of them: it binds the opening `pointerdown` and hands the controller its own open/close rules, so the gesture changes *when* a menu opens, never *how*.
|
|
359
360
|
- **Slots:** default (vf-menu elements). **Parts:** `bar`.
|
|
360
361
|
|
|
@@ -428,10 +429,10 @@ The kit's layout primitive: a flexbox whose `gap`, `pad`, `width` and `height` a
|
|
|
428
429
|
- **Slots:** default (the children; `fill-width` / `fill-height` on any of them). **Parts:** none. **Events:** none.
|
|
429
430
|
|
|
430
431
|
#### `vf-container` (`VfContainer`, vf-container.ts)
|
|
431
|
-
A plain sized box: `width`/`height` in whole system px around a bare slot —
|
|
432
|
-
- **Attributes/props:** `width` / `height`: number (whole system px, via `VfSized`); the `top` / `left` pair via `VfPositioned` like nearly every component; `pattern?: string` — a library pattern by name or sixteen hex digits (docs/PATTERNS.md), painted as the box's background: unset paints nothing, an unrecognized value paints nothing and warns once. On a **child**: `fill-width` and `fill-height`, bare attributes as in `vf-stack`. That is the whole API.
|
|
432
|
+
A plain sized box: `width`/`height` in whole system px around a bare slot — nothing drawn but what `pattern` and `rule` name, no layout opinion. Explicit placement (src/position.ts) leaves one line of CSS the kit can't write for a consumer: children placed with `top`/`left` need a positioned ancestor, so a region of the consumer's *own* needs `position: relative` in a stylesheet. This component is that region as an element — a DITL's enclosing rectangle with nothing drawn in it.
|
|
433
|
+
- **Attributes/props:** `width` / `height`: number (whole system px, via `VfSized`); the `top` / `left` pair via `VfPositioned` like nearly every component; `pattern?: string` — a library pattern by name or sixteen hex digits (docs/PATTERNS.md), painted as the box's background: unset paints nothing, an unrecognized value paints nothing and warns once; `rule?: string` — edge names separated by spaces (`"bottom"`, `"top bottom"`, up to all four in any order), the 1px rule drawn on those edges as the box's own border inside the declared size: unset draws none, a value naming anything but an edge draws none and warns once. On a **child**: `fill-width` and `fill-height`, bare attributes as in `vf-stack`. That is the whole API.
|
|
433
434
|
- **Not a `vf-stack`.** The stack is a flexbox with opinions — an axis, a cross-axis default, fills compiled into flex. The container has none: in-flow children get normal flow, placed children get a coordinate system. Reach for it when the stack's opinions are the thing in the way — a field of placed icons, a fixed stage for placed art, a composition that brings its own layout with it.
|
|
434
|
-
- **Visual:** none unless `pattern` is set — no
|
|
435
|
+
- **Visual:** none unless `pattern` or `rule` is set — no role, keyboard behavior or selection; what it holds decides what it is. With `pattern`, the shadow box carries the fill as its own background (`vfPatternFill`: the `vf-pattern-fill` class always, `vf-patterned` while a pattern is resolved — *Patterns* under *Tiled fills*), riding the snap correction with the coordinate system; the raster is sized from the declared axes and measured on an undeclared one. With `rule`, the same box wears `vfRule`'s per-edge classes (§4): the rule is its border, inside the declared size (`box-sizing: border-box`), so a 24px `rule="bottom"` strip is 23 rows of box over one of ink — the menu bar's anatomy — and the padding box, which placed children anchor to and percentage fills resolve against, begins inside the rule. A pattern's paper reaches the rule and its phase anchors at the padding box, so it begins directly inside a top or left rule. `npm run verify:rule`. `:host` is a block with `width: fit-content`, so an undeclared axis shrink-wraps rather than claiming the parent's width (the stack's rule — a layout box must not hand out a size nobody declared; a declared dimension lands on the host's inline style and beats it). The slot sits in one shadow box coinciding with the host box (`.vf-snap`, `display: flow-root`, `height: 100%`): flow-root so a slotted margin cannot collapse through the top edge and push the coordinate origin off the host's corner, 100% so percentage fills resolve against a declared height. That box owns `position: relative` — the anchor for `top`/`left` children — the component's purpose. Content that outgrows the declared box overflows it rather than growing it.
|
|
435
436
|
- **Carries a `GridSnapController`.** A container's box is itself the consumer's coordinate system, including for non-`vf` content that cannot correct itself. The shadow box owns the positioning anchor and the `vf-snap` class *together*, so the correction moves the whole coordinate system and everything placed against it rides along, kit or not. A declared size and whole-px `top`/`left` are on the grid by construction; the controller covers the origin the page contributes. (This is the arrangement `vf-stack` has since adopted too — one shadow box owning anchor and correction together.)
|
|
436
437
|
- **Typographically transparent**, exactly as `vf-stack` and for the same reason: `font`, `-webkit-font-smoothing`, `color`, `user-select` and `text-align` return to `inherit`, so wrapping content in a sized box changes nothing about how that content reads.
|
|
437
438
|
- **`fill-width` / `fill-height`** are read about the host (`width`/`height: 100%`, for a parent that can give the box a size) and compiled for slotted children as the percentage form — normal flow has no flex axes to translate onto. `width: 100%` always binds against the box; `height: 100%` binds only against a declared `height` (percentage-against-auto computes to auto), so a fill with nothing to take is inert, not an error. A light-DOM declaration beats the `::slotted` rule, as everywhere.
|
|
@@ -154,13 +154,18 @@
|
|
|
154
154
|
},
|
|
155
155
|
{
|
|
156
156
|
"name": "vf-container",
|
|
157
|
-
"description": "`<vf-container>` — a box that is nothing but its declared size.\n\nThe kit's positioned-placement story (src/position.ts) ends with one line of\nCSS it can't write for you: children placed with `top`/`left` need a\npositioned ancestor, and while every kit container is one — a window body, a\nstack, a fieldset — a region of *your own* needs `position: relative` in a\nstylesheet. This component is that region as an element: declare `width` and\n`height` in whole system px, slot anything into it, place children against\nits origin. A DITL's enclosing rectangle, with nothing drawn in it.\n\n```html\n<vf-container width=\"200\" height=\"120\">\n <vf-icon label=\"System\" width=\"64\" top=\"8\" left=\"12\">…</vf-icon>\n <vf-icon label=\"Finder\" width=\"64\" top=\"8\" left=\"104\">…</vf-icon>\n</vf-container>\n```\n\nThe rectangle is the whole API — `width`/`height` here, plus the `top`/`left`\npair nearly every component takes (VfPositioned), so a container is\nitself placeable: inside a window, a desktop, or another container, at whole\nsystem px that keep its box on the device-pixel grid by construction.\n\n**It is not a `vf-stack`.** The stack is a flexbox with opinions — it\ndistributes children along an axis, compiles `fill-width`/`fill-height` into\nflex, defaults a cross-axis alignment. This box has no layout opinion at\nall: in-flow children get normal flow, placed children get a coordinate\nsystem, and that is the whole API. Reach for it when the stack's opinions\nare the thing in the way — a field of placed icons, a fixed stage for\nabsolutely-positioned art, a consumer's own composition that brings its\nlayout with it.\n\n**The declared size is the layout.** `width`/`height` land on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)` (VfSized), so the box scales\nwith the display and sits on the device-pixel grid by construction. Content\nthat outgrows the box overflows it rather than growing it — the number is\nthe layout, and content that doesn't fit is a number to raise. Leave a\ndimension off and that axis shrink-wraps: `fit-content`, not the parent's\nwidth, because a layout box that silently claimed a size nobody declared\nwould be inventing one (the `vf-stack` rule, held here too).\n\n**It paints nothing and means nothing** — unless `pattern`
|
|
157
|
+
"description": "`<vf-container>` — a box that is nothing but its declared size.\n\nThe kit's positioned-placement story (src/position.ts) ends with one line of\nCSS it can't write for you: children placed with `top`/`left` need a\npositioned ancestor, and while every kit container is one — a window body, a\nstack, a fieldset — a region of *your own* needs `position: relative` in a\nstylesheet. This component is that region as an element: declare `width` and\n`height` in whole system px, slot anything into it, place children against\nits origin. A DITL's enclosing rectangle, with nothing drawn in it.\n\n```html\n<vf-container width=\"200\" height=\"120\">\n <vf-icon label=\"System\" width=\"64\" top=\"8\" left=\"12\">…</vf-icon>\n <vf-icon label=\"Finder\" width=\"64\" top=\"8\" left=\"104\">…</vf-icon>\n</vf-container>\n```\n\nThe rectangle is the whole API — `width`/`height` here, plus the `top`/`left`\npair nearly every component takes (VfPositioned), so a container is\nitself placeable: inside a window, a desktop, or another container, at whole\nsystem px that keep its box on the device-pixel grid by construction.\n\n**It is not a `vf-stack`.** The stack is a flexbox with opinions — it\ndistributes children along an axis, compiles `fill-width`/`fill-height` into\nflex, defaults a cross-axis alignment. This box has no layout opinion at\nall: in-flow children get normal flow, placed children get a coordinate\nsystem, and that is the whole API. Reach for it when the stack's opinions\nare the thing in the way — a field of placed icons, a fixed stage for\nabsolutely-positioned art, a consumer's own composition that brings its\nlayout with it.\n\n**The declared size is the layout.** `width`/`height` land on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)` (VfSized), so the box scales\nwith the display and sits on the device-pixel grid by construction. Content\nthat outgrows the box overflows it rather than growing it — the number is\nthe layout, and content that doesn't fit is a number to raise. Leave a\ndimension off and that axis shrink-wraps: `fit-content`, not the parent's\nwidth, because a layout box that silently claimed a size nobody declared\nwould be inventing one (the `vf-stack` rule, held here too).\n\n**It paints nothing and means nothing** — unless `pattern` or `rule` say\nwhat to paint. No role, keyboard behavior or selection; what it holds\ndecides what it is.\n\n**`rule` draws the 1px rule on the edges it names** — `rule=\"bottom\"` is\nthe menu bar's anatomy (the box's rows over one row of ink), `rule=\"top\"`\na status strip's, all four a framed box. The rule is the box's own border\n(vfRule), inside the declared size, so a 24px `rule=\"bottom\"`\nstrip is 23 rows of box over the line; content, `fill-width` children and\nplaced children begin inside it, as a rectangle's interior begins inside\nFrameRect's line.\n\n**`pattern` fills the box with a 1-bit pattern**: one of the 38 standard\nMacPaint patterns by name (`pattern=\"bricks\"`, `pattern=\"gray-50\"` —\ndocs/PATTERNS.md has the table), or sixteen hex digits stating a custom\n8×8 pattern row by row, the way a PAT resource did. It is painted as the\nbox's own background — black ink on a `--vf-white` ground, anchored at\nthe box's top-left corner, under the content — by the same whole-surface\nraster mechanism as the desktop dither, so it is 1-bit at every density\nand zoom (src/pattern-fill.ts). A declared `width`/`height` sizes the\nraster exactly; an undeclared axis (`fill-width`, a shrink-wrapped\nheight) is measured. Under forced colors the pattern goes flat Canvas.\n\n**It holds its box on the device-pixel grid** — with a `GridSnapController`.\nA container's box is itself the consumer's coordinate system, including for\nnon-`vf` content that cannot correct itself, so the box is the thing to\nhold on the grid. The shadow box below owns the `position: relative` anchor\nand the `vf-snap` class together, so the correction\nmoves the whole coordinate system — everything placed against it rides\nalong instead of being re-corrected child by child. (`vf-stack` shipped\nwithout a controller on the theory that slotted `vf-*` children correct\ntheir own origins; this component is where that theory's gap — consumer\ncontent — became visible, and the stack has since adopted the same\narrangement.)\n\nLike the stack it is **typographically transparent**: `vfBase`'s chrome\ndress is returned to `inherit` on the host, so wrapping content in a sized\nbox changes nothing about how that content reads.\n\n`fill-width` / `fill-height` work here the way they do everywhere: read\nabout the host (be as big as *its* parent allows), and compiled for slotted\nchildren — `width: 100%` in normal flow, so a child filling the cross of a\ndeclared box needs no stylesheet. A height fill needs a declared `height`\nto resolve against; with none it is inert, not an error.\n---\n\n\n### **Slots:**\n - _default_ - The content. In flow by default; `top`/`left` on a kit child places it against this box's origin. `fill-width` / `fill-height` on a child fills it to the declared box.",
|
|
158
158
|
"attributes": [
|
|
159
159
|
{
|
|
160
160
|
"name": "pattern",
|
|
161
161
|
"description": "A 1-bit fill for the box: a library pattern by name (`bricks`,\n`gray-50`, … — the 38 standard MacPaint patterns, docs/PATTERNS.md) or\nsixteen hex digits stating a custom 8×8 pattern row by row, bit 7 the\nleftmost pixel, 1 = ink (`\"DD 77 DD 77 DD 77 DD 77\"`). Painted in black\non a `--vf-white` ground under the content, anchored at the box's\ntop-left. Unset, the container paints nothing; an unrecognized value\npaints nothing and warns once.",
|
|
162
162
|
"values": []
|
|
163
163
|
},
|
|
164
|
+
{
|
|
165
|
+
"name": "rule",
|
|
166
|
+
"description": "The 1px rule on the box's edges: edge names separated by spaces —\n`\"bottom\"`, `\"top bottom\"`, up to all four in any order. Drawn as the\nbox's own border inside the declared size, in `--vf-black`, scaled with\nthe display like every kit frame's border; content and placed children\nbegin inside it.\nUnset, no rule; a value naming anything but an edge draws none and\nwarns once.",
|
|
167
|
+
"values": []
|
|
168
|
+
},
|
|
164
169
|
{
|
|
165
170
|
"name": "width",
|
|
166
171
|
"description": "Width in whole system px. What the number means is the component's own\naffair — the measure a paragraph wraps to, a caption column, a window's\nbox — but the mechanics are shared: the length lands on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)`, so the box scales with the\ndisplay and sits on the device-pixel grid by construction. Remove it\nand the width returns to layout.",
|
package/editor/web-types.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json",
|
|
3
3
|
"name": "vintage-frames",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.1",
|
|
5
5
|
"description-markup": "markdown",
|
|
6
6
|
"contributions": {
|
|
7
7
|
"html": {
|
|
@@ -378,7 +378,7 @@
|
|
|
378
378
|
},
|
|
379
379
|
{
|
|
380
380
|
"name": "vf-container",
|
|
381
|
-
"description": "`<vf-container>` — a box that is nothing but its declared size.\n\nThe kit's positioned-placement story (src/position.ts) ends with one line of\nCSS it can't write for you: children placed with `top`/`left` need a\npositioned ancestor, and while every kit container is one — a window body, a\nstack, a fieldset — a region of *your own* needs `position: relative` in a\nstylesheet. This component is that region as an element: declare `width` and\n`height` in whole system px, slot anything into it, place children against\nits origin. A DITL's enclosing rectangle, with nothing drawn in it.\n\n```html\n<vf-container width=\"200\" height=\"120\">\n <vf-icon label=\"System\" width=\"64\" top=\"8\" left=\"12\">…</vf-icon>\n <vf-icon label=\"Finder\" width=\"64\" top=\"8\" left=\"104\">…</vf-icon>\n</vf-container>\n```\n\nThe rectangle is the whole API — `width`/`height` here, plus the `top`/`left`\npair nearly every component takes (VfPositioned), so a container is\nitself placeable: inside a window, a desktop, or another container, at whole\nsystem px that keep its box on the device-pixel grid by construction.\n\n**It is not a `vf-stack`.** The stack is a flexbox with opinions — it\ndistributes children along an axis, compiles `fill-width`/`fill-height` into\nflex, defaults a cross-axis alignment. This box has no layout opinion at\nall: in-flow children get normal flow, placed children get a coordinate\nsystem, and that is the whole API. Reach for it when the stack's opinions\nare the thing in the way — a field of placed icons, a fixed stage for\nabsolutely-positioned art, a consumer's own composition that brings its\nlayout with it.\n\n**The declared size is the layout.** `width`/`height` land on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)` (VfSized), so the box scales\nwith the display and sits on the device-pixel grid by construction. Content\nthat outgrows the box overflows it rather than growing it — the number is\nthe layout, and content that doesn't fit is a number to raise. Leave a\ndimension off and that axis shrink-wraps: `fit-content`, not the parent's\nwidth, because a layout box that silently claimed a size nobody declared\nwould be inventing one (the `vf-stack` rule, held here too).\n\n**It paints nothing and means nothing** — unless `pattern`
|
|
381
|
+
"description": "`<vf-container>` — a box that is nothing but its declared size.\n\nThe kit's positioned-placement story (src/position.ts) ends with one line of\nCSS it can't write for you: children placed with `top`/`left` need a\npositioned ancestor, and while every kit container is one — a window body, a\nstack, a fieldset — a region of *your own* needs `position: relative` in a\nstylesheet. This component is that region as an element: declare `width` and\n`height` in whole system px, slot anything into it, place children against\nits origin. A DITL's enclosing rectangle, with nothing drawn in it.\n\n```html\n<vf-container width=\"200\" height=\"120\">\n <vf-icon label=\"System\" width=\"64\" top=\"8\" left=\"12\">…</vf-icon>\n <vf-icon label=\"Finder\" width=\"64\" top=\"8\" left=\"104\">…</vf-icon>\n</vf-container>\n```\n\nThe rectangle is the whole API — `width`/`height` here, plus the `top`/`left`\npair nearly every component takes (VfPositioned), so a container is\nitself placeable: inside a window, a desktop, or another container, at whole\nsystem px that keep its box on the device-pixel grid by construction.\n\n**It is not a `vf-stack`.** The stack is a flexbox with opinions — it\ndistributes children along an axis, compiles `fill-width`/`fill-height` into\nflex, defaults a cross-axis alignment. This box has no layout opinion at\nall: in-flow children get normal flow, placed children get a coordinate\nsystem, and that is the whole API. Reach for it when the stack's opinions\nare the thing in the way — a field of placed icons, a fixed stage for\nabsolutely-positioned art, a consumer's own composition that brings its\nlayout with it.\n\n**The declared size is the layout.** `width`/`height` land on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)` (VfSized), so the box scales\nwith the display and sits on the device-pixel grid by construction. Content\nthat outgrows the box overflows it rather than growing it — the number is\nthe layout, and content that doesn't fit is a number to raise. Leave a\ndimension off and that axis shrink-wraps: `fit-content`, not the parent's\nwidth, because a layout box that silently claimed a size nobody declared\nwould be inventing one (the `vf-stack` rule, held here too).\n\n**It paints nothing and means nothing** — unless `pattern` or `rule` say\nwhat to paint. No role, keyboard behavior or selection; what it holds\ndecides what it is.\n\n**`rule` draws the 1px rule on the edges it names** — `rule=\"bottom\"` is\nthe menu bar's anatomy (the box's rows over one row of ink), `rule=\"top\"`\na status strip's, all four a framed box. The rule is the box's own border\n(vfRule), inside the declared size, so a 24px `rule=\"bottom\"`\nstrip is 23 rows of box over the line; content, `fill-width` children and\nplaced children begin inside it, as a rectangle's interior begins inside\nFrameRect's line.\n\n**`pattern` fills the box with a 1-bit pattern**: one of the 38 standard\nMacPaint patterns by name (`pattern=\"bricks\"`, `pattern=\"gray-50\"` —\ndocs/PATTERNS.md has the table), or sixteen hex digits stating a custom\n8×8 pattern row by row, the way a PAT resource did. It is painted as the\nbox's own background — black ink on a `--vf-white` ground, anchored at\nthe box's top-left corner, under the content — by the same whole-surface\nraster mechanism as the desktop dither, so it is 1-bit at every density\nand zoom (src/pattern-fill.ts). A declared `width`/`height` sizes the\nraster exactly; an undeclared axis (`fill-width`, a shrink-wrapped\nheight) is measured. Under forced colors the pattern goes flat Canvas.\n\n**It holds its box on the device-pixel grid** — with a `GridSnapController`.\nA container's box is itself the consumer's coordinate system, including for\nnon-`vf` content that cannot correct itself, so the box is the thing to\nhold on the grid. The shadow box below owns the `position: relative` anchor\nand the `vf-snap` class together, so the correction\nmoves the whole coordinate system — everything placed against it rides\nalong instead of being re-corrected child by child. (`vf-stack` shipped\nwithout a controller on the theory that slotted `vf-*` children correct\ntheir own origins; this component is where that theory's gap — consumer\ncontent — became visible, and the stack has since adopted the same\narrangement.)\n\nLike the stack it is **typographically transparent**: `vfBase`'s chrome\ndress is returned to `inherit` on the host, so wrapping content in a sized\nbox changes nothing about how that content reads.\n\n`fill-width` / `fill-height` work here the way they do everywhere: read\nabout the host (be as big as *its* parent allows), and compiled for slotted\nchildren — `width: 100%` in normal flow, so a child filling the cross of a\ndeclared box needs no stylesheet. A height fill needs a declared `height`\nto resolve against; with none it is inert, not an error.\n---\n\n\n### **Slots:**\n - _default_ - The content. In flow by default; `top`/`left` on a kit child places it against this box's origin. `fill-width` / `fill-height` on a child fills it to the declared box.",
|
|
382
382
|
"doc-url": "",
|
|
383
383
|
"attributes": [
|
|
384
384
|
{
|
|
@@ -386,6 +386,11 @@
|
|
|
386
386
|
"description": "A 1-bit fill for the box: a library pattern by name (`bricks`,\n`gray-50`, … — the 38 standard MacPaint patterns, docs/PATTERNS.md) or\nsixteen hex digits stating a custom 8×8 pattern row by row, bit 7 the\nleftmost pixel, 1 = ink (`\"DD 77 DD 77 DD 77 DD 77\"`). Painted in black\non a `--vf-white` ground under the content, anchored at the box's\ntop-left. Unset, the container paints nothing; an unrecognized value\npaints nothing and warns once.",
|
|
387
387
|
"value": { "type": "string | null | undefined" }
|
|
388
388
|
},
|
|
389
|
+
{
|
|
390
|
+
"name": "rule",
|
|
391
|
+
"description": "The 1px rule on the box's edges: edge names separated by spaces —\n`\"bottom\"`, `\"top bottom\"`, up to all four in any order. Drawn as the\nbox's own border inside the declared size, in `--vf-black`, scaled with\nthe display like every kit frame's border; content and placed children\nbegin inside it.\nUnset, no rule; a value naming anything but an edge draws none and\nwarns once.",
|
|
392
|
+
"value": { "type": "string | null | undefined" }
|
|
393
|
+
},
|
|
389
394
|
{
|
|
390
395
|
"name": "width",
|
|
391
396
|
"description": "Width in whole system px. What the number means is the component's own\naffair — the measure a paragraph wraps to, a caption column, a window's\nbox — but the mechanics are shared: the length lands on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)`, so the box scales with the\ndisplay and sits on the device-pixel grid by construction. Remove it\nand the width returns to layout.",
|
|
@@ -421,6 +426,11 @@
|
|
|
421
426
|
"description": "A 1-bit fill for the box: a library pattern by name (`bricks`,\n`gray-50`, … — the 38 standard MacPaint patterns, docs/PATTERNS.md) or\nsixteen hex digits stating a custom 8×8 pattern row by row, bit 7 the\nleftmost pixel, 1 = ink (`\"DD 77 DD 77 DD 77 DD 77\"`). Painted in black\non a `--vf-white` ground under the content, anchored at the box's\ntop-left. Unset, the container paints nothing; an unrecognized value\npaints nothing and warns once.",
|
|
422
427
|
"type": "string | null | undefined"
|
|
423
428
|
},
|
|
429
|
+
{
|
|
430
|
+
"name": "rule",
|
|
431
|
+
"description": "The 1px rule on the box's edges: edge names separated by spaces —\n`\"bottom\"`, `\"top bottom\"`, up to all four in any order. Drawn as the\nbox's own border inside the declared size, in `--vf-black`, scaled with\nthe display like every kit frame's border; content and placed children\nbegin inside it.\nUnset, no rule; a value naming anything but an edge draws none and\nwarns once.",
|
|
432
|
+
"type": "string | null | undefined"
|
|
433
|
+
},
|
|
424
434
|
{
|
|
425
435
|
"name": "width",
|
|
426
436
|
"description": "Width in whole system px. What the number means is the component's own\naffair — the measure a paragraph wraps to, a caption column, a window's\nbox — but the mechanics are shared: the length lands on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)`, so the box scales with the\ndisplay and sits on the device-pixel grid by construction. Remove it\nand the width returns to layout.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vintage-frames",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Lit web components that emulate the look and feel of classic Mac OS System 7",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"build:pages": "vite build --config vite.pages.config.ts",
|
|
60
60
|
"preview:pages": "vite preview --config vite.pages.config.ts",
|
|
61
61
|
"prepack": "npm run build && npm run verify:manifest",
|
|
62
|
+
"version": "npm run analyze && git add custom-elements.json editor",
|
|
62
63
|
"analyze": "cem analyze",
|
|
63
64
|
"verify:manifest": "node scripts/verify-manifest.mjs",
|
|
64
65
|
"typecheck": "tsc --noEmit",
|
|
@@ -85,6 +86,7 @@
|
|
|
85
86
|
"verify:scale": "node scripts/verify-scale.mjs",
|
|
86
87
|
"verify:tile": "node scripts/verify-tile.mjs",
|
|
87
88
|
"verify:pattern": "node scripts/verify-pattern.mjs",
|
|
89
|
+
"verify:rule": "node scripts/verify-rule.mjs",
|
|
88
90
|
"verify:zoom": "tsc src/zoom.ts --outDir scripts/.tmp --target es2022 --module esnext --moduleResolution bundler --skipLibCheck && node scripts/verify-zoom.mjs",
|
|
89
91
|
"verify:chrome": "node scripts/verify-chrome.mjs",
|
|
90
92
|
"verify:window": "node scripts/verify-window.mjs",
|