mnfst 0.5.80 → 0.5.82
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 +1 -1
- package/lib/manifest.accordion.css +4 -4
- package/lib/manifest.appwrite.auth.js +66 -33
- package/lib/manifest.avatar.css +8 -8
- package/lib/manifest.button.css +7 -7
- package/lib/manifest.checkbox.css +5 -5
- package/lib/manifest.code.css +152 -193
- package/lib/manifest.code.js +841 -881
- package/lib/manifest.code.min.css +1 -1
- package/lib/manifest.colorpicker.css +11 -11
- package/lib/manifest.components.js +25 -155
- package/lib/manifest.css +278 -230
- package/lib/manifest.data.js +46 -2
- package/lib/manifest.dialog.css +2 -2
- package/lib/manifest.divider.css +2 -2
- package/lib/manifest.dropdown.css +9 -9
- package/lib/manifest.form.css +10 -10
- package/lib/manifest.input.css +9 -9
- package/lib/manifest.integrity.json +26 -0
- package/lib/manifest.js +60 -5
- package/lib/manifest.markdown.js +192 -79
- package/lib/manifest.min.css +1 -1
- package/lib/manifest.radio.css +1 -1
- package/lib/manifest.range.css +7 -7
- package/lib/manifest.resize.css +1 -1
- package/lib/manifest.router.js +49 -76
- package/lib/manifest.schema.json +1 -1
- package/lib/manifest.sidebar.css +5 -6
- package/lib/manifest.slides.css +5 -5
- package/lib/manifest.svg.js +75 -5
- package/lib/manifest.switch.css +4 -4
- package/lib/manifest.table.css +4 -4
- package/lib/manifest.theme.css +46 -41
- package/lib/manifest.toast.css +7 -7
- package/lib/manifest.tooltip.css +3 -3
- package/lib/manifest.tooltips.js +41 -0
- package/lib/manifest.typography.css +124 -69
- package/lib/manifest.utilities.css +48 -54
- package/lib/manifest.utilities.js +9 -29
- package/package.json +4 -7
- package/lib/manifest.export.js +0 -535
- package/lib/manifest.virtual.js +0 -319
package/LICENSE
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
Copyright ©
|
|
5
|
+
Copyright © 2026 Andrew Matlock
|
|
6
6
|
|
|
7
7
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
8
8
|
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
align-items: center;
|
|
30
30
|
padding: var(--spacing-field-padding, 0.625rem) 0;
|
|
31
31
|
font-weight: bold;
|
|
32
|
-
color: var(--color-content-stark,
|
|
32
|
+
color: var(--color-content-stark, darkslategray);
|
|
33
33
|
user-select: none;
|
|
34
34
|
cursor: pointer;
|
|
35
35
|
transition: var(--transition, all .05s ease-in-out);
|
|
@@ -42,11 +42,11 @@
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
&:hover {
|
|
45
|
-
color: color-mix(in oklch, var(--color-surface-1,
|
|
45
|
+
color: color-mix(in oklch, var(--color-surface-1, whitesmoke) 40%, var(--color-content-stark, darkslategray))
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
&:active {
|
|
49
|
-
color: color-mix(in oklch, var(--color-surface-1,
|
|
49
|
+
color: color-mix(in oklch, var(--color-surface-1, whitesmoke) 50%, var(--color-content-stark, darkslategray))
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/* Add custom icon */
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
order: 1;
|
|
56
56
|
width: 1rem;
|
|
57
57
|
height: 1rem;
|
|
58
|
-
background-color: color-mix(in oklch, var(--color-field-surface,
|
|
58
|
+
background-color: color-mix(in oklch, var(--color-field-surface, color-mix(darkslategray 10%, transparent)) 50%, var(--color-field-inverse, darkslategray));
|
|
59
59
|
-webkit-mask-image: var(--icon-accordion, url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 256 256'%3E%3Cpath fill='%23000' d='m184.49 136.49l-80 80a12 12 0 0 1-17-17L159 128L87.51 56.49a12 12 0 1 1 17-17l80 80a12 12 0 0 1-.02 17'/%3E%3C/svg%3E"));
|
|
60
60
|
mask-image: var(--icon-accordion, url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 256 256'%3E%3Cpath fill='%23000' d='m184.49 136.49l-80 80a12 12 0 0 1-17-17L159 128L87.51 56.49a12 12 0 1 1 17-17l80 80a12 12 0 0 1-.02 17'/%3E%3C/svg%3E"));
|
|
61
61
|
-webkit-mask-repeat: no-repeat;
|
|
@@ -8,6 +8,21 @@
|
|
|
8
8
|
|
|
9
9
|
/* Auth config */
|
|
10
10
|
|
|
11
|
+
// Refuse strings that still contain an unresolved ${VAR} reference. The loader
|
|
12
|
+
// runs window.ManifestDataConfig.interpolateManifest at manifest-load time, so
|
|
13
|
+
// by the time we read these fields the env-var substitution has already been
|
|
14
|
+
// applied. Anything still matching ${VAR} is an undefined env var — passing it
|
|
15
|
+
// to Appwrite would either silently fail or, worse, be sent verbatim as an
|
|
16
|
+
// HTTP header value, leaking the env var name. Loud-fail instead.
|
|
17
|
+
function resolvedOrNull(value, fieldName) {
|
|
18
|
+
if (typeof value !== 'string') return value;
|
|
19
|
+
if (/\$\{[^}]+\}/.test(value)) {
|
|
20
|
+
console.error(`[Manifest Auth] manifest.appwrite.${fieldName} references an undefined env var (${value}). Auth disabled.`);
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
|
|
11
26
|
// Load manifest if not already loaded (loader may set __manifestLoaded / registry.manifest)
|
|
12
27
|
async function ensureManifest() {
|
|
13
28
|
if (window.ManifestComponentsRegistry?.manifest) {
|
|
@@ -34,13 +49,22 @@ async function getAppwriteConfig() {
|
|
|
34
49
|
}
|
|
35
50
|
|
|
36
51
|
const appwriteConfig = manifest.appwrite;
|
|
37
|
-
const endpoint = appwriteConfig.endpoint;
|
|
38
|
-
const projectId = appwriteConfig.projectId;
|
|
39
|
-
|
|
52
|
+
const endpoint = resolvedOrNull(appwriteConfig.endpoint, 'endpoint');
|
|
53
|
+
const projectId = resolvedOrNull(appwriteConfig.projectId, 'projectId');
|
|
54
|
+
// Optional dev key to bypass rate limits in development. The schema
|
|
55
|
+
// documents `${VAR_NAME}` interpolation for this field specifically —
|
|
56
|
+
// refuse to forward a literal placeholder as an HTTP header.
|
|
57
|
+
const devKey = appwriteConfig.devKey ? resolvedOrNull(appwriteConfig.devKey, 'devKey') : undefined;
|
|
40
58
|
|
|
41
59
|
if (!endpoint || !projectId) {
|
|
42
60
|
return null;
|
|
43
61
|
}
|
|
62
|
+
// devKey is optional: if the user supplied one but it failed to resolve,
|
|
63
|
+
// resolvedOrNull returned null (and logged) — drop the field rather than
|
|
64
|
+
// initialize Appwrite with a literal `${VAR}` header.
|
|
65
|
+
if (appwriteConfig.devKey && devKey === null) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
44
68
|
|
|
45
69
|
// Get auth methods from config (defaults to ["magic", "oauth"] if not specified)
|
|
46
70
|
const authMethods = appwriteConfig.auth?.methods || ["magic", "oauth"];
|
|
@@ -164,6 +188,27 @@ function initializeAuthStore() {
|
|
|
164
188
|
// Cross-tab synchronization using localStorage events
|
|
165
189
|
const STORAGE_KEY = 'manifest:auth:state';
|
|
166
190
|
|
|
191
|
+
// Whitelist of Appwrite Session fields safe to mirror across tabs.
|
|
192
|
+
// CRITICALLY excludes `secret` (the bearer credential), `providerAccessToken`,
|
|
193
|
+
// `providerRefreshToken`, and `providerAccessTokenExpiry`. The cookie set
|
|
194
|
+
// on the Appwrite domain is the actual auth of record; this localStorage
|
|
195
|
+
// copy only supports UI cross-tab sync ("someone just logged in here").
|
|
196
|
+
// An XSS on this origin must not be able to lift session secrets out.
|
|
197
|
+
const SAFE_SESSION_FIELDS = [
|
|
198
|
+
'$id', 'userId', 'provider', 'expire', 'current',
|
|
199
|
+
'clientName', 'osName', 'osCode', 'deviceName',
|
|
200
|
+
'deviceBrand', 'deviceModel', 'countryCode', 'countryName'
|
|
201
|
+
];
|
|
202
|
+
|
|
203
|
+
function sanitizeSessionForStorage(session) {
|
|
204
|
+
if (!session || typeof session !== 'object') return session;
|
|
205
|
+
const safe = {};
|
|
206
|
+
for (const f of SAFE_SESSION_FIELDS) {
|
|
207
|
+
if (f in session) safe[f] = session[f];
|
|
208
|
+
}
|
|
209
|
+
return safe;
|
|
210
|
+
}
|
|
211
|
+
|
|
167
212
|
// Listen for storage events from other tabs
|
|
168
213
|
window.addEventListener('storage', (e) => {
|
|
169
214
|
if (e.key === STORAGE_KEY && e.newValue) {
|
|
@@ -193,7 +238,7 @@ function initializeAuthStore() {
|
|
|
193
238
|
isAuthenticated: store.isAuthenticated,
|
|
194
239
|
isAnonymous: store.isAnonymous,
|
|
195
240
|
user: store.user,
|
|
196
|
-
session: store.session,
|
|
241
|
+
session: sanitizeSessionForStorage(store.session),
|
|
197
242
|
magicLinkSent: store.magicLinkSent,
|
|
198
243
|
magicLinkExpired: store.magicLinkExpired,
|
|
199
244
|
error: store.error
|
|
@@ -5769,13 +5814,11 @@ function initializeMagicLinks() {
|
|
|
5769
5814
|
);
|
|
5770
5815
|
|
|
5771
5816
|
if (isRateLimit) {
|
|
5772
|
-
//
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
}
|
|
5778
|
-
this.error = 'Rate limit exceeded. Please wait a moment and refresh the page.';
|
|
5817
|
+
// Don't persist {userId, secret} for retry — the secret is a
|
|
5818
|
+
// bearer credential that any same-origin XSS could lift. On
|
|
5819
|
+
// rate limit the user must re-request the magic link from
|
|
5820
|
+
// their email (cleanupUrl has already stripped URL params).
|
|
5821
|
+
this.error = 'Rate limit exceeded. Please wait a moment and request a new magic link.';
|
|
5779
5822
|
this.isAuthenticated = false;
|
|
5780
5823
|
this.isAnonymous = false;
|
|
5781
5824
|
this.magicLinkExpired = false;
|
|
@@ -5901,15 +5944,10 @@ function handleMagicLinkCallbacks() {
|
|
|
5901
5944
|
|
|
5902
5945
|
const callbackInfo = event.detail;
|
|
5903
5946
|
|
|
5904
|
-
//
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
secret: callbackInfo.secret
|
|
5909
|
-
}));
|
|
5910
|
-
} catch (e) {
|
|
5911
|
-
console.warn('[Manifest Appwrite Auth] Could not store callback:', e);
|
|
5912
|
-
}
|
|
5947
|
+
// Note: we deliberately do NOT persist {userId, secret} to sessionStorage
|
|
5948
|
+
// as a retry safety net. The secret is a single-use bearer credential
|
|
5949
|
+
// that any same-origin XSS could read; UX of "refresh to retry on rate
|
|
5950
|
+
// limit" isn't worth the credential-exfil risk.
|
|
5913
5951
|
|
|
5914
5952
|
// Handle the callback
|
|
5915
5953
|
await store.handleMagicLinkCallback(callbackInfo.userId, callbackInfo.secret);
|
|
@@ -6247,16 +6285,11 @@ function initializeCallbacks() {
|
|
|
6247
6285
|
const secret = urlParams.get('secret');
|
|
6248
6286
|
const expire = urlParams.get('expire');
|
|
6249
6287
|
|
|
6250
|
-
//
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
storedCallback = JSON.parse(stored);
|
|
6256
|
-
}
|
|
6257
|
-
} catch (e) {
|
|
6258
|
-
// Ignore parse errors
|
|
6259
|
-
}
|
|
6288
|
+
// Eagerly clear any stale magic-link credentials left in sessionStorage
|
|
6289
|
+
// by an earlier (pre-fix) Manifest version. We no longer persist them,
|
|
6290
|
+
// and reading stale ones could resurrect a credential that should have
|
|
6291
|
+
// been forgotten.
|
|
6292
|
+
try { sessionStorage.removeItem('manifest:magic-link:callback'); } catch (e) {}
|
|
6260
6293
|
|
|
6261
6294
|
// Check OAuth redirect flag
|
|
6262
6295
|
const isOAuthCallback = sessionStorage.getItem('manifest:oauth:redirect') === 'true';
|
|
@@ -6267,14 +6300,14 @@ function initializeCallbacks() {
|
|
|
6267
6300
|
const isTeamInvite = !!(teamId && membershipId && userId && secret);
|
|
6268
6301
|
|
|
6269
6302
|
const callbackInfo = {
|
|
6270
|
-
userId: userId
|
|
6271
|
-
secret: secret
|
|
6303
|
+
userId: userId,
|
|
6304
|
+
secret: secret,
|
|
6272
6305
|
expire: expire,
|
|
6273
6306
|
teamId: teamId,
|
|
6274
6307
|
membershipId: membershipId,
|
|
6275
6308
|
isOAuth: isOAuthCallback,
|
|
6276
6309
|
isTeamInvite: isTeamInvite,
|
|
6277
|
-
hasCallback: !!
|
|
6310
|
+
hasCallback: !!userId && !!secret,
|
|
6278
6311
|
hasExpired: !!expire && !userId && !secret
|
|
6279
6312
|
};
|
|
6280
6313
|
|
package/lib/manifest.avatar.css
CHANGED
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
font-weight: bold;
|
|
15
15
|
text-align: center;
|
|
16
16
|
text-transform: uppercase;
|
|
17
|
-
color: var(--color-field-inverse,
|
|
18
|
-
background-color: var(--color-field-surface,
|
|
17
|
+
color: var(--color-field-inverse, darkslategray);
|
|
18
|
+
background-color: var(--color-field-surface, color-mix(darkslategray 10%, transparent));
|
|
19
19
|
background-clip: content-box;
|
|
20
20
|
background-size: cover;
|
|
21
21
|
background-position: center;
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
/* Icon */
|
|
25
25
|
&[x-icon] {
|
|
26
|
-
color: var(--color-content-subtle,
|
|
26
|
+
color: var(--color-content-subtle, darkgray)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/* Profile pic */
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
width: 9px;
|
|
48
48
|
height: 9px;
|
|
49
49
|
z-index: 1;
|
|
50
|
-
background-color: var(--color-field-surface,
|
|
51
|
-
border: 1px solid var(--color-page,
|
|
50
|
+
background-color: var(--color-field-surface, color-mix(darkslategray 10%, transparent));
|
|
51
|
+
border: 1px solid var(--color-page, white);
|
|
52
52
|
border-radius: 50%
|
|
53
53
|
}
|
|
54
54
|
}
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
background-blend-mode: normal;
|
|
60
60
|
|
|
61
61
|
&:hover {
|
|
62
|
-
background-color: var(--color-field-surface-hover,
|
|
62
|
+
background-color: var(--color-field-surface-hover, color-mix(darkslategray 15%, transparent));
|
|
63
63
|
background-blend-mode: multiply;
|
|
64
64
|
|
|
65
65
|
& img {
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
padding-inline-end: 1.5ch;
|
|
95
95
|
|
|
96
96
|
&:hover .avatar {
|
|
97
|
-
background-color: var(--color-field-surface-hover,
|
|
97
|
+
background-color: var(--color-field-surface-hover, color-mix(darkslategray 15%, transparent));
|
|
98
98
|
transition: var(--transition, all .05s ease-in-out)
|
|
99
99
|
}
|
|
100
100
|
}
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
|
|
108
108
|
:where(.avatar) {
|
|
109
109
|
margin-inline-end: calc(var(--spacing-field-height, 2.25rem) * -0.3);
|
|
110
|
-
box-shadow: 0 0 0 3px var(--color-page,
|
|
110
|
+
box-shadow: 0 0 0 3px var(--color-page, white)
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
}
|
package/lib/manifest.button.css
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
@layer components {
|
|
4
4
|
|
|
5
|
-
:where(button:not(.link), [role=button], [type=button], [type=reset], [type=submit], select):not(.unstyle) {
|
|
5
|
+
:where(button:not(.link), [role=button], [type=button], [type=reset], [type=submit], select):not(code, .unstyle) {
|
|
6
6
|
display: inline-flex;
|
|
7
7
|
flex-flow: row;
|
|
8
8
|
justify-content: center;
|
|
@@ -17,13 +17,13 @@
|
|
|
17
17
|
overflow: hidden;
|
|
18
18
|
white-space: nowrap;
|
|
19
19
|
text-overflow: ellipsis;
|
|
20
|
-
color: var(--color-field-inverse,
|
|
21
|
-
background-color: var(--color-field-surface,
|
|
20
|
+
color: var(--color-field-inverse, darkslategray);
|
|
21
|
+
background-color: var(--color-field-surface, color-mix(darkslategray 10%, transparent));
|
|
22
22
|
border-width: 0;
|
|
23
23
|
border-style: solid;
|
|
24
24
|
border-color: transparent;
|
|
25
25
|
border-radius: var(--radius, 0.5rem);
|
|
26
|
-
outline-color: var(--color-line,
|
|
26
|
+
outline-color: var(--color-line, color-mix(darkslategray 10%, transparent));
|
|
27
27
|
cursor: pointer;
|
|
28
28
|
transition: var(--transition, all .05s ease-in-out);
|
|
29
29
|
appearance: button;
|
|
@@ -52,15 +52,15 @@
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
&:hover {
|
|
55
|
-
background-color: var(--color-field-surface-hover,
|
|
55
|
+
background-color: var(--color-field-surface-hover, color-mix(darkslategray 15%, transparent))
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
&:active {
|
|
59
|
-
background-color: var(--color-field-surface-hover,
|
|
59
|
+
background-color: var(--color-field-surface-hover, color-mix(darkslategray 15%, transparent))
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
&:focus-visible {
|
|
63
|
-
background-color: var(--color-field-surface,
|
|
63
|
+
background-color: var(--color-field-surface, color-mix(darkslategray 10%, transparent))
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
|
|
@@ -16,13 +16,13 @@
|
|
|
16
16
|
&:checked {
|
|
17
17
|
|
|
18
18
|
&:hover {
|
|
19
|
-
background-color: var(--color-field-surface-hover,
|
|
20
|
-
border-color: var(--color-field-surface-hover,
|
|
19
|
+
background-color: var(--color-field-surface-hover, color-mix(darkslategray 15%, transparent));
|
|
20
|
+
border-color: var(--color-field-surface-hover, color-mix(darkslategray 15%, transparent))
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
&:active {
|
|
24
|
-
background-color: var(--color-field-surface-hover,
|
|
25
|
-
border-color: var(--color-field-surface-hover,
|
|
24
|
+
background-color: var(--color-field-surface-hover, color-mix(darkslategray 15%, transparent));
|
|
25
|
+
border-color: var(--color-field-surface-hover, color-mix(darkslategray 15%, transparent))
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
left: 50%;
|
|
35
35
|
width: 60%;
|
|
36
36
|
height: 60%;
|
|
37
|
-
background-color: var(--color-field-inverse,
|
|
37
|
+
background-color: var(--color-field-inverse, darkslategray);
|
|
38
38
|
-webkit-mask-image: var(--icon-checkbox, url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath fill='currentColor' d='m0 11l2-2l5 5L18 3l2 2L7 18z'/%3E%3C/svg%3E"));
|
|
39
39
|
mask-image: var(--icon-checkbox, url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath fill='currentColor' d='m0 11l2-2l5 5L18 3l2 2L7 18z'/%3E%3C/svg%3E"));
|
|
40
40
|
-webkit-mask-repeat: no-repeat;
|