monowind 0.1.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/LICENSE +21 -0
- package/README.md +50 -0
- package/dist/ascii.d.ts +16 -0
- package/dist/ascii.d.ts.map +1 -0
- package/dist/borders.d.ts +21 -0
- package/dist/borders.d.ts.map +1 -0
- package/dist/cdn.d.ts +13 -0
- package/dist/cdn.d.ts.map +1 -0
- package/dist/cdn.js +970 -0
- package/dist/cdn.js.map +1 -0
- package/dist/element.d.ts +14 -0
- package/dist/element.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +987 -0
- package/dist/index.js.map +1 -0
- package/dist/layout.d.ts +36 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/metrics.d.ts +11 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/render.d.ts +9 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/style.d.ts +10 -0
- package/dist/style.d.ts.map +1 -0
- package/dist/tree.d.ts +20 -0
- package/dist/tree.d.ts.map +1 -0
- package/dist/types.d.ts +151 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/wrap.d.ts +27 -0
- package/dist/wrap.d.ts.map +1 -0
- package/package.json +49 -0
- package/src/styles.css +145 -0
package/dist/wrap.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Greedy word-wrap for monospace text.
|
|
3
|
+
*
|
|
4
|
+
* Words are runs of non-whitespace; whitespace runs collapse to single spaces
|
|
5
|
+
* between fitting words. Browsers also treat a hyphen inside a word as a
|
|
6
|
+
* break opportunity (break after `-`, no hyphen added) — except before a
|
|
7
|
+
* digit, per UAX #14 (`2026-08` doesn't break) — so words are further split
|
|
8
|
+
* into breakable segments. A segment longer than `width` breaks at cell
|
|
9
|
+
* boundaries. `\n` in the input is a HARD line break — the wrap restarts on
|
|
10
|
+
* a new line (the source of these is `<br>` elements, converted to `\n` by
|
|
11
|
+
* the tree builder). A blank hard line still occupies one row.
|
|
12
|
+
*
|
|
13
|
+
* Matches how a browser wraps `white-space: normal; overflow-wrap: anywhere`
|
|
14
|
+
* text in a fixed-width monospace container — we set that in styles.css so
|
|
15
|
+
* the two agree.
|
|
16
|
+
*/
|
|
17
|
+
export declare function wrapLines(text: string, width: number): string[];
|
|
18
|
+
/** Number of rows `text` occupies at `width` (see wrapLines). */
|
|
19
|
+
export declare function wrapLineCount(text: string, width: number): number;
|
|
20
|
+
/**
|
|
21
|
+
* Split a word at its internal break opportunities: after each hyphen run,
|
|
22
|
+
* unless the next character is a digit (UAX #14: no break between a hyphen
|
|
23
|
+
* and a following number). `"mx-auto"` → `["mx-", "auto"]`;
|
|
24
|
+
* `"2026-08"` → `["2026-08"]`. Also the unit of min-content width.
|
|
25
|
+
*/
|
|
26
|
+
export declare function breakableSegments(word: string): string[];
|
|
27
|
+
//# sourceMappingURL=wrap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrap.d.ts","sourceRoot":"","sources":["../src/wrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAG/D;AAED,iEAAiE;AACjE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAcxD"}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "monowind",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Text-based user interfaces (TUI) on the web, from ordinary HTML and Tailwind utility classes",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ascii",
|
|
7
|
+
"monospace",
|
|
8
|
+
"tailwind",
|
|
9
|
+
"terminal",
|
|
10
|
+
"text-based",
|
|
11
|
+
"tui",
|
|
12
|
+
"web-component"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "Benoît Rouleau",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/benface/monowind.git",
|
|
19
|
+
"directory": "packages/core"
|
|
20
|
+
},
|
|
21
|
+
"style": "./src/styles.css",
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"src/styles.css"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"style": "./src/styles.css",
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"default": "./dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./styles.css": "./src/styles.css"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@tailwindcss/browser": "^4.3.3",
|
|
38
|
+
"happy-dom": "^20.11.6",
|
|
39
|
+
"typescript": "^7.0.2",
|
|
40
|
+
"vite": "^8.2.2",
|
|
41
|
+
"vitest": "^4.1.11"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "vite build && vite build -c vite.cdn.config.ts && tsc -p tsconfig.build.json",
|
|
45
|
+
"typecheck": "tsc --noEmit",
|
|
46
|
+
"test": "vitest run",
|
|
47
|
+
"test:watch": "vitest"
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/styles.css
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/* monowind companion stylesheet — loaded into the light DOM by the app.
|
|
2
|
+
* The shadow root has its own baked-in styles; this file targets the light-DOM
|
|
3
|
+
* subtree the host owns.
|
|
4
|
+
*
|
|
5
|
+
* Layering: defaults and normalization that USERS may override live in
|
|
6
|
+
* `@layer base` — Tailwind's utilities layer (and any unlayered author CSS)
|
|
7
|
+
* then wins over them, matching Tailwind's own preflight behavior. Rules the
|
|
8
|
+
* ENGINE owns (geometry, typography locking, alignment/overflow enforcement)
|
|
9
|
+
* stay unlayered with `!important` so no utility can push content off-grid. */
|
|
10
|
+
|
|
11
|
+
@layer base {
|
|
12
|
+
mono-wind {
|
|
13
|
+
display: block;
|
|
14
|
+
position: relative;
|
|
15
|
+
/* New stacking context so ancestor z-index / blend-modes don't bleed in
|
|
16
|
+
* and our own absolute-positioned decorations don't leak out. */
|
|
17
|
+
isolation: isolate;
|
|
18
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
19
|
+
font-size: 14px;
|
|
20
|
+
line-height: 1;
|
|
21
|
+
letter-spacing: 0;
|
|
22
|
+
/* Default themable focus ring color (see form-controls rule below). */
|
|
23
|
+
--mw-focus-color: currentColor;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* Normalize native form controls so they don't borrow browser chrome that
|
|
27
|
+
* would break the grid. Padding, margin, border-width, and box-sizing are
|
|
28
|
+
* handled by the engine-owned rules below. In `base` so utilities
|
|
29
|
+
* (text-*, bg-*, …) can restyle controls. */
|
|
30
|
+
mono-wind :is(button, input, select, textarea) {
|
|
31
|
+
border-radius: 0;
|
|
32
|
+
color: inherit;
|
|
33
|
+
background: transparent;
|
|
34
|
+
font: inherit;
|
|
35
|
+
line-height: inherit;
|
|
36
|
+
text-align: inherit;
|
|
37
|
+
appearance: none;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Restore a themable focus ring — `appearance: none` above kills the UA
|
|
41
|
+
* default, and without this keyboard users can lose track of where focus
|
|
42
|
+
* is. `outline-offset: -1px` keeps the ring inside the element's box so it
|
|
43
|
+
* doesn't overlap our decoration layer. */
|
|
44
|
+
mono-wind :is(button, input, select, textarea, a):focus-visible {
|
|
45
|
+
outline: 2px solid var(--mw-focus-color);
|
|
46
|
+
outline-offset: -1px;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* Hide the host until the first layout completes to avoid the flash of raw
|
|
51
|
+
* browser-rendered content (flex without our decorations, elements at their
|
|
52
|
+
* natural sizes). `visibility: hidden` preserves layout & measurability so
|
|
53
|
+
* cell metrics can still be probed while invisible. */
|
|
54
|
+
mono-wind:not([data-mw-ready]) {
|
|
55
|
+
visibility: hidden !important;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* Lock typography on descendants so the grid metrics stay consistent —
|
|
59
|
+
* `!important` because user classes (`.text-2xl` etc.) would otherwise beat
|
|
60
|
+
* these element-selector rules on specificity. */
|
|
61
|
+
mono-wind * {
|
|
62
|
+
font-family: inherit !important;
|
|
63
|
+
font-size: inherit !important;
|
|
64
|
+
line-height: inherit !important;
|
|
65
|
+
letter-spacing: inherit !important;
|
|
66
|
+
vertical-align: baseline !important;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* Match the engine's greedy word-wrap: break at spaces, and break long
|
|
70
|
+
* words at cell boundaries when they overflow. Gated on `measuring` (unlike
|
|
71
|
+
* the font lock above) so the style reader sees the authored `white-space`
|
|
72
|
+
* — that's how `nowrap` is detected. */
|
|
73
|
+
mono-wind:not([measuring]) * {
|
|
74
|
+
white-space: normal !important;
|
|
75
|
+
overflow-wrap: anywhere !important;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* Author-requested `white-space: nowrap` (or `pre`) — the engine sized this
|
|
79
|
+
* box for unwrapped hard lines, so the browser must not wrap either. Wins
|
|
80
|
+
* over the lock above on specificity. */
|
|
81
|
+
mono-wind:not([measuring]) [data-mw-nowrap] {
|
|
82
|
+
white-space: nowrap !important;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* Truncation (`truncate` = nowrap + clip + text-overflow: ellipsis): use
|
|
86
|
+
* `hidden` rather than the normalized `clip` for this combo — some engines
|
|
87
|
+
* only apply `text-overflow` to scroll containers, which `clip` boxes are
|
|
88
|
+
* not. Wins over the `[data-mw-clip]` rule below on specificity. */
|
|
89
|
+
mono-wind:not([measuring]) [data-mw-nowrap][data-mw-clip] {
|
|
90
|
+
overflow: hidden !important;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* Every CSS property the engine has taken over on laid-out elements.
|
|
94
|
+
* Gated on the `measuring` attribute so the style reader still sees the
|
|
95
|
+
* authored values during its read pass. Being unlayered already beats all
|
|
96
|
+
* layered CSS (e.g. Tailwind utilities); the `!important` additionally
|
|
97
|
+
* defends against inline `style=""` attributes (hand-written or CSS-in-JS)
|
|
98
|
+
* and high-specificity unlayered author CSS. Known escape hatch: Tailwind's
|
|
99
|
+
* `!` modifier (important-in-layer) still wins, per the cascade spec.
|
|
100
|
+
*
|
|
101
|
+
* Geometry: position/box-sizing/left/top/width/height + min/max — set from
|
|
102
|
+
* the engine-owned --mw-x/y/w/h custom properties.
|
|
103
|
+
*
|
|
104
|
+
* Neutralized: border-width (painted as glyphs by the decoration layer),
|
|
105
|
+
* margin (position:absolute + margin still nudges the element off-grid).
|
|
106
|
+
*
|
|
107
|
+
* Owned via `--mw-p{t,r,b,l}` (padding) and `--mw-b{t,r,b,l}` (border cells):
|
|
108
|
+
* both must land on whole cells. The browser applies the sum as native
|
|
109
|
+
* padding since we've zeroed native border-width — this way, content sits
|
|
110
|
+
* inset by exactly the (border + padding) cells the engine planned for.
|
|
111
|
+
*
|
|
112
|
+
* Left alone: flex/grid/gap/justify/align — laid-out children are all
|
|
113
|
+
* position:absolute (out of flow), so nothing consumes them anyway. */
|
|
114
|
+
mono-wind:not([measuring]) [data-mw-laid-out] {
|
|
115
|
+
position: absolute !important;
|
|
116
|
+
box-sizing: border-box !important;
|
|
117
|
+
left: calc(var(--mw-x, 0) * var(--mw-cw, 1px)) !important;
|
|
118
|
+
top: calc(var(--mw-y, 0) * var(--mw-ch, 1px)) !important;
|
|
119
|
+
width: calc(var(--mw-w, 0) * var(--mw-cw, 1px)) !important;
|
|
120
|
+
height: calc(var(--mw-h, 0) * var(--mw-ch, 1px)) !important;
|
|
121
|
+
min-width: 0 !important;
|
|
122
|
+
min-height: 0 !important;
|
|
123
|
+
max-width: none !important;
|
|
124
|
+
max-height: none !important;
|
|
125
|
+
border-width: 0 !important;
|
|
126
|
+
padding-top: calc((var(--mw-pt, 0) + var(--mw-bt, 0)) * var(--mw-ch, 1px)) !important;
|
|
127
|
+
padding-right: calc((var(--mw-pr, 0) + var(--mw-br, 0)) * var(--mw-cw, 1px)) !important;
|
|
128
|
+
padding-bottom: calc((var(--mw-pb, 0) + var(--mw-bb, 0)) * var(--mw-ch, 1px)) !important;
|
|
129
|
+
padding-left: calc((var(--mw-pl, 0) + var(--mw-bl, 0)) * var(--mw-cw, 1px)) !important;
|
|
130
|
+
margin: 0 !important;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* Author-requested `overflow: hidden` or `overflow: clip` — both keep
|
|
134
|
+
* content inside the engine-allocated box. Uses `clip` (no scroll
|
|
135
|
+
* container, cheaper, more precise semantic for what we're doing). */
|
|
136
|
+
mono-wind [data-mw-clip] {
|
|
137
|
+
overflow: clip !important;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* Force off-grid text alignments back to `start`. `text-left`/`right`/`start`/
|
|
141
|
+
* `end` all land on whole cells (offset = (W - N) × cell width); `center` and
|
|
142
|
+
* `justify` produce fractional per-line offsets we can't snap to the grid. */
|
|
143
|
+
mono-wind [data-mw-text-align-blocked] {
|
|
144
|
+
text-align: start !important;
|
|
145
|
+
}
|