@filipgutica/ui 1.11.0 → 1.11.2
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/README.md +60 -53
- package/dist/site/CaptureScreen.vue.d.ts +1 -1
- package/dist/site.js +56 -54
- package/package.json +1 -1
- package/src/site.css +23 -2
package/README.md
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
# @filipgutica/ui
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
Tailwind CSS 4
|
|
3
|
+
@filipgutica/ui is a compact Vue 3.5+ component library built on
|
|
4
|
+
[Reka UI](https://reka-ui.com) and styled with Tailwind CSS 4. You can recolor
|
|
5
|
+
it with a VS Code color theme: the theme supplies the colors, and the library
|
|
6
|
+
keeps typography, spacing, and component structure the same.
|
|
7
|
+
|
|
8
|
+
Use it when you want dense, calm controls: neutral surfaces with one accent
|
|
9
|
+
color, compact sizes on a 4px rhythm, and visible keyboard focus. It also
|
|
10
|
+
provides the shared header, navigation, and page components of Filip's project
|
|
11
|
+
sites.
|
|
5
12
|
|
|
6
13
|
[Docs and playground](https://filipgutica.github.io/ui/) ·
|
|
7
14
|
[GitHub](https://github.com/filipgutica/ui) ·
|
|
@@ -9,6 +16,9 @@ Tailwind CSS 4 and VS Code theme support.
|
|
|
9
16
|
|
|
10
17
|
## Install
|
|
11
18
|
|
|
19
|
+
You need a Vue 3.5+ app whose stylesheet Tailwind CSS 4 already builds. This
|
|
20
|
+
package does not install Tailwind.
|
|
21
|
+
|
|
12
22
|
```sh
|
|
13
23
|
pnpm add @filipgutica/ui
|
|
14
24
|
```
|
|
@@ -22,6 +32,9 @@ Import the styles from your application stylesheet:
|
|
|
22
32
|
@import "@filipgutica/ui/components.css";
|
|
23
33
|
```
|
|
24
34
|
|
|
35
|
+
The third import adds Graphite Indigo, the palette of the project sites. Leave
|
|
36
|
+
it out to keep the base theme from `theme.css`, with blue actions.
|
|
37
|
+
|
|
25
38
|
## Use
|
|
26
39
|
|
|
27
40
|
```vue
|
|
@@ -37,45 +50,12 @@ import { UiButton } from '@filipgutica/ui'
|
|
|
37
50
|
```
|
|
38
51
|
|
|
39
52
|
The [component docs](https://filipgutica.github.io/ui/#/components/button)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
restoration. Keep native navigation in the server-rendered page until hydration;
|
|
47
|
-
the application decides when to replace it and when to close the drawer at a
|
|
48
|
-
desktop breakpoint. To focus a link destination after closing, prevent the
|
|
49
|
-
cancelable `closeAutoFocus` event and focus that heading.
|
|
50
|
-
|
|
51
|
-
`UiTabs` renders every labelled panel during SSR and initial hydration. After
|
|
52
|
-
mounting, it adds keyboard-accessible tabs and hides inactive panels without
|
|
53
|
-
unmounting them. A tab strip narrower than its tabs scrolls, fades each clipped
|
|
54
|
-
edge, and keeps the selected tab in view when it resizes. Use unique item values
|
|
55
|
-
and keep page anchor headings outside the panels. Re-align an initial hash after
|
|
56
|
-
layout settles if enhancement changes the page height.
|
|
57
|
-
|
|
58
|
-
Track page sections from a component's setup function:
|
|
59
|
-
|
|
60
|
-
```ts
|
|
61
|
-
import { useActiveSection } from '@filipgutica/ui'
|
|
62
|
-
|
|
63
|
-
const activeSection = useActiveSection({
|
|
64
|
-
targetIds: ['overview', 'install', 'commands'],
|
|
65
|
-
})
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
The returned readonly ref contains the last supplied heading above the root's
|
|
69
|
-
CSS `scroll-padding-top` inset, or the final section at the document bottom.
|
|
70
|
-
IDs must remain stable and in page order; missing elements are ignored after
|
|
71
|
-
mounting. Bind matching links to `aria-current="location"`. Scrolling, resizing,
|
|
72
|
-
layout changes, completed transitions, and restored pages update the value. After
|
|
73
|
-
a jump to a section, through a `#id` link, a hash change, or a deep link, that
|
|
74
|
-
section stays current until the reader scrolls away from where the jump lands, so
|
|
75
|
-
a page too short to bring it to the top still marks it.
|
|
76
|
-
The composable does not change focus, the URL, or browser history, and removes
|
|
77
|
-
its listeners and observer when the component unmounts. During SSR it returns
|
|
78
|
-
the first supplied ID without accessing browser globals.
|
|
53
|
+
show an example, a sandbox, and the props, slots, and events of the components
|
|
54
|
+
in `@filipgutica/ui`. Read the
|
|
55
|
+
[Drawer](https://filipgutica.github.io/ui/#/components/drawer) and
|
|
56
|
+
[Tabs](https://filipgutica.github.io/ui/#/components/tabs) pages before you use
|
|
57
|
+
them for page navigation: they explain how each one behaves before and after
|
|
58
|
+
hydration.
|
|
79
59
|
|
|
80
60
|
## Themes
|
|
81
61
|
|
|
@@ -88,20 +68,47 @@ const theme = parseVsCodeTheme({ source, fileName })
|
|
|
88
68
|
applySemanticTheme({ root: document.documentElement, theme })
|
|
89
69
|
```
|
|
90
70
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
71
|
+
The theme must define a background color and a foreground color, such as
|
|
72
|
+
`editor.background` and `editor.foreground`.
|
|
73
|
+
|
|
74
|
+
To let people search for and import themes from Open VSX, use
|
|
75
|
+
`createOpenVsxThemeService` from `@filipgutica/ui/open-vsx`. It runs on Node or
|
|
76
|
+
in a browser with HTTPS and Web Crypto. In a browser, it works only when the
|
|
77
|
+
registry and its resource host allow cross-origin requests.
|
|
78
|
+
|
|
79
|
+
The theme search-and-install dialog on the docs site is not exported. Build
|
|
80
|
+
your own picker from the exported components and theme helpers.
|
|
96
81
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
82
|
+
## Track the current section
|
|
83
|
+
|
|
84
|
+
`useActiveSection` tells you which section of a long page the reader is on,
|
|
85
|
+
so you can mark its link. Call it from a component's setup function:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
import { useActiveSection } from '@filipgutica/ui'
|
|
89
|
+
|
|
90
|
+
const activeSection = useActiveSection({
|
|
91
|
+
targetIds: ['overview', 'install', 'commands'],
|
|
92
|
+
})
|
|
93
|
+
```
|
|
100
94
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
95
|
+
Bind `aria-current="location"` to the link whose section matches the value.
|
|
96
|
+
|
|
97
|
+
- The returned readonly ref holds the last supplied heading above the root's
|
|
98
|
+
CSS `scroll-padding-top` inset. At the bottom of the document, it holds the
|
|
99
|
+
final section.
|
|
100
|
+
- Keep the IDs stable and in page order. IDs with no element on the page are
|
|
101
|
+
ignored after mounting.
|
|
102
|
+
- Scrolling, resizing, layout changes, completed transitions, and restored
|
|
103
|
+
pages update the value.
|
|
104
|
+
- After a jump to a section, through a `#id` link, a hash change, or a deep
|
|
105
|
+
link, that section stays current until the reader scrolls away from where the
|
|
106
|
+
jump lands. So a page too short to bring the section to the top still marks
|
|
107
|
+
it.
|
|
108
|
+
- It does not change focus, the URL, or browser history. It removes its
|
|
109
|
+
listeners and observer when the component unmounts.
|
|
110
|
+
- During SSR it returns the first supplied ID without accessing browser
|
|
111
|
+
globals.
|
|
105
112
|
|
|
106
113
|
## Project sites
|
|
107
114
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
type __VLS_Props = {
|
|
2
2
|
html: string;
|
|
3
3
|
label: string;
|
|
4
|
-
/**
|
|
4
|
+
/** Fits the capture to the available width when it remains readable. */
|
|
5
5
|
fit: boolean;
|
|
6
6
|
};
|
|
7
7
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
package/dist/site.js
CHANGED
|
@@ -73,24 +73,24 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
|
|
|
73
73
|
href: "https://filipgutica.github.io/ui/",
|
|
74
74
|
repository: "https://github.com/filipgutica/ui"
|
|
75
75
|
}
|
|
76
|
-
], re = { class: "fg-site-header" },
|
|
76
|
+
], re = { class: "fg-site-header" }, ie = { class: "fg-site-header__inner" }, V = ["aria-label"], H = {
|
|
77
77
|
class: "fg-site-projects__menu fg-site-nav",
|
|
78
78
|
"aria-label": "Projects"
|
|
79
|
-
},
|
|
79
|
+
}, U = ["href", "aria-current"], W = { class: "fg-site-header__end" }, G = {
|
|
80
80
|
key: 0,
|
|
81
81
|
class: "fg-site-header__links",
|
|
82
82
|
"aria-label": "Site links"
|
|
83
|
-
},
|
|
83
|
+
}, K = ["href"], q = {
|
|
84
84
|
key: 0,
|
|
85
85
|
class: "fg-site-appearance",
|
|
86
86
|
role: "group",
|
|
87
87
|
"aria-label": "Color theme"
|
|
88
|
-
},
|
|
88
|
+
}, J = [
|
|
89
89
|
"aria-label",
|
|
90
90
|
"title",
|
|
91
91
|
"aria-pressed",
|
|
92
92
|
"onClick"
|
|
93
|
-
],
|
|
93
|
+
], Y = {
|
|
94
94
|
viewBox: "0 0 24 24",
|
|
95
95
|
width: "18",
|
|
96
96
|
height: "18",
|
|
@@ -98,10 +98,10 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
|
|
|
98
98
|
stroke: "currentColor",
|
|
99
99
|
"stroke-width": "1.5",
|
|
100
100
|
"aria-hidden": "true"
|
|
101
|
-
},
|
|
101
|
+
}, ae = {
|
|
102
102
|
key: 2,
|
|
103
103
|
d: "M20.5 13.5A9 9 0 0 1 10.5 3a9 9 0 1 0 10 10.5Z"
|
|
104
|
-
},
|
|
104
|
+
}, oe = /* @__PURE__ */ m({
|
|
105
105
|
__name: "UiSiteHeader",
|
|
106
106
|
props: {
|
|
107
107
|
project: {},
|
|
@@ -132,7 +132,7 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
|
|
|
132
132
|
};
|
|
133
133
|
return b(() => {
|
|
134
134
|
i.value = !0, document.addEventListener("pointerdown", h);
|
|
135
|
-
}), y(() => document.removeEventListener("pointerdown", h)), (s, f) => (S(), u("header", re, [d("div",
|
|
135
|
+
}), y(() => document.removeEventListener("pointerdown", h)), (s, f) => (S(), u("header", re, [d("div", ie, [d("details", {
|
|
136
136
|
ref_key: "menu",
|
|
137
137
|
ref: a,
|
|
138
138
|
class: "fg-site-projects",
|
|
@@ -152,14 +152,14 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
|
|
|
152
152
|
stroke: "currentColor",
|
|
153
153
|
"stroke-width": "1.5",
|
|
154
154
|
"aria-hidden": "true"
|
|
155
|
-
}, [d("path", { d: "m4 6 4 4 4-4" })], -1)], 8,
|
|
155
|
+
}, [d("path", { d: "m4 6 4 4 4-4" })], -1)], 8, V), d("nav", H, [(S(!0), u(o, null, w(O(B), (t) => (S(), u("a", {
|
|
156
156
|
key: t.id,
|
|
157
157
|
href: t.href,
|
|
158
158
|
"aria-current": t.id === e.project ? "page" : void 0
|
|
159
|
-
}, D(t.label), 9,
|
|
159
|
+
}, D(t.label), 9, U))), 128))])], 544), d("div", W, [e.links.length > 0 ? (S(), u("nav", G, [(S(!0), u(o, null, w(e.links, (e) => (S(), u("a", {
|
|
160
160
|
key: e.href,
|
|
161
161
|
href: e.href
|
|
162
|
-
}, D(e.label), 9,
|
|
162
|
+
}, D(e.label), 9, K))), 128))])) : l("", !0), T(s.$slots, "default", {}, () => [i.value && O(r) ? (S(), u("div", q, [(S(), u(o, null, w(t, (e) => d("button", {
|
|
163
163
|
key: e.value,
|
|
164
164
|
type: "button",
|
|
165
165
|
class: "fg-site-appearance__choice",
|
|
@@ -167,7 +167,7 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
|
|
|
167
167
|
title: `${e.label} theme`,
|
|
168
168
|
"aria-pressed": O(r).choice.value === e.value,
|
|
169
169
|
onClick: (t) => O(r).chooseAppearance(e.value)
|
|
170
|
-
}, [(S(), u("svg",
|
|
170
|
+
}, [(S(), u("svg", Y, [e.value === "system" ? (S(), u(o, { key: 0 }, [f[1] ||= d("rect", {
|
|
171
171
|
x: "3",
|
|
172
172
|
y: "4",
|
|
173
173
|
width: "18",
|
|
@@ -177,13 +177,13 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
|
|
|
177
177
|
cx: "12",
|
|
178
178
|
cy: "12",
|
|
179
179
|
r: "4"
|
|
180
|
-
}, null, -1), f[4] ||= d("path", { d: "M12 2v2m0 16v2M2 12h2m16 0h2M5 5l1.5 1.5m11 11L19 19M5 19l1.5-1.5m11-11L19 5" }, null, -1)], 64)) : (S(), u("path",
|
|
180
|
+
}, null, -1), f[4] ||= d("path", { d: "M12 2v2m0 16v2M2 12h2m16 0h2M5 5l1.5 1.5m11 11L19 19M5 19l1.5-1.5m11-11L19 5" }, null, -1)], 64)) : (S(), u("path", ae))]))], 8, J)), 64))])) : l("", !0)])])])]));
|
|
181
181
|
}
|
|
182
|
-
}),
|
|
182
|
+
}), se = ["id"], ce = ["role", "aria-labelledby"], le = ["id"], ue = [
|
|
183
183
|
"href",
|
|
184
184
|
"aria-current",
|
|
185
185
|
"onClick"
|
|
186
|
-
],
|
|
186
|
+
], de = { key: 0 }, X = /* @__PURE__ */ m({
|
|
187
187
|
__name: "SiteNavLinks",
|
|
188
188
|
props: {
|
|
189
189
|
contentId: {},
|
|
@@ -212,14 +212,14 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
|
|
|
212
212
|
key: 0,
|
|
213
213
|
id: i(t),
|
|
214
214
|
class: "fg-site-rail__group-label"
|
|
215
|
-
}, D(e.label), 9,
|
|
215
|
+
}, D(e.label), 9, le)) : l("", !0), (S(!0), u(o, null, w(e.items, (e) => (S(), u("a", {
|
|
216
216
|
key: e.href,
|
|
217
217
|
href: e.href,
|
|
218
218
|
"aria-current": a(e),
|
|
219
219
|
onClick: (t) => n("navigate", e.href, t)
|
|
220
|
-
}, [e.code ? (S(), u("code",
|
|
220
|
+
}, [e.code ? (S(), u("code", de, D(e.label), 1)) : (S(), u(o, { key: 1 }, [f(D(e.label), 1)], 64))], 8, ue))), 128))], 8, ce))), 128))])], 8, se));
|
|
221
221
|
}
|
|
222
|
-
}),
|
|
222
|
+
}), fe = ["data-enhanced"], pe = ["open"], me = ["aria-controls"], he = "(max-width: 800px)", ge = /* @__PURE__ */ m({
|
|
223
223
|
__name: "UiSiteNav",
|
|
224
224
|
props: {
|
|
225
225
|
label: {},
|
|
@@ -237,7 +237,7 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
|
|
|
237
237
|
_ ? (e.preventDefault(), _.tabIndex = -1, _.focus({ preventScroll: !0 }), _ = null) : s.value || (e.preventDefault(), y());
|
|
238
238
|
};
|
|
239
239
|
return b(() => {
|
|
240
|
-
h = matchMedia(
|
|
240
|
+
h = matchMedia(he), w(), o.value = !0, h.addEventListener("change", w);
|
|
241
241
|
}), x(() => h?.removeEventListener("change", w)), (a, h) => (S(), u("div", {
|
|
242
242
|
ref_key: "root",
|
|
243
243
|
ref: n,
|
|
@@ -250,7 +250,7 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
|
|
|
250
250
|
}, [d("summary", {
|
|
251
251
|
class: "fg-site-rail__summary",
|
|
252
252
|
"aria-controls": O(t)
|
|
253
|
-
}, D(e.label), 9,
|
|
253
|
+
}, D(e.label), 9, me), p(X, {
|
|
254
254
|
"content-id": O(t),
|
|
255
255
|
label: e.label,
|
|
256
256
|
groups: e.groups,
|
|
@@ -260,7 +260,7 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
|
|
|
260
260
|
"label",
|
|
261
261
|
"groups",
|
|
262
262
|
"active-id"
|
|
263
|
-
])], 8,
|
|
263
|
+
])], 8, pe)) : (S(), c(i, {
|
|
264
264
|
key: 1,
|
|
265
265
|
open: l.value,
|
|
266
266
|
"onUpdate:open": h[0] ||= (e) => l.value = e,
|
|
@@ -275,7 +275,7 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
|
|
|
275
275
|
default: M(() => [f(D(e.label), 1)]),
|
|
276
276
|
_: 1
|
|
277
277
|
})]),
|
|
278
|
-
default: M(() => [p(
|
|
278
|
+
default: M(() => [p(X, {
|
|
279
279
|
"content-id": O(t),
|
|
280
280
|
label: e.label,
|
|
281
281
|
groups: e.groups,
|
|
@@ -288,14 +288,14 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
|
|
|
288
288
|
"active-id"
|
|
289
289
|
])]),
|
|
290
290
|
_: 1
|
|
291
|
-
}, 8, ["open", "title"]))], 8,
|
|
291
|
+
}, 8, ["open", "title"]))], 8, fe));
|
|
292
292
|
}
|
|
293
|
-
}),
|
|
293
|
+
}), _e = [
|
|
294
294
|
"aria-label",
|
|
295
295
|
"tabindex",
|
|
296
296
|
"data-fit",
|
|
297
297
|
"innerHTML"
|
|
298
|
-
], Q = /* @__PURE__ */ m({
|
|
298
|
+
], Z = .7, Q = /* @__PURE__ */ m({
|
|
299
299
|
__name: "CaptureScreen",
|
|
300
300
|
props: {
|
|
301
301
|
html: {},
|
|
@@ -303,35 +303,37 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
|
|
|
303
303
|
fit: { type: Boolean }
|
|
304
304
|
},
|
|
305
305
|
setup(e) {
|
|
306
|
-
let t = C(), n = C(1), r = C(), i, a = () => {
|
|
306
|
+
let t = C(), n = C(1), r = C(), i = s(() => r.value !== void 0), a, o = () => {
|
|
307
307
|
if (!e.fit) {
|
|
308
308
|
r.value = void 0, n.value = 1;
|
|
309
309
|
return;
|
|
310
310
|
}
|
|
311
311
|
let i = t.value, a = i?.firstElementChild;
|
|
312
|
-
!i?.clientWidth || !(a instanceof HTMLElement) || !a.offsetWidth
|
|
312
|
+
if (!i?.clientWidth || !(a instanceof HTMLElement) || !a.offsetWidth) return;
|
|
313
|
+
let o = Math.min(1, i.clientWidth / a.offsetWidth);
|
|
314
|
+
o >= Z && (i.scrollLeft = 0), n.value = o >= Z ? o : 1, r.value = o >= Z ? Math.ceil(a.offsetHeight * n.value) + i.offsetHeight - i.clientHeight : void 0;
|
|
313
315
|
};
|
|
314
|
-
return j(() => [e.fit, e.html],
|
|
315
|
-
typeof ResizeObserver < "u" && (
|
|
316
|
-
}), x(() =>
|
|
316
|
+
return j(() => [e.fit, e.html], o, { flush: "post" }), b(() => {
|
|
317
|
+
typeof ResizeObserver < "u" && (a = new ResizeObserver(o), t.value && a.observe(t.value)), o();
|
|
318
|
+
}), x(() => a?.disconnect()), (a, o) => (S(), u("div", {
|
|
317
319
|
ref_key: "container",
|
|
318
320
|
ref: t,
|
|
319
321
|
class: "fg-capture__screen",
|
|
320
322
|
role: "region",
|
|
321
323
|
"aria-label": e.label,
|
|
322
|
-
tabindex:
|
|
323
|
-
"data-fit":
|
|
324
|
+
tabindex: i.value ? void 0 : 0,
|
|
325
|
+
"data-fit": i.value || void 0,
|
|
324
326
|
style: v({
|
|
325
327
|
height: r.value === void 0 ? void 0 : `${r.value}px`,
|
|
326
328
|
"--capture-scale": n.value
|
|
327
329
|
}),
|
|
328
330
|
innerHTML: e.html
|
|
329
|
-
}, null, 12,
|
|
331
|
+
}, null, 12, _e));
|
|
330
332
|
}
|
|
331
|
-
}),
|
|
333
|
+
}), ve = ["data-enhanced"], ye = ["aria-label"], be = ["id"], xe = { class: "fg-capture__preview" }, Se = ["title", "onClick"], Ce = {
|
|
332
334
|
key: 0,
|
|
333
335
|
class: "fg-capture__caption"
|
|
334
|
-
},
|
|
336
|
+
}, we = { key: 0 }, Te = /* @__PURE__ */ m({
|
|
335
337
|
__name: "UiCapture",
|
|
336
338
|
props: /*@__PURE__*/ h({
|
|
337
339
|
items: {},
|
|
@@ -394,7 +396,7 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
|
|
|
394
396
|
stroke: "currentColor",
|
|
395
397
|
"stroke-width": "1.5",
|
|
396
398
|
"aria-hidden": "true"
|
|
397
|
-
}, [d("path", { d: "M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7" })], -1), d("span", { class: "fg-capture__full-label" }, "Full size", -1)]], 8,
|
|
399
|
+
}, [d("path", { d: "M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7" })], -1), d("span", { class: "fg-capture__full-label" }, "Full size", -1)]], 8, ye)) : l("", !0),
|
|
398
400
|
p(n, {
|
|
399
401
|
"model-value": h.value,
|
|
400
402
|
items: m.value,
|
|
@@ -405,7 +407,7 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
|
|
|
405
407
|
key: 0,
|
|
406
408
|
id: `${e.idPrefix}${n.id}`,
|
|
407
409
|
class: "fg-capture__figure"
|
|
408
|
-
}, [d("div",
|
|
410
|
+
}, [d("div", xe, [p(Q, {
|
|
409
411
|
html: n.html,
|
|
410
412
|
label: `Terminal capture: ${n.label}`,
|
|
411
413
|
fit: a.value
|
|
@@ -421,7 +423,7 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
|
|
|
421
423
|
"aria-hidden": "true",
|
|
422
424
|
title: `Open ${n.label} capture at full size`,
|
|
423
425
|
onClick: (e) => j(n)
|
|
424
|
-
}, null, 8,
|
|
426
|
+
}, null, 8, Se)) : l("", !0)]), n.command || s.$slots.caption ? (S(), u("figcaption", Ce, [n.command ? (S(), u("code", we, D(n.command), 1)) : l("", !0), T(s.$slots, "caption", { item: n })])) : l("", !0)], 8, be)) : l("", !0)], 64))), 128))]),
|
|
425
427
|
_: 3
|
|
426
428
|
}, 8, [
|
|
427
429
|
"model-value",
|
|
@@ -452,26 +454,26 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
|
|
|
452
454
|
}, null, 8, ["html", "label"])) : l("", !0)]),
|
|
453
455
|
_: 1
|
|
454
456
|
}, 8, ["open", "title"])
|
|
455
|
-
], 8,
|
|
457
|
+
], 8, ve));
|
|
456
458
|
}
|
|
457
|
-
}),
|
|
459
|
+
}), Ee = { class: "fg-command-list" }, De = { class: "fg-command-list__table" }, $ = { scope: "col" }, Oe = { scope: "col" }, ke = { key: 0 }, Ae = { class: "fg-command-list__command" }, je = { class: "fg-command-list__description" }, Me = {
|
|
458
460
|
key: 0,
|
|
459
461
|
class: "fg-command-list__action"
|
|
460
|
-
},
|
|
462
|
+
}, Ne = [
|
|
461
463
|
"disabled",
|
|
462
464
|
"aria-label",
|
|
463
465
|
"title",
|
|
464
466
|
"onClick"
|
|
465
|
-
],
|
|
467
|
+
], Pe = {
|
|
466
468
|
viewBox: "0 0 24 24",
|
|
467
469
|
fill: "none",
|
|
468
470
|
stroke: "currentColor",
|
|
469
471
|
"stroke-width": "1.5",
|
|
470
472
|
"aria-hidden": "true"
|
|
471
|
-
},
|
|
473
|
+
}, Fe = {
|
|
472
474
|
key: 0,
|
|
473
475
|
d: "m5 12 4 4L19 6"
|
|
474
|
-
},
|
|
476
|
+
}, Ie = /* @__PURE__ */ m({
|
|
475
477
|
__name: "UiCommandList",
|
|
476
478
|
props: {
|
|
477
479
|
items: {},
|
|
@@ -491,14 +493,14 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
|
|
|
491
493
|
unavailableMessage: "Clipboard unavailable. Select the command to copy it.",
|
|
492
494
|
failureMessage: "Could not copy. Select the command to copy it."
|
|
493
495
|
}), p = C(""), m = s(() => a.value || (r.value ? `${p.value} copied to clipboard.` : "")), h = (e) => (p.value = e, c(e));
|
|
494
|
-
return (e, s) => (S(), u("div",
|
|
495
|
-
d("th",
|
|
496
|
-
d("th",
|
|
497
|
-
t.copyable ? (S(), u("td",
|
|
496
|
+
return (e, s) => (S(), u("div", Ee, [d("table", De, [d("thead", null, [d("tr", null, [
|
|
497
|
+
d("th", $, D(t.commandHeader), 1),
|
|
498
|
+
d("th", Oe, D(t.descriptionHeader), 1),
|
|
499
|
+
t.copyable ? (S(), u("td", ke)) : l("", !0)
|
|
498
500
|
])]), d("tbody", null, [(S(!0), u(o, null, w(t.items, (c) => (S(), u("tr", { key: c.command }, [
|
|
499
|
-
d("td",
|
|
500
|
-
d("td",
|
|
501
|
-
t.copyable ? (S(), u("td",
|
|
501
|
+
d("td", Ae, [T(e.$slots, "command", { item: c }, () => [d("code", null, D(c.command), 1)])]),
|
|
502
|
+
d("td", je, [T(e.$slots, "description", { item: c }, () => [f(D(c.description), 1)])]),
|
|
503
|
+
t.copyable ? (S(), u("td", Me, [n.value ? (S(), u("button", {
|
|
502
504
|
key: 0,
|
|
503
505
|
type: "button",
|
|
504
506
|
class: "fg-command-list__copy",
|
|
@@ -506,13 +508,13 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
|
|
|
506
508
|
"aria-label": `Copy ${c.command}`,
|
|
507
509
|
title: `Copy ${c.command}`,
|
|
508
510
|
onClick: (e) => h(c.command)
|
|
509
|
-
}, [(S(), u("svg",
|
|
511
|
+
}, [(S(), u("svg", Pe, [O(r) && !O(a) && p.value === c.command ? (S(), u("path", Fe)) : (S(), u(o, { key: 1 }, [s[0] ||= d("rect", {
|
|
510
512
|
x: "8",
|
|
511
513
|
y: "8",
|
|
512
514
|
width: "12",
|
|
513
515
|
height: "13",
|
|
514
516
|
rx: "2"
|
|
515
|
-
}, null, -1), s[1] ||= d("path", { d: "M16 8V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h3" }, null, -1)], 64))]))], 8,
|
|
517
|
+
}, null, -1), s[1] ||= d("path", { d: "M16 8V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h3" }, null, -1)], 64))]))], 8, Ne)) : l("", !0)])) : l("", !0)
|
|
516
518
|
]))), 128))])]), d("span", {
|
|
517
519
|
class: _(["fg-command-list__status", { "fg-site-sr-only": !O(a) }]),
|
|
518
520
|
role: "status"
|
|
@@ -520,4 +522,4 @@ var I = "tool-site-theme", L = (e) => e === "system" || e === "light" || e === "
|
|
|
520
522
|
}
|
|
521
523
|
});
|
|
522
524
|
//#endregion
|
|
523
|
-
export { I as SITE_APPEARANCE_KEY, B as SITE_PROJECTS,
|
|
525
|
+
export { I as SITE_APPEARANCE_KEY, B as SITE_PROJECTS, Te as UiCapture, Ie as UiCommandList, oe as UiSiteHeader, ge as UiSiteNav, R as applySiteAppearance, ne as siteThemeBootScript, z as useSiteAppearance };
|
package/package.json
CHANGED
package/src/site.css
CHANGED
|
@@ -222,6 +222,8 @@
|
|
|
222
222
|
flex: none;
|
|
223
223
|
border: 1px solid var(--color-border-subtle);
|
|
224
224
|
border-radius: var(--radius-control);
|
|
225
|
+
/* A muted track, so the pressed choice reads as a raised thumb in both themes. */
|
|
226
|
+
background: var(--color-surface-muted);
|
|
225
227
|
}
|
|
226
228
|
.fg-site-appearance__choice {
|
|
227
229
|
display: inline-flex;
|
|
@@ -240,7 +242,16 @@
|
|
|
240
242
|
.fg-site-appearance__choice:first-child { border-radius: var(--radius-control) 0 0 var(--radius-control); }
|
|
241
243
|
.fg-site-appearance__choice:last-child { border-radius: 0 var(--radius-control) var(--radius-control) 0; }
|
|
242
244
|
.fg-site-appearance__choice:hover { color: var(--color-text); background: var(--color-row-hover); }
|
|
243
|
-
|
|
245
|
+
/* The ring gives the pressed choice 3:1 against the track in both themes (WCAG 1.4.11). */
|
|
246
|
+
.fg-site-appearance__choice[aria-pressed="true"] {
|
|
247
|
+
color: var(--color-text);
|
|
248
|
+
background: var(--color-row-selected);
|
|
249
|
+
box-shadow: inset 0 0 0 1px var(--color-border);
|
|
250
|
+
}
|
|
251
|
+
/* Forced colors drop the fill and the ring, so the pressed choice takes the system highlight. */
|
|
252
|
+
@media (forced-colors: active) {
|
|
253
|
+
.fg-site-appearance__choice[aria-pressed="true"] { forced-color-adjust: none; color: HighlightText; background: Highlight; }
|
|
254
|
+
}
|
|
244
255
|
|
|
245
256
|
/* Focus is a 2px outline outside the control, which forced-colors mode keeps. An
|
|
246
257
|
* inset ring is used only where a scrolling ancestor would clip an outer one. */
|
|
@@ -367,7 +378,7 @@
|
|
|
367
378
|
.fg-capture__screen .tb1 { background: var(--t1, #f0776c); }
|
|
368
379
|
.fg-capture__screen .tb2 { background: var(--t2, #4cc38a); }
|
|
369
380
|
.fg-capture__screen .tb3 { background: var(--t3, #e2a54b); }
|
|
370
|
-
.fg-capture__screen .tb4 { background: var(--t4, #
|
|
381
|
+
.fg-capture__screen .tb4 { background: var(--t4, #3354c4); }
|
|
371
382
|
.fg-capture__screen .tb5 { background: var(--t5, #c792ea); }
|
|
372
383
|
.fg-capture__screen .tb6 { background: var(--t6, #5fd3d9); }
|
|
373
384
|
.fg-capture__screen .tb7 { background: var(--t7, #c8c8d0); }
|
|
@@ -382,6 +393,8 @@
|
|
|
382
393
|
.fg-capture__screen .tf-dim { color: var(--t-dim, #a0a0ab); }
|
|
383
394
|
.fg-capture__screen .tf-bg { color: var(--t-bg, #131316); }
|
|
384
395
|
.fg-capture__screen .tb-fg { background: var(--t-fg, #e6e6ea); }
|
|
396
|
+
/* Text on the light selection row stays dark, whatever its own colour class. */
|
|
397
|
+
.fg-capture__screen .tb8 { color: #1b1b1f; }
|
|
385
398
|
.fg-capture__screen .tbold { font-weight: var(--weight-semibold); }
|
|
386
399
|
.fg-capture__screen .tital { font-style: italic; }
|
|
387
400
|
.fg-capture__screen .tul { text-decoration: underline; text-underline-offset: 0.18em; }
|
|
@@ -398,6 +411,8 @@
|
|
|
398
411
|
background: transparent;
|
|
399
412
|
cursor: zoom-in;
|
|
400
413
|
}
|
|
414
|
+
/* Leave pointer gestures to the screen when the capture needs to scroll. */
|
|
415
|
+
.fg-capture__screen:not([data-fit]) + .fg-capture__expand { pointer-events: none; }
|
|
401
416
|
.fg-capture__caption {
|
|
402
417
|
display: grid;
|
|
403
418
|
justify-items: start;
|
|
@@ -612,6 +627,12 @@
|
|
|
612
627
|
}
|
|
613
628
|
}
|
|
614
629
|
@media (max-width: 640px) {
|
|
630
|
+
/* Phones: three or more site links do not fit beside the project menu, so the menu and
|
|
631
|
+
* the header controls share the first row and the links take the second. That keeps the
|
|
632
|
+
* header to two rows. With fewer links, the header keeps its wrapping row. */
|
|
633
|
+
.fg-site-header__inner:has(.fg-site-header__links > :nth-child(3)) .fg-site-header__end { display: contents; }
|
|
634
|
+
.fg-site-header__inner:has(.fg-site-header__links > :nth-child(3)) .fg-site-projects { margin-inline-end: auto; }
|
|
635
|
+
.fg-site-header__inner:has(.fg-site-header__links > :nth-child(3)) .fg-site-header__links { order: 1; flex-basis: 100%; }
|
|
615
636
|
.fg-site-docs { padding-inline: var(--space-4); }
|
|
616
637
|
/* Docs-shell pages keep one header row on phones and repeat these links in their footer. */
|
|
617
638
|
.fg-site-docs .fg-site-header__links { display: none; }
|