mnfst 0.5.214 → 0.5.215
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/lib/manifest.appwrite.auth.js +6 -90
- package/lib/manifest.appwrite.data.js +1 -1
- package/lib/manifest.avatar.css +10 -5
- package/lib/manifest.button.css +18 -0
- package/lib/manifest.charts.js +1 -1
- package/lib/manifest.colorpicker.js +2 -2
- package/lib/manifest.combobox.js +1 -1
- package/lib/manifest.components.js +32 -11
- package/lib/manifest.css +61 -14
- package/lib/manifest.d.ts +13 -1
- package/lib/manifest.data.js +1 -1
- package/lib/manifest.datepicker.js +1 -1
- package/lib/manifest.export.js +249 -1
- package/lib/manifest.form.css +6 -1
- package/lib/manifest.integrity.json +12 -12
- package/lib/manifest.js +13 -4
- package/lib/manifest.min.css +1 -1
- package/lib/manifest.schema.json +5 -0
- package/lib/manifest.status.js +1 -1
- package/lib/manifest.sw.js +5 -3
- package/lib/manifest.typography.css +21 -7
- package/lib/manifest.utilities.css +6 -1
- package/package.json +1 -1
|
@@ -1070,57 +1070,6 @@ window.ManifestAppwriteAuth = {
|
|
|
1070
1070
|
|
|
1071
1071
|
/* Auth frontend */
|
|
1072
1072
|
|
|
1073
|
-
// Create a safe fallback proxy for undefined properties (similar to data proxies)
|
|
1074
|
-
let authLoadingProxy = null;
|
|
1075
|
-
function createAuthLoadingProxy() {
|
|
1076
|
-
if (authLoadingProxy) {
|
|
1077
|
-
return authLoadingProxy;
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
const fallback = Object.create(null);
|
|
1081
|
-
fallback[Symbol.toPrimitive] = function (hint) {
|
|
1082
|
-
return hint === 'number' ? 0 : '';
|
|
1083
|
-
};
|
|
1084
|
-
fallback.valueOf = function () { return ''; };
|
|
1085
|
-
fallback.toString = function () { return ''; };
|
|
1086
|
-
|
|
1087
|
-
Object.defineProperty(fallback, 'length', {
|
|
1088
|
-
value: 0,
|
|
1089
|
-
writable: false,
|
|
1090
|
-
enumerable: false,
|
|
1091
|
-
configurable: false
|
|
1092
|
-
});
|
|
1093
|
-
|
|
1094
|
-
authLoadingProxy = new Proxy(fallback, {
|
|
1095
|
-
get(target, key) {
|
|
1096
|
-
if (key === Symbol.iterator) {
|
|
1097
|
-
return function* () { };
|
|
1098
|
-
}
|
|
1099
|
-
if (key === 'then' || key === 'catch' || key === 'finally' ||
|
|
1100
|
-
key === Symbol.toStringTag || key === Symbol.hasInstance ||
|
|
1101
|
-
key === 'constructor' || key === '__proto__' || key === 'prototype') {
|
|
1102
|
-
return undefined;
|
|
1103
|
-
}
|
|
1104
|
-
if (key in target || key === Symbol.toPrimitive) {
|
|
1105
|
-
const value = target[key];
|
|
1106
|
-
if (value !== undefined) {
|
|
1107
|
-
return value;
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
// Return proxy itself for safe chaining (allows $auth.user.email even if user is undefined)
|
|
1111
|
-
return authLoadingProxy;
|
|
1112
|
-
},
|
|
1113
|
-
has(target, key) {
|
|
1114
|
-
if (typeof key === 'string') {
|
|
1115
|
-
return true;
|
|
1116
|
-
}
|
|
1117
|
-
return key in target || key === Symbol.toPrimitive;
|
|
1118
|
-
}
|
|
1119
|
-
});
|
|
1120
|
-
|
|
1121
|
-
return authLoadingProxy;
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
1073
|
// Initialize $auth magic method
|
|
1125
1074
|
function initializeAuthMagic() {
|
|
1126
1075
|
if (typeof Alpine === 'undefined') {
|
|
@@ -1188,43 +1137,10 @@ function initializeAuthMagic() {
|
|
|
1188
1137
|
return () => ({ success: false, error: 'Method not initialized' });
|
|
1189
1138
|
}
|
|
1190
1139
|
}
|
|
1191
|
-
//
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
// If value is an array, return it as-is (arrays are already iterable and don't need proxying)
|
|
1196
|
-
if (Array.isArray(value)) {
|
|
1197
|
-
return value;
|
|
1198
|
-
}
|
|
1199
|
-
// If value is an object, wrap it in a proxy for safe nested property access
|
|
1200
|
-
if (typeof value === 'object' && value !== null) {
|
|
1201
|
-
// Recursive helper function for nested object proxying
|
|
1202
|
-
function createNestedAuthProxy(objTarget) {
|
|
1203
|
-
return new Proxy(objTarget, {
|
|
1204
|
-
get(objTarget, key) {
|
|
1205
|
-
// Handle special keys
|
|
1206
|
-
if (key === Symbol.iterator || key === 'then' || key === 'catch' || key === 'finally') {
|
|
1207
|
-
return undefined;
|
|
1208
|
-
}
|
|
1209
|
-
const nestedValue = objTarget[key];
|
|
1210
|
-
// If nested value is undefined or null, return loading proxy for safe chaining
|
|
1211
|
-
if (nestedValue === undefined || nestedValue === null) {
|
|
1212
|
-
return createAuthLoadingProxy();
|
|
1213
|
-
}
|
|
1214
|
-
// If nested value is an array, return it as-is
|
|
1215
|
-
if (Array.isArray(nestedValue)) {
|
|
1216
|
-
return nestedValue;
|
|
1217
|
-
}
|
|
1218
|
-
// If nested value is an object, wrap recursively
|
|
1219
|
-
if (typeof nestedValue === 'object' && nestedValue !== null) {
|
|
1220
|
-
return createNestedAuthProxy(nestedValue);
|
|
1221
|
-
}
|
|
1222
|
-
return nestedValue;
|
|
1223
|
-
}
|
|
1224
|
-
});
|
|
1225
|
-
}
|
|
1226
|
-
return createNestedAuthProxy(value);
|
|
1227
|
-
}
|
|
1140
|
+
// Hand back the real value — no placeholder. A signed-out
|
|
1141
|
+
// user is null, not a stand-in object: `?.`, `||` and typeof
|
|
1142
|
+
// must all behave. Templates use `$auth.user?.email`; Alpine
|
|
1143
|
+
// renders undefined as ''.
|
|
1228
1144
|
return value;
|
|
1229
1145
|
}
|
|
1230
1146
|
|
|
@@ -1285,8 +1201,8 @@ function initializeAuthMagic() {
|
|
|
1285
1201
|
return store.getProvider();
|
|
1286
1202
|
}
|
|
1287
1203
|
|
|
1288
|
-
//
|
|
1289
|
-
return
|
|
1204
|
+
// Unknown property — undefined, same as any other object.
|
|
1205
|
+
return undefined;
|
|
1290
1206
|
},
|
|
1291
1207
|
set(target, prop, value) {
|
|
1292
1208
|
// Forward assignments to the store for two-way binding (x-model)
|
package/lib/manifest.avatar.css
CHANGED
|
@@ -17,11 +17,14 @@
|
|
|
17
17
|
text-align: center;
|
|
18
18
|
text-transform: uppercase;
|
|
19
19
|
color: var(--color-field-inverse, oklch(43.9% 0 0));
|
|
20
|
-
|
|
20
|
+
/* surface-3, not field-surface: an avatar is content, not a control.
|
|
21
|
+
Themes are free to make fields translucent, and a see-through avatar
|
|
22
|
+
bleeds the one stacked behind it. Same value in both modes today. */
|
|
23
|
+
background-color: var(--color-surface-3, oklch(87% 0 0));
|
|
21
24
|
background-clip: content-box;
|
|
22
25
|
background-size: cover;
|
|
23
26
|
background-position: center;
|
|
24
|
-
border-radius:
|
|
27
|
+
border-radius: 50%;
|
|
25
28
|
|
|
26
29
|
/* Icon */
|
|
27
30
|
&[x-icon] {
|
|
@@ -49,7 +52,7 @@
|
|
|
49
52
|
width: 9px;
|
|
50
53
|
height: 9px;
|
|
51
54
|
z-index: 1;
|
|
52
|
-
background-color: var(--color-
|
|
55
|
+
background-color: var(--color-surface-3, oklch(87% 0 0));
|
|
53
56
|
border: 1px solid var(--color-page, oklch(98.5% 0 0));
|
|
54
57
|
border-radius: 50%
|
|
55
58
|
}
|
|
@@ -61,7 +64,9 @@
|
|
|
61
64
|
background-blend-mode: normal;
|
|
62
65
|
|
|
63
66
|
&:hover {
|
|
64
|
-
|
|
67
|
+
/* Steps off the avatar's own surface, not a field token — darkens in
|
|
68
|
+
light mode, lightens in dark, and never goes translucent. */
|
|
69
|
+
background-color: color-mix(in oklch, var(--color-surface-3, oklch(87% 0 0)) 88%, var(--color-content-stark, oklch(20.5% 0 0)));
|
|
65
70
|
background-blend-mode: multiply;
|
|
66
71
|
|
|
67
72
|
& img {
|
|
@@ -113,7 +118,7 @@
|
|
|
113
118
|
padding-inline-end: 1.5ch;
|
|
114
119
|
|
|
115
120
|
&:hover .avatar {
|
|
116
|
-
background-color: var(--color-
|
|
121
|
+
background-color: color-mix(in oklch, var(--color-surface-3, oklch(87% 0 0)) 88%, var(--color-content-stark, oklch(20.5% 0 0)));
|
|
117
122
|
transition: var(--transition, all .05s ease-in-out)
|
|
118
123
|
}
|
|
119
124
|
}
|
package/lib/manifest.button.css
CHANGED
|
@@ -80,4 +80,22 @@
|
|
|
80
80
|
mask-image: var(--icon-chevron-down, url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m6 9 6 6 6-6'/%3E%3C/svg%3E"))
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
+
|
|
84
|
+
/* Without base-select the reset's `appearance: button` leaves the select
|
|
85
|
+
natively rendered, and WebKit then ignores the author's border-radius and
|
|
86
|
+
imposes its own — so selects stop matching every other field. `appearance:
|
|
87
|
+
none` hands styling back; it also drops the native arrow, and ::picker-icon
|
|
88
|
+
only paints under base-select, so redraw the chevron as a background. It
|
|
89
|
+
can't be masked to currentColor there (an SVG loaded as an image can't see
|
|
90
|
+
it), hence the baked mid-grey, legible on the field surface in both modes. */
|
|
91
|
+
@supports not (appearance: base-select) {
|
|
92
|
+
:where(select):not(.unstyle) {
|
|
93
|
+
appearance: none;
|
|
94
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23808080' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
|
|
95
|
+
background-repeat: no-repeat;
|
|
96
|
+
background-position: right var(--spacing-field-padding, 0.625rem) center;
|
|
97
|
+
background-size: 1rem;
|
|
98
|
+
padding-inline-end: calc(var(--spacing-field-padding, 0.625rem) * 2 + 1rem)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
83
101
|
}
|
package/lib/manifest.charts.js
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
const merged = JSON.parse(JSON.stringify(fallbacks || {}));
|
|
26
26
|
try {
|
|
27
27
|
if (!window.Alpine || typeof Alpine.evaluate !== 'function') return merged;
|
|
28
|
-
try { Alpine.evaluate(document.body, '$locale && $locale.current'); } catch (_) { } // dep → re-resolve on locale switch
|
|
28
|
+
try { Alpine.evaluate(document.body, 'typeof $locale !== "undefined" && $locale.current'); } catch (_) { } // dep → re-resolve on locale switch
|
|
29
29
|
for (const name of this._loadedSourceNames()) {
|
|
30
30
|
let ui;
|
|
31
31
|
try { ui = Alpine.evaluate(document.body, `$x['${name}'] && $x['${name}']._ui && $x['${name}']._ui['${component}']`); } catch (_) { ui = null; }
|
|
@@ -23,7 +23,7 @@ function initializeColorpickerPlugin() {
|
|
|
23
23
|
const merged = JSON.parse(JSON.stringify(fallbacks || {}));
|
|
24
24
|
try {
|
|
25
25
|
if (!window.Alpine || typeof Alpine.evaluate !== 'function') return merged;
|
|
26
|
-
try { Alpine.evaluate(document.body, '$locale && $locale.current'); } catch (_) { } // dep → re-resolve on locale switch
|
|
26
|
+
try { Alpine.evaluate(document.body, 'typeof $locale !== "undefined" && $locale.current'); } catch (_) { } // dep → re-resolve on locale switch
|
|
27
27
|
for (const name of this._loadedSourceNames()) {
|
|
28
28
|
let ui;
|
|
29
29
|
try { ui = Alpine.evaluate(document.body, `$x['${name}'] && $x['${name}']._ui && $x['${name}']._ui['${component}']`); } catch (_) { ui = null; }
|
|
@@ -1689,7 +1689,7 @@ function initializeColorpickerPlugin() {
|
|
|
1689
1689
|
|
|
1690
1690
|
// Reactive deps trigger re-runs on locale switch / manifest load.
|
|
1691
1691
|
Alpine.effect(() => {
|
|
1692
|
-
try { Alpine.evaluate(evalCtx, '$locale && $locale.current'); } catch {}
|
|
1692
|
+
try { Alpine.evaluate(evalCtx, 'typeof $locale !== "undefined" && $locale.current'); } catch {}
|
|
1693
1693
|
try { Alpine.evaluate(evalCtx, '$x && $x.manifest && $x.manifest._loadedFrom'); } catch {}
|
|
1694
1694
|
runDiscovery();
|
|
1695
1695
|
// Re-localize the whole picker on locale switch.
|
package/lib/manifest.combobox.js
CHANGED
|
@@ -23,7 +23,7 @@ if (!window.ManifestUI) {
|
|
|
23
23
|
const merged = JSON.parse(JSON.stringify(fallbacks || {}));
|
|
24
24
|
try {
|
|
25
25
|
if (!window.Alpine || typeof Alpine.evaluate !== 'function') return merged;
|
|
26
|
-
try { Alpine.evaluate(document.body, '$locale && $locale.current'); } catch (_) { }
|
|
26
|
+
try { Alpine.evaluate(document.body, 'typeof $locale !== "undefined" && $locale.current'); } catch (_) { }
|
|
27
27
|
for (const name of this._loadedSourceNames()) {
|
|
28
28
|
let ui;
|
|
29
29
|
try { ui = Alpine.evaluate(document.body, `$x['${name}'] && $x['${name}']._ui && $x['${name}']._ui['${component}']`); } catch (_) { ui = null; }
|
|
@@ -102,9 +102,11 @@ window.ManifestComponentsRegistry = {
|
|
|
102
102
|
window.ManifestComponentsLoader = {
|
|
103
103
|
cache: {},
|
|
104
104
|
_loading: {},
|
|
105
|
+
_missing: null,
|
|
105
106
|
initialize() {
|
|
106
107
|
this.cache = {};
|
|
107
108
|
this._loading = {};
|
|
109
|
+
this._missing = new Set();
|
|
108
110
|
// Preload components listed in registry.preloaded
|
|
109
111
|
const registry = window.ManifestComponentsRegistry;
|
|
110
112
|
if (registry && Array.isArray(registry.preloaded)) {
|
|
@@ -123,30 +125,42 @@ window.ManifestComponentsLoader = {
|
|
|
123
125
|
return this._loading[name];
|
|
124
126
|
}
|
|
125
127
|
const registry = window.ManifestComponentsRegistry;
|
|
126
|
-
if (!registry
|
|
127
|
-
console.warn('[Manifest]
|
|
128
|
+
if (!registry) {
|
|
129
|
+
console.warn('[Manifest] Registry unavailable, cannot load component:', name);
|
|
128
130
|
return null;
|
|
129
131
|
}
|
|
130
|
-
|
|
132
|
+
// No manifest.json is fine — the convention fallback below still resolves.
|
|
133
|
+
const mf = registry.manifest || {};
|
|
134
|
+
let path = (mf.preloadedComponents || []).concat(mf.components || [])
|
|
131
135
|
.find(p => p.split('/').pop().replace('.html', '') === name);
|
|
136
|
+
// Convention fallback: unlisted names resolve to components/<name>.html.
|
|
137
|
+
let convention = false;
|
|
132
138
|
if (!path) {
|
|
133
|
-
|
|
134
|
-
return null;
|
|
139
|
+
if (!this._missing) this._missing = new Set();
|
|
140
|
+
if (this._missing.has(name)) return null;
|
|
141
|
+
path = 'components/' + name + '.html';
|
|
142
|
+
convention = true;
|
|
135
143
|
}
|
|
136
144
|
const base = (typeof window.getManifestBase === 'function' ? window.getManifestBase() : '') || '/';
|
|
137
145
|
let url = path.startsWith('/') || path.startsWith('http') ? path : base + path;
|
|
138
146
|
// Version stamp (publish-injected `deployment`, or authored `version`) busts browser-cached component HTML
|
|
139
|
-
const stamp =
|
|
147
|
+
const stamp = mf.deployment || mf.version;
|
|
140
148
|
if (stamp) url += (url.includes('?') ? '&' : '?') + 'v=' + encodeURIComponent(String(stamp));
|
|
141
149
|
const promise = (async () => {
|
|
142
150
|
try {
|
|
143
151
|
const response = await fetch(url);
|
|
144
152
|
if (!response.ok) {
|
|
145
|
-
|
|
153
|
+
if (convention) {
|
|
154
|
+
this._missing.add(name);
|
|
155
|
+
console.warn('[Manifest] Component', name, 'is not listed in manifest.json and', path, 'was not found (HTTP', response.status + ')');
|
|
156
|
+
} else {
|
|
157
|
+
console.warn('[Manifest] HTML file not found for component', name, 'at path:', path, '(HTTP', response.status + ')');
|
|
158
|
+
}
|
|
146
159
|
return null;
|
|
147
160
|
}
|
|
148
161
|
const content = await response.text();
|
|
149
162
|
this.cache[name] = content;
|
|
163
|
+
if (convention) registry.registered.add(name);
|
|
150
164
|
return content;
|
|
151
165
|
} catch (error) {
|
|
152
166
|
console.warn('[Manifest] Failed to load component', name, 'from', path + ':', error.message);
|
|
@@ -176,6 +190,9 @@ function escapeForSingleQuotedJsString(s) {
|
|
|
176
190
|
.replace(/\t/g, '\\t');
|
|
177
191
|
}
|
|
178
192
|
|
|
193
|
+
// Framework web components (not project components) — never fetched.
|
|
194
|
+
const FRAMEWORK_COMPONENT_TAGS = new Set(['code', 'code-group']);
|
|
195
|
+
|
|
179
196
|
window.ManifestComponentsProcessor = {
|
|
180
197
|
async processComponent(element, instanceId) {
|
|
181
198
|
const name = element.tagName.toLowerCase().replace('x-', '');
|
|
@@ -185,10 +202,7 @@ window.ManifestComponentsProcessor = {
|
|
|
185
202
|
console.debug('[Manifest Components] skipped: registry or loader unavailable', element);
|
|
186
203
|
return;
|
|
187
204
|
}
|
|
188
|
-
if (
|
|
189
|
-
console.debug('[Manifest Components] skipped: component not registered', element);
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
205
|
+
if (FRAMEWORK_COMPONENT_TAGS.has(name)) return;
|
|
192
206
|
if (element.hasAttribute('data-pre-rendered') || element.hasAttribute('data-processed')) {
|
|
193
207
|
// Pre-rendered content skips re-fetch but still needs Alpine init.
|
|
194
208
|
if (element.hasAttribute('data-pre-rendered') && window.Alpine && typeof window.Alpine.initTree === 'function') {
|
|
@@ -197,8 +211,15 @@ window.ManifestComponentsProcessor = {
|
|
|
197
211
|
console.debug('[Manifest Components] skipped: already pre-rendered/processed', element);
|
|
198
212
|
return;
|
|
199
213
|
}
|
|
214
|
+
// Unregistered names still try the loader's components/<name>.html
|
|
215
|
+
// convention; a miss leaves the element alone (it may be someone else's).
|
|
216
|
+
const wasRegistered = registry.registered.has(name);
|
|
200
217
|
const content = await loader.loadComponent(name);
|
|
201
218
|
if (!content) {
|
|
219
|
+
if (!wasRegistered) {
|
|
220
|
+
console.debug('[Manifest Components] skipped: component not registered', element);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
202
223
|
console.debug('[Manifest Components] skipped: failed to load component', element);
|
|
203
224
|
element.replaceWith(document.createComment(` Failed to load component: ${name} `));
|
|
204
225
|
return;
|
package/lib/manifest.css
CHANGED
|
@@ -474,11 +474,14 @@
|
|
|
474
474
|
text-align: center;
|
|
475
475
|
text-transform: uppercase;
|
|
476
476
|
color: var(--color-field-inverse, oklch(43.9% 0 0));
|
|
477
|
-
|
|
477
|
+
/* surface-3, not field-surface: an avatar is content, not a control.
|
|
478
|
+
Themes are free to make fields translucent, and a see-through avatar
|
|
479
|
+
bleeds the one stacked behind it. Same value in both modes today. */
|
|
480
|
+
background-color: var(--color-surface-3, oklch(87% 0 0));
|
|
478
481
|
background-clip: content-box;
|
|
479
482
|
background-size: cover;
|
|
480
483
|
background-position: center;
|
|
481
|
-
border-radius:
|
|
484
|
+
border-radius: 50%;
|
|
482
485
|
|
|
483
486
|
/* Icon */
|
|
484
487
|
&[x-icon] {
|
|
@@ -506,7 +509,7 @@
|
|
|
506
509
|
width: 9px;
|
|
507
510
|
height: 9px;
|
|
508
511
|
z-index: 1;
|
|
509
|
-
background-color: var(--color-
|
|
512
|
+
background-color: var(--color-surface-3, oklch(87% 0 0));
|
|
510
513
|
border: 1px solid var(--color-page, oklch(98.5% 0 0));
|
|
511
514
|
border-radius: 50%
|
|
512
515
|
}
|
|
@@ -518,7 +521,9 @@
|
|
|
518
521
|
background-blend-mode: normal;
|
|
519
522
|
|
|
520
523
|
&:hover {
|
|
521
|
-
|
|
524
|
+
/* Steps off the avatar's own surface, not a field token — darkens in
|
|
525
|
+
light mode, lightens in dark, and never goes translucent. */
|
|
526
|
+
background-color: color-mix(in oklch, var(--color-surface-3, oklch(87% 0 0)) 88%, var(--color-content-stark, oklch(20.5% 0 0)));
|
|
522
527
|
background-blend-mode: multiply;
|
|
523
528
|
|
|
524
529
|
& img {
|
|
@@ -570,7 +575,7 @@
|
|
|
570
575
|
padding-inline-end: 1.5ch;
|
|
571
576
|
|
|
572
577
|
&:hover .avatar {
|
|
573
|
-
background-color: var(--color-
|
|
578
|
+
background-color: color-mix(in oklch, var(--color-surface-3, oklch(87% 0 0)) 88%, var(--color-content-stark, oklch(20.5% 0 0)));
|
|
574
579
|
transition: var(--transition, all .05s ease-in-out)
|
|
575
580
|
}
|
|
576
581
|
}
|
|
@@ -670,6 +675,24 @@
|
|
|
670
675
|
mask-image: var(--icon-chevron-down, url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m6 9 6 6 6-6'/%3E%3C/svg%3E"))
|
|
671
676
|
}
|
|
672
677
|
}
|
|
678
|
+
|
|
679
|
+
/* Without base-select the reset's `appearance: button` leaves the select
|
|
680
|
+
natively rendered, and WebKit then ignores the author's border-radius and
|
|
681
|
+
imposes its own — so selects stop matching every other field. `appearance:
|
|
682
|
+
none` hands styling back; it also drops the native arrow, and ::picker-icon
|
|
683
|
+
only paints under base-select, so redraw the chevron as a background. It
|
|
684
|
+
can't be masked to currentColor there (an SVG loaded as an image can't see
|
|
685
|
+
it), hence the baked mid-grey, legible on the field surface in both modes. */
|
|
686
|
+
@supports not (appearance: base-select) {
|
|
687
|
+
:where(select):not(.unstyle) {
|
|
688
|
+
appearance: none;
|
|
689
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23808080' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
|
|
690
|
+
background-repeat: no-repeat;
|
|
691
|
+
background-position: right var(--spacing-field-padding, 0.625rem) center;
|
|
692
|
+
background-size: 1rem;
|
|
693
|
+
padding-inline-end: calc(var(--spacing-field-padding, 0.625rem) * 2 + 1rem)
|
|
694
|
+
}
|
|
695
|
+
}
|
|
673
696
|
}
|
|
674
697
|
|
|
675
698
|
/* Manifest Charts */
|
|
@@ -3258,7 +3281,12 @@
|
|
|
3258
3281
|
}
|
|
3259
3282
|
|
|
3260
3283
|
& input,
|
|
3261
|
-
& .combobox
|
|
3284
|
+
& .combobox {
|
|
3285
|
+
width: fit-content
|
|
3286
|
+
}
|
|
3287
|
+
|
|
3288
|
+
/* Own rule: a selector list is not forgiving, so a browser that can't
|
|
3289
|
+
parse the :has() would drop the two plain selectors above with it. */
|
|
3262
3290
|
& :where(label, .label):has(input:not([type=checkbox], [type=radio]), select, textarea) {
|
|
3263
3291
|
width: fit-content
|
|
3264
3292
|
}
|
|
@@ -4880,7 +4908,11 @@
|
|
|
4880
4908
|
border-radius: var(--radius, 0.5rem)
|
|
4881
4909
|
}
|
|
4882
4910
|
|
|
4883
|
-
/* Badges
|
|
4911
|
+
/* Badges. Leading is normal so descenders clear the bottom edge, padding
|
|
4912
|
+
comes off --spacing rather than ch (0.65ch crowds the first letter), and
|
|
4913
|
+
the radius follows --radius instead of a hard pill. The semantic surface
|
|
4914
|
+
stays solid: tinting it and keeping --color-field-inverse drops dark-mode
|
|
4915
|
+
contrast to ~1.1, and moving to --color-content-stark only reaches ~3. */
|
|
4884
4916
|
:where(.badge) {
|
|
4885
4917
|
display: inline-flex;
|
|
4886
4918
|
justify-content: center;
|
|
@@ -4888,17 +4920,27 @@
|
|
|
4888
4920
|
gap: 0.25rem;
|
|
4889
4921
|
width: fit-content;
|
|
4890
4922
|
height: fit-content;
|
|
4891
|
-
padding: 0.
|
|
4923
|
+
padding: calc(var(--spacing, 0.25rem) * 0.5) calc(var(--spacing, 0.25rem) * 2);
|
|
4892
4924
|
font-weight: 500;
|
|
4893
4925
|
font-size: 0.75rem;
|
|
4894
|
-
line-height: 1;
|
|
4926
|
+
line-height: 1.25;
|
|
4895
4927
|
color: var(--color-field-inverse, oklch(43.9% 0 0));
|
|
4896
4928
|
background-color: var(--color-field-surface, color-mix(in oklch, oklch(97% 0 0) 10%, transparent));
|
|
4897
|
-
border-radius:
|
|
4929
|
+
border-radius: calc(var(--radius, 0.5rem) * 0.6);
|
|
4930
|
+
|
|
4931
|
+
/* Solo icons square up, as buttons do. (The old :only-child here keyed
|
|
4932
|
+
off the badge's siblings, so an icon badge next to anything got word
|
|
4933
|
+
padding.) */
|
|
4934
|
+
&[x-icon] {
|
|
4935
|
+
padding: calc(var(--spacing, 0.25rem) * 0.5);
|
|
4936
|
+
aspect-ratio: 1
|
|
4937
|
+
}
|
|
4898
4938
|
|
|
4899
|
-
/*
|
|
4900
|
-
|
|
4901
|
-
|
|
4939
|
+
/* Same for a badge wrapping a bare <svg>. Its own rule so the plain
|
|
4940
|
+
selector above still lands without :has(). */
|
|
4941
|
+
&:has(> svg:only-child) {
|
|
4942
|
+
padding: calc(var(--spacing, 0.25rem) * 0.5);
|
|
4943
|
+
aspect-ratio: 1
|
|
4902
4944
|
}
|
|
4903
4945
|
}
|
|
4904
4946
|
|
|
@@ -5845,7 +5887,12 @@
|
|
|
5845
5887
|
margin-top: calc(3rem)
|
|
5846
5888
|
}
|
|
5847
5889
|
|
|
5848
|
-
&>details:last-child
|
|
5890
|
+
&>details:last-child {
|
|
5891
|
+
margin-bottom: calc(3rem)
|
|
5892
|
+
}
|
|
5893
|
+
|
|
5894
|
+
/* Own rule — :not() is not forgiving either, so an unsupported :has()
|
|
5895
|
+
inside it would take :last-child above down with it. */
|
|
5849
5896
|
&>details:not(:last-child):not(:has(+ details, + hr)) {
|
|
5850
5897
|
margin-bottom: calc(3rem)
|
|
5851
5898
|
}
|
package/lib/manifest.d.ts
CHANGED
|
@@ -268,6 +268,16 @@ export type ManifestExport = (opts?: {
|
|
|
268
268
|
[extra: string]: unknown;
|
|
269
269
|
}) => Promise<unknown>;
|
|
270
270
|
|
|
271
|
+
/** `$import(opts)` from the import plugin — pick a local file and parse it (json/csv). Resolves the parsed data, or null when the dialog is dismissed. */
|
|
272
|
+
export type ManifestImport = (opts?: {
|
|
273
|
+
format?: 'json' | 'csv';
|
|
274
|
+
/** Data source to replace with the parsed content. */
|
|
275
|
+
source?: string;
|
|
276
|
+
/** Override the file input's accept list. */
|
|
277
|
+
accept?: string;
|
|
278
|
+
[extra: string]: unknown;
|
|
279
|
+
}) => Promise<unknown>;
|
|
280
|
+
|
|
271
281
|
/** `$device` from the utilities plugin (enriched by the device plugin when present). */
|
|
272
282
|
export interface ManifestDevice {
|
|
273
283
|
readonly os: string;
|
|
@@ -424,7 +434,7 @@ export type ManifestDirective =
|
|
|
424
434
|
| 'x-icon' | 'x-svg' | 'x-markdown' | 'x-code' | 'x-code-group'
|
|
425
435
|
| 'x-toast' | 'x-tooltip' | 'x-carousel' | 'x-virtual' | 'x-resize' | 'x-computed' | 'x-defer'
|
|
426
436
|
| 'x-chart' | 'x-date' | 'x-color' | 'x-colorpicker' | 'x-combobox'
|
|
427
|
-
| 'x-export' | 'x-pay' | 'x-files' | 'x-data-files'
|
|
437
|
+
| 'x-export' | 'x-import' | 'x-pay' | 'x-files' | 'x-data-files'
|
|
428
438
|
| 'x-edit' | 'x-text-edit';
|
|
429
439
|
|
|
430
440
|
// ---------------------------------------------------------------------------
|
|
@@ -471,6 +481,8 @@ declare global {
|
|
|
471
481
|
const $chart: ManifestChart;
|
|
472
482
|
/** Export plugin. */
|
|
473
483
|
const $export: ManifestExport;
|
|
484
|
+
/** Import plugin. */
|
|
485
|
+
const $import: ManifestImport;
|
|
474
486
|
/** Element editor (edit plugin; opt-in). */
|
|
475
487
|
const $edit: ManifestEdit;
|
|
476
488
|
/** Text editor (text-edit plugin). */
|
package/lib/manifest.data.js
CHANGED
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
const merged = JSON.parse(JSON.stringify(fallbacks || {}));
|
|
75
75
|
try {
|
|
76
76
|
if (!window.Alpine || typeof Alpine.evaluate !== 'function') return merged;
|
|
77
|
-
try { Alpine.evaluate(document.body, '$locale && $locale.current'); } catch (_) { } // dep → re-resolve on locale switch
|
|
77
|
+
try { Alpine.evaluate(document.body, 'typeof $locale !== "undefined" && $locale.current'); } catch (_) { } // dep → re-resolve on locale switch
|
|
78
78
|
for (const name of this._loadedSourceNames()) {
|
|
79
79
|
let ui;
|
|
80
80
|
try { ui = Alpine.evaluate(document.body, `$x['${name}'] && $x['${name}']._ui && $x['${name}']._ui['${component}']`); } catch (_) { ui = null; }
|