mnfst 0.5.163 → 0.5.164
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 +107 -199
- package/lib/manifest.appwrite.data.js +1 -3
- package/lib/manifest.charts.js +34 -79
- package/lib/manifest.chat.js +515 -0
- package/lib/manifest.code.js +110 -340
- package/lib/manifest.colorpicker.js +252 -551
- package/lib/manifest.combobox.js +73 -91
- package/lib/manifest.components.js +34 -95
- package/lib/manifest.css +0 -132
- package/lib/manifest.data.js +189 -562
- package/lib/manifest.datepicker.js +43 -128
- package/lib/manifest.dropdowns.js +18 -39
- package/lib/manifest.export.js +29 -147
- package/lib/manifest.icons.js +9 -24
- package/lib/manifest.integrity.json +23 -23
- package/lib/manifest.js +62 -126
- package/lib/manifest.localization.js +67 -200
- package/lib/manifest.markdown.js +65 -174
- package/lib/manifest.min.css +1 -1
- package/lib/manifest.payments.js +40 -139
- package/lib/manifest.router.js +24 -73
- package/lib/manifest.status.js +20 -56
- package/lib/manifest.svg.js +12 -30
- package/lib/manifest.tooltips.js +28 -65
- package/lib/manifest.utilities.js +40 -107
- package/lib/manifest.virtual.js +15 -53
- package/package.json +1 -1
- package/lib/manifest.appwrite.presence.js +0 -1650
|
@@ -66,6 +66,8 @@ window.ManifestComponentsRegistry = {
|
|
|
66
66
|
});
|
|
67
67
|
if (res.ok) {
|
|
68
68
|
manifest = await res.json();
|
|
69
|
+
// No-loader path: resolve ${VAR} placeholders the dynamic loader would have.
|
|
70
|
+
window.ManifestDataConfig?.interpolateManifest?.(manifest);
|
|
69
71
|
} else {
|
|
70
72
|
console.warn('[Manifest] Failed to load manifest.json (HTTP', res.status + ')');
|
|
71
73
|
}
|
|
@@ -151,16 +153,9 @@ window.ManifestComponentsLoader = {
|
|
|
151
153
|
|
|
152
154
|
// Components processor
|
|
153
155
|
|
|
154
|
-
// Escape a
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
// when component templates use backtick literals (e.g. x-text="`Hi $modify('n')`"):
|
|
158
|
-
// - `\` → a trailing backslash would escape the closing quote
|
|
159
|
-
// - `` ` `` → terminates a backtick template literal
|
|
160
|
-
// - `${` → opens an interpolation that Alpine evaluates as JS
|
|
161
|
-
// Without escaping those, a bound value like `${alert(1)}` from a data
|
|
162
|
-
// source becomes code execution inside the wrapping template literal.
|
|
163
|
-
// Backslash must be escaped FIRST so the other replacements don't compound.
|
|
156
|
+
// Escape a value for safe interpolation in single-quoted JS strings AND
|
|
157
|
+
// backtick template literals. Backslash, backtick, and ${ must be escaped or a
|
|
158
|
+
// bound value like `${alert(1)}` becomes code execution; escape backslash FIRST.
|
|
164
159
|
function escapeForSingleQuotedJsString(s) {
|
|
165
160
|
return String(s)
|
|
166
161
|
.replace(/\\/g, '\\\\')
|
|
@@ -184,8 +179,7 @@ window.ManifestComponentsProcessor = {
|
|
|
184
179
|
return;
|
|
185
180
|
}
|
|
186
181
|
if (element.hasAttribute('data-pre-rendered') || element.hasAttribute('data-processed')) {
|
|
187
|
-
// Pre-rendered
|
|
188
|
-
// still needs Alpine initialization (x-data, @click, :class, x-color etc.).
|
|
182
|
+
// Pre-rendered content skips re-fetch but still needs Alpine init.
|
|
189
183
|
if (element.hasAttribute('data-pre-rendered') && window.Alpine && typeof window.Alpine.initTree === 'function') {
|
|
190
184
|
try { window.Alpine.initTree(element); } catch (e) { /* graceful */ }
|
|
191
185
|
}
|
|
@@ -226,10 +220,10 @@ window.ManifestComponentsProcessor = {
|
|
|
226
220
|
const props = {};
|
|
227
221
|
Array.from(element.attributes).forEach(attr => {
|
|
228
222
|
if (attr.name !== name && attr.name !== 'class' && !attr.name.startsWith('data-')) {
|
|
229
|
-
// Store
|
|
223
|
+
// Store original case and lowercase
|
|
230
224
|
props[attr.name] = attr.value;
|
|
231
225
|
props[attr.name.toLowerCase()] = attr.value;
|
|
232
|
-
//
|
|
226
|
+
// Alpine bindings (:foo): also store without the colon
|
|
233
227
|
if (attr.name.startsWith(':')) {
|
|
234
228
|
const keyWithoutColon = attr.name.substring(1);
|
|
235
229
|
props[keyWithoutColon] = attr.value;
|
|
@@ -292,32 +286,25 @@ window.ManifestComponentsProcessor = {
|
|
|
292
286
|
if (!val || val.trim() === '' || /^[\r\n\t\s]+$/.test(val)) {
|
|
293
287
|
return value.includes('||') ? 'null' : "''";
|
|
294
288
|
}
|
|
295
|
-
//
|
|
289
|
+
// $-prefixed values are Alpine expressions — don't quote.
|
|
296
290
|
if (val.startsWith('$')) {
|
|
297
|
-
//
|
|
298
|
-
//
|
|
291
|
+
// Guard $x data-source expressions on x-for/if/show against
|
|
292
|
+
// errors before the source loads: optional-chain + fallback.
|
|
299
293
|
if ((attr.name === 'x-for' || attr.name === 'x-if' || attr.name === 'x-show') && val.startsWith('$x') && !val.includes('??')) {
|
|
300
|
-
// Convert regular property access dots to optional chaining for safe navigation
|
|
301
294
|
let safeVal = val.replace(/\./g, '?.');
|
|
302
|
-
// Add fallback based on directive type (only if user hasn't already provided one)
|
|
303
295
|
if (attr.name === 'x-for') {
|
|
304
|
-
// x-for needs an iterable, so fallback to empty array
|
|
305
296
|
return `${safeVal} ?? []`;
|
|
306
297
|
} else {
|
|
307
|
-
// x-if and x-show evaluate to boolean, fallback to false
|
|
308
298
|
return `${safeVal} ?? false`;
|
|
309
299
|
}
|
|
310
300
|
}
|
|
311
301
|
return val;
|
|
312
302
|
}
|
|
313
|
-
//
|
|
314
|
-
// that reference data sources or other dynamic content
|
|
303
|
+
// x-for/if/show can hold expressions (e.g. "card in $x.data.items") — preserve as-is.
|
|
315
304
|
if (attr.name === 'x-for' || attr.name === 'x-if' || attr.name === 'x-show') {
|
|
316
|
-
// For these directives, preserve the value as-is to allow Alpine to evaluate it
|
|
317
|
-
// This is critical for x-for expressions like "card in $x.data.items"
|
|
318
305
|
return val;
|
|
319
306
|
}
|
|
320
|
-
//
|
|
307
|
+
// Quote everything else so it's treated as a string.
|
|
321
308
|
return `'${escapeForSingleQuotedJsString(val)}'`;
|
|
322
309
|
}
|
|
323
310
|
);
|
|
@@ -369,19 +356,16 @@ window.ManifestComponentsProcessor = {
|
|
|
369
356
|
rootElement.setAttribute('data-component', instanceId);
|
|
370
357
|
}
|
|
371
358
|
});
|
|
372
|
-
//
|
|
373
|
-
//
|
|
374
|
-
// skipped by the first loop due to conditions. Classes are already handled in the first loop, so we skip them here.
|
|
359
|
+
// Copy any placeholder attributes the first loop skipped onto the first
|
|
360
|
+
// root element (classes already handled there, so skip them).
|
|
375
361
|
if (topLevelElements.length > 0) {
|
|
376
362
|
const firstRoot = topLevelElements[0];
|
|
377
363
|
Array.from(element.attributes).forEach(attr => {
|
|
378
|
-
// Skip attributes that were already handled in the first loop
|
|
379
|
-
// Classes are always handled in the first loop, so skip them here to avoid duplication
|
|
380
364
|
if (attr.name === 'class') {
|
|
381
|
-
return;
|
|
365
|
+
return;
|
|
382
366
|
}
|
|
383
367
|
|
|
384
|
-
//
|
|
368
|
+
// Routing/data attributes to preserve
|
|
385
369
|
const preserveAttributes = [
|
|
386
370
|
'data-order', 'x-route', 'data-component', 'data-head',
|
|
387
371
|
'x-route-*', 'data-route-*', 'x-tabpanel'
|
|
@@ -396,32 +380,25 @@ window.ManifestComponentsProcessor = {
|
|
|
396
380
|
(attr.name !== name && !attr.name.startsWith('data-')) ||
|
|
397
381
|
attr.name === 'data-order' || attr.name === 'x-route' || attr.name === 'data-head';
|
|
398
382
|
|
|
399
|
-
//
|
|
383
|
+
// Apply if unhandled or preserved, and not in the skip list.
|
|
400
384
|
if ((!alreadyHandledInFirstLoop || shouldPreserve) &&
|
|
401
385
|
!['data-original-placeholder', 'data-pre-rendered', 'data-processed'].includes(attr.name)) {
|
|
402
386
|
if (attr.name.startsWith('x-') || attr.name.startsWith(':') || attr.name.startsWith('@')) {
|
|
403
|
-
//
|
|
387
|
+
// x-data: merge two object literals; otherwise replace.
|
|
404
388
|
if (attr.name === 'x-data' && firstRoot.hasAttribute('x-data')) {
|
|
405
|
-
// For x-data, we need to merge the objects - this is complex, so for now we'll append
|
|
406
|
-
// The user should structure their x-data to avoid conflicts
|
|
407
389
|
const existing = firstRoot.getAttribute('x-data');
|
|
408
|
-
// If both are objects, try to merge them
|
|
409
390
|
if (existing.trim().startsWith('{') && attr.value.trim().startsWith('{')) {
|
|
410
|
-
// Remove outer braces and merge
|
|
411
391
|
const existingContent = existing.trim().slice(1, -1).trim();
|
|
412
392
|
const newContent = attr.value.trim().slice(1, -1).trim();
|
|
413
393
|
const merged = `{ ${existingContent}${existingContent && newContent ? ', ' : ''}${newContent} }`;
|
|
414
394
|
firstRoot.setAttribute('x-data', merged);
|
|
415
395
|
} else {
|
|
416
|
-
// If not both objects, replace (user should handle this case)
|
|
417
396
|
firstRoot.setAttribute(attr.name, attr.value);
|
|
418
397
|
}
|
|
419
398
|
} else {
|
|
420
|
-
// For other Alpine directives, replace if they exist
|
|
421
399
|
firstRoot.setAttribute(attr.name, attr.value);
|
|
422
400
|
}
|
|
423
401
|
} else {
|
|
424
|
-
// For other attributes, replace if they exist
|
|
425
402
|
firstRoot.setAttribute(attr.name, attr.value);
|
|
426
403
|
}
|
|
427
404
|
}
|
|
@@ -435,18 +412,14 @@ window.ManifestComponentsProcessor = {
|
|
|
435
412
|
const fragment = document.createDocumentFragment();
|
|
436
413
|
topLevelElements.forEach(el => fragment.appendChild(el));
|
|
437
414
|
|
|
438
|
-
// Replace the placeholder element with the component content
|
|
439
|
-
// Alpine will auto-initialize on DOM insertion, but we need to ensure
|
|
440
|
-
// magic methods are ready first. If data plugin is ready, give it a tick
|
|
441
|
-
// to ensure Alpine has processed the magic method registration.
|
|
442
415
|
parent.replaceChild(fragment, element);
|
|
443
416
|
|
|
444
|
-
// Manually
|
|
445
|
-
//
|
|
417
|
+
// Manually init Alpine on the swapped-in elements once magic methods are
|
|
418
|
+
// ready — prevents "i is not a function" errors.
|
|
446
419
|
if (window.Alpine && typeof window.Alpine.initTree === 'function') {
|
|
447
420
|
const initAlpine = () => {
|
|
448
|
-
//
|
|
449
|
-
//
|
|
421
|
+
// Init auth convenience methods before Alpine evaluates expressions,
|
|
422
|
+
// else "$auth.isCreatingTeam is not a function" after reinit.
|
|
450
423
|
if (window.ManifestAppwriteAuthTeamsConvenience && window.ManifestAppwriteAuthTeamsConvenience.initialize) {
|
|
451
424
|
try {
|
|
452
425
|
const authStore = window.Alpine.store('auth');
|
|
@@ -458,10 +431,8 @@ window.ManifestComponentsProcessor = {
|
|
|
458
431
|
}
|
|
459
432
|
}
|
|
460
433
|
|
|
461
|
-
// Re-initialize Alpine on the swapped elements
|
|
462
|
-
// This ensures magic methods are available when expressions are evaluated
|
|
463
434
|
topLevelElements.forEach(el => {
|
|
464
|
-
if (!el.__x) {
|
|
435
|
+
if (!el.__x) {
|
|
465
436
|
try {
|
|
466
437
|
window.Alpine.initTree(el);
|
|
467
438
|
} catch (e) {
|
|
@@ -471,7 +442,7 @@ window.ManifestComponentsProcessor = {
|
|
|
471
442
|
});
|
|
472
443
|
};
|
|
473
444
|
|
|
474
|
-
// If data plugin is ready, wait a tick
|
|
445
|
+
// If the data plugin is ready, wait a tick for its magic method.
|
|
475
446
|
if (window.__manifestDataMagicRegistered) {
|
|
476
447
|
if (window.Alpine.nextTick) {
|
|
477
448
|
window.Alpine.nextTick(initAlpine);
|
|
@@ -479,18 +450,16 @@ window.ManifestComponentsProcessor = {
|
|
|
479
450
|
setTimeout(initAlpine, 0);
|
|
480
451
|
}
|
|
481
452
|
} else {
|
|
482
|
-
// Data plugin not ready, initialize immediately (will fail gracefully)
|
|
483
453
|
initAlpine();
|
|
484
454
|
}
|
|
485
455
|
}
|
|
486
456
|
|
|
487
|
-
// Execute scripts after
|
|
457
|
+
// Execute component scripts after render (small delay for the DOM swap)
|
|
488
458
|
if (scripts.length > 0) {
|
|
489
|
-
// Use a small delay to ensure DOM is updated
|
|
490
459
|
setTimeout(() => {
|
|
491
460
|
scripts.forEach(script => {
|
|
492
461
|
if (script.src) {
|
|
493
|
-
// External script
|
|
462
|
+
// External script → append to head
|
|
494
463
|
const scriptEl = document.createElement('script');
|
|
495
464
|
scriptEl.src = script.src;
|
|
496
465
|
scriptEl.type = script.type;
|
|
@@ -498,9 +467,8 @@ window.ManifestComponentsProcessor = {
|
|
|
498
467
|
if (script.defer) scriptEl.defer = true;
|
|
499
468
|
document.head.appendChild(scriptEl);
|
|
500
469
|
} else if (script.content) {
|
|
501
|
-
// Inline script
|
|
470
|
+
// Inline script → run in global scope
|
|
502
471
|
try {
|
|
503
|
-
// Create a function to execute the script in the global scope
|
|
504
472
|
const executeScript = new Function(script.content);
|
|
505
473
|
executeScript();
|
|
506
474
|
} catch (error) {
|
|
@@ -799,25 +767,7 @@ window.ManifestComponentsMutation = {
|
|
|
799
767
|
}
|
|
800
768
|
};
|
|
801
769
|
|
|
802
|
-
|
|
803
|
-
//
|
|
804
|
-
// Two enhancements that run on top of the existing on-encounter loader:
|
|
805
|
-
//
|
|
806
|
-
// 1. Parallel batch on route change. When manifest:route-change fires,
|
|
807
|
-
// scan the [x-route] subtrees that match the new route and call
|
|
808
|
-
// loadComponent() on every <x-*> tag inside them. The loader
|
|
809
|
-
// deduplicates fetches, so calling it for components that the
|
|
810
|
-
// regular swapping logic is already mounting is harmless — but
|
|
811
|
-
// pre-issuing in parallel saves 50–200 ms vs. one-by-one fetches.
|
|
812
|
-
//
|
|
813
|
-
// 2. Prefetch on hover. When the pointer enters an internal <a href>,
|
|
814
|
-
// derive the target pathname, find the [x-route] subtree(s) that
|
|
815
|
-
// would match it, and prefetch their components. By the time the
|
|
816
|
-
// user clicks the link, the components are warm in the loader's
|
|
817
|
-
// cache and navigation feels instant.
|
|
818
|
-
//
|
|
819
|
-
// Both phases require zero author configuration. Manifest auto-discovers
|
|
820
|
-
// what to prefetch from the existing [x-route] DOM structure.
|
|
770
|
+
/* Manifest Components — route-level prefetch (batch on route change + on hover) */
|
|
821
771
|
|
|
822
772
|
(function () {
|
|
823
773
|
'use strict';
|
|
@@ -825,20 +775,15 @@ window.ManifestComponentsMutation = {
|
|
|
825
775
|
// <x-*> tag pattern — lowercase, hyphenated.
|
|
826
776
|
const TAG_RE = /^x-[a-z][a-z0-9-]*$/;
|
|
827
777
|
|
|
828
|
-
// Framework
|
|
829
|
-
// themselves, not as project components in manifest.json). Skip these
|
|
830
|
-
// when scanning for project components to prefetch.
|
|
778
|
+
// Framework web components (not project components) — skip when scanning.
|
|
831
779
|
const FRAMEWORK_TAGS = new Set(['code', 'code-group']);
|
|
832
780
|
|
|
833
|
-
// Anchors
|
|
834
|
-
// garbage-collects naturally as elements leave the tree.
|
|
781
|
+
// Anchors already hover-prefetched. WeakSet so detached nodes GC naturally.
|
|
835
782
|
const prefetchedAnchors = new WeakSet();
|
|
836
783
|
|
|
837
784
|
function loader() { return window.ManifestComponentsLoader; }
|
|
838
785
|
|
|
839
|
-
// Match a
|
|
840
|
-
// leading/trailing slashes, '/' represented as '/'). Mirrors the
|
|
841
|
-
// router visibility logic so prefetch targets the same subtrees.
|
|
786
|
+
// Match a route pattern against a normalized pathname. Mirrors router visibility.
|
|
842
787
|
function routeMatches(routeValue, pathname) {
|
|
843
788
|
const pieces = String(routeValue || '').split(',').map((s) => s.trim()).filter(Boolean);
|
|
844
789
|
let matched = false;
|
|
@@ -877,10 +822,7 @@ window.ManifestComponentsMutation = {
|
|
|
877
822
|
function discoverComponentNames(root) {
|
|
878
823
|
const names = new Set();
|
|
879
824
|
if (!root || !root.querySelectorAll) return names;
|
|
880
|
-
//
|
|
881
|
-
// We filter by tag name in JS — there's no CSS selector for "tag
|
|
882
|
-
// name starts with x-". A page typically has a few thousand nodes,
|
|
883
|
-
// which scans in well under a millisecond.
|
|
825
|
+
// No CSS selector for "tag starts with x-", so scan all and filter in JS.
|
|
884
826
|
root.querySelectorAll('*').forEach((el) => {
|
|
885
827
|
const tag = el.tagName.toLowerCase();
|
|
886
828
|
if (!tag.startsWith('x-') || !TAG_RE.test(tag)) return;
|
|
@@ -925,10 +867,7 @@ window.ManifestComponentsMutation = {
|
|
|
925
867
|
prefetchForRoute(pathname);
|
|
926
868
|
});
|
|
927
869
|
|
|
928
|
-
// 2) Hover prefetch.
|
|
929
|
-
// anchor on each event so we get a single trigger per anchor entry
|
|
930
|
-
// without needing pointerenter (which doesn't bubble). Dedup via
|
|
931
|
-
// a WeakSet so repeat moves within the anchor don't re-scan.
|
|
870
|
+
// 2) Hover prefetch. pointerover bubbles (pointerenter doesn't); WeakSet dedups.
|
|
932
871
|
document.addEventListener('pointerover', (e) => {
|
|
933
872
|
if (!e.target || !e.target.closest) return;
|
|
934
873
|
const a = e.target.closest('a[href]');
|
package/lib/manifest.css
CHANGED
|
@@ -3233,138 +3233,6 @@
|
|
|
3233
3233
|
}
|
|
3234
3234
|
}
|
|
3235
3235
|
|
|
3236
|
-
/* Manifest Presence - Visual Indicators */
|
|
3237
|
-
|
|
3238
|
-
/* CSS Variables - move to manifest.theme.css after testing completes */
|
|
3239
|
-
:root {
|
|
3240
|
-
/* Focus indicator */
|
|
3241
|
-
--presence-focus-outline-width: 2px;
|
|
3242
|
-
--presence-focus-outline-style: dashed;
|
|
3243
|
-
--presence-focus-outline-offset: 2px;
|
|
3244
|
-
--presence-focus-opacity: 0.6;
|
|
3245
|
-
--presence-focus-color: #3b82f6;
|
|
3246
|
-
|
|
3247
|
-
/* Text caret indicator */
|
|
3248
|
-
--presence-caret-width: 2px;
|
|
3249
|
-
--presence-caret-height: 1.2em;
|
|
3250
|
-
--presence-caret-color: #3b82f6;
|
|
3251
|
-
--presence-caret-z-index: 1000;
|
|
3252
|
-
--presence-caret-blink-duration: 1s;
|
|
3253
|
-
--presence-caret-opacity-high: 1;
|
|
3254
|
-
--presence-caret-opacity-low: 0.3;
|
|
3255
|
-
|
|
3256
|
-
/* Text selection highlight */
|
|
3257
|
-
--presence-selection-color: #3b82f6;
|
|
3258
|
-
--presence-selection-opacity: 0.2;
|
|
3259
|
-
--presence-selection-z-index: 999;
|
|
3260
|
-
|
|
3261
|
-
/* Cursor indicator (if rendered) */
|
|
3262
|
-
--presence-cursor-size: 8px;
|
|
3263
|
-
--presence-cursor-border-width: 2px;
|
|
3264
|
-
--presence-cursor-z-index: 1001;
|
|
3265
|
-
|
|
3266
|
-
/* Timing and threshold configuration (read by JavaScript) */
|
|
3267
|
-
--presence-throttle: 300ms;
|
|
3268
|
-
--presence-cleanup-interval: 30000ms;
|
|
3269
|
-
--presence-min-change-threshold: 5px;
|
|
3270
|
-
--presence-idle-threshold: 5000ms;
|
|
3271
|
-
}
|
|
3272
|
-
|
|
3273
|
-
@layer elements {
|
|
3274
|
-
|
|
3275
|
-
/* Focus indicator - applied to elements focused by other users */
|
|
3276
|
-
:where(.presence-focused) {
|
|
3277
|
-
outline: var(--presence-focus-outline-width) var(--presence-focus-outline-style) var(--presence-focus-color);
|
|
3278
|
-
outline-offset: var(--presence-focus-outline-offset);
|
|
3279
|
-
opacity: var(--presence-focus-opacity);
|
|
3280
|
-
}
|
|
3281
|
-
|
|
3282
|
-
/* Dynamic focus color via data attribute */
|
|
3283
|
-
:where(.presence-focused[data-presence-focus-color]) {
|
|
3284
|
-
--presence-focus-color: attr(data-presence-focus-color);
|
|
3285
|
-
outline-color: var(--presence-focus-color);
|
|
3286
|
-
}
|
|
3287
|
-
|
|
3288
|
-
/* Caret indicator for other users' text cursors */
|
|
3289
|
-
:where(.presence-caret) {
|
|
3290
|
-
position: absolute;
|
|
3291
|
-
width: var(--presence-caret-width);
|
|
3292
|
-
height: var(--presence-caret-height);
|
|
3293
|
-
background-color: var(--presence-caret-color);
|
|
3294
|
-
pointer-events: none;
|
|
3295
|
-
z-index: var(--presence-caret-z-index);
|
|
3296
|
-
animation: presence-caret-blink var(--presence-caret-blink-duration) infinite;
|
|
3297
|
-
}
|
|
3298
|
-
|
|
3299
|
-
/* Dynamic caret color via data attribute */
|
|
3300
|
-
:where(.presence-caret[data-presence-caret-color]),
|
|
3301
|
-
[data-presence-caret-color] .presence-caret {
|
|
3302
|
-
--presence-caret-color: attr(data-presence-caret-color);
|
|
3303
|
-
}
|
|
3304
|
-
|
|
3305
|
-
/* Caret blink animation */
|
|
3306
|
-
@keyframes presence-caret-blink {
|
|
3307
|
-
|
|
3308
|
-
0%,
|
|
3309
|
-
50% {
|
|
3310
|
-
opacity: var(--presence-caret-opacity-high);
|
|
3311
|
-
}
|
|
3312
|
-
|
|
3313
|
-
51%,
|
|
3314
|
-
100% {
|
|
3315
|
-
opacity: var(--presence-caret-opacity-low);
|
|
3316
|
-
}
|
|
3317
|
-
}
|
|
3318
|
-
|
|
3319
|
-
/* Selection highlight for other users' text selections */
|
|
3320
|
-
:where(.presence-selection) {
|
|
3321
|
-
position: absolute;
|
|
3322
|
-
background-color: var(--presence-selection-color);
|
|
3323
|
-
opacity: var(--presence-selection-opacity);
|
|
3324
|
-
pointer-events: none;
|
|
3325
|
-
z-index: var(--presence-selection-z-index);
|
|
3326
|
-
}
|
|
3327
|
-
|
|
3328
|
-
/* Dynamic selection color - plugin sets CSS variable via setProperty */
|
|
3329
|
-
:where(.presence-selection) {
|
|
3330
|
-
background-color: var(--presence-selection-color);
|
|
3331
|
-
}
|
|
3332
|
-
|
|
3333
|
-
/* Cursor indicator (for mouse/touch cursors) */
|
|
3334
|
-
:where(.presence-cursor) {
|
|
3335
|
-
position: absolute;
|
|
3336
|
-
width: var(--presence-cursor-size);
|
|
3337
|
-
height: var(--presence-cursor-size);
|
|
3338
|
-
border: var(--presence-cursor-border-width) solid var(--presence-cursor-color, var(--presence-focus-color));
|
|
3339
|
-
border-radius: 50%;
|
|
3340
|
-
background-color: var(--presence-cursor-color, var(--presence-focus-color));
|
|
3341
|
-
pointer-events: none;
|
|
3342
|
-
z-index: var(--presence-cursor-z-index);
|
|
3343
|
-
transform: translate(-50%, -50%);
|
|
3344
|
-
}
|
|
3345
|
-
|
|
3346
|
-
/* Cursor label (user name) */
|
|
3347
|
-
:where(.presence-cursor-label) {
|
|
3348
|
-
position: absolute;
|
|
3349
|
-
top: calc(var(--presence-cursor-size) + 4px);
|
|
3350
|
-
left: 50%;
|
|
3351
|
-
transform: translateX(-50%);
|
|
3352
|
-
background-color: var(--presence-cursor-color, var(--presence-focus-color));
|
|
3353
|
-
color: oklch(98.5% 0 0);
|
|
3354
|
-
padding: 2px 6px;
|
|
3355
|
-
border-radius: 4px;
|
|
3356
|
-
font-size: 12px;
|
|
3357
|
-
white-space: nowrap;
|
|
3358
|
-
pointer-events: none;
|
|
3359
|
-
z-index: calc(var(--presence-cursor-z-index) + 1);
|
|
3360
|
-
}
|
|
3361
|
-
|
|
3362
|
-
/* Element that contains presence indicators */
|
|
3363
|
-
:where([data-presence-caret-user], [data-presence-selection-user], [data-presence-focus-user]) {
|
|
3364
|
-
position: relative;
|
|
3365
|
-
}
|
|
3366
|
-
}
|
|
3367
|
-
|
|
3368
3236
|
/* Manifest Radios */
|
|
3369
3237
|
|
|
3370
3238
|
@layer components {
|