also looking at this
feat: native trail maps and complete native mobile screens
#1017
60 files
+2505
-823
Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.
| @@ -8,11 +8,11 @@ | ||
| 8 | 8 | "prod": "bun --hot run dist/dev.js" |
| 9 | 9 | }, |
| 10 | 10 | "dependencies": { |
| 11 | "@stacksjs/logging": "^0.75.219", | |
| 12 | "@stacksjs/queue": "^0.75.219", | |
| 13 | "@stacksjs/router": "^0.75.219", | |
| 14 | "@stacksjs/storage": "^0.75.219", | |
| 15 | "@stacksjs/utils": "^0.75.219" | |
| 11 | "@stacksjs/logging": "^0.75.221", | |
| 12 | "@stacksjs/queue": "^0.75.221", | |
| 13 | "@stacksjs/router": "^0.75.221", | |
| 14 | "@stacksjs/storage": "^0.75.221", | |
| 15 | "@stacksjs/utils": "^0.75.221" | |
| 16 | 16 | }, |
| 17 | 17 | "workspaces": [ |
| 18 | 18 | "../core/*" |
| @@ -73,6 +73,7 @@ export { nativeDestination, nativeShortcutDestination } from '../../../resources | ||
| 73 | 73 | export { exportNativeGpx } from '../../../resources/functions/native-export' |
| 74 | 74 | export { nativeMapParts, nativeRouteRegion, nativeZoomRegion } from '../../../resources/functions/native-map' |
| 75 | 75 | export { nativeOAuthCallback } from '../../../resources/functions/native-oauth' |
| 76 | export { createNativeRecordingCheckpoint } from '../../../resources/functions/native-recording-checkpoint' | |
| 76 | 77 | export { nativeRecordingDuration, movingSeconds, recordingSamples, recordingFigures, recordingPayload } from '../../../resources/functions/native-recording' |
| 77 | 78 | export type { NativeRecording } from '../../../resources/functions/native-recording' |
| 78 | 79 | export { returnToServerFromBundledCopy, serverPath, serverURLFor, holdOnBundledPage, NATIVE_REMOTE_FILE, NATIVE_REMOTE_RETRY_MS, NATIVE_REMOTE_PROBE_TIMEOUT_MS } from '../../../resources/functions/native-remote' |
| @@ -136,7 +137,7 @@ export { normalizeTagKey, readTags, featureFromTag, trailFeatures, trailHighligh | ||
| 136 | 137 | export type { TrailFeature, TrailNotice, FeatureTrail } from '../../../resources/functions/trail-features' |
| 137 | 138 | export { routePartsFromSegments, lineLengthMeters, encodeRouteParts, decodeRouteParts, primaryRoutePart, ROUTE_SNAP_METERS } from '../../../resources/functions/trail-geometry' |
| 138 | 139 | export type { RoutePair, RouteNetwork } from '../../../resources/functions/trail-geometry' |
| 139 | export { trailSegmentMiles, trailDirectionSigns, trailSurfaceTypes, trailGpx } from '../../../resources/functions/trail-map' | |
| 140 | export { trailheadPosition, trailSegmentMiles, trailDirectionSigns, trailSurfaceTypes, trailGpx, trailRouteStyle } from '../../../resources/functions/trail-map' | |
| 140 | 141 | export { placeOfLocation, whereLocation, wholeTrailFigures, trailFacts, countryLabel, trailBreadcrumbs, trailFaq, exploreNearLinks, ratingSummary, forecastDayLabel, forecastBar, NEAR_PLACE_PREFIX } from '../../../resources/functions/trail-page' |
| 141 | 142 | export type { PageTrail, TrailFigures, Crumb, FaqEntry, LinkGroup, RatingSummary } from '../../../resources/functions/trail-page' |
| 142 | 143 | export { parsePhotoList, trailGallery } from '../../../resources/functions/trail-photos' |
| @@ -295,6 +295,7 @@ declare global { | ||
| 295 | 295 | const nativeRouteRegion: typeof autoImports.nativeRouteRegion |
| 296 | 296 | const nativeZoomRegion: typeof autoImports.nativeZoomRegion |
| 297 | 297 | const nativeOAuthCallback: typeof autoImports.nativeOAuthCallback |
| 298 | const createNativeRecordingCheckpoint: typeof autoImports.createNativeRecordingCheckpoint | |
| 298 | 299 | const nativeRecordingDuration: typeof autoImports.nativeRecordingDuration |
| 299 | 300 | const movingSeconds: typeof autoImports.movingSeconds |
| 300 | 301 | const recordingSamples: typeof autoImports.recordingSamples |
| @@ -475,10 +476,12 @@ declare global { | ||
| 475 | 476 | const decodeRouteParts: typeof autoImports.decodeRouteParts |
| 476 | 477 | const primaryRoutePart: typeof autoImports.primaryRoutePart |
| 477 | 478 | const ROUTE_SNAP_METERS: typeof autoImports.ROUTE_SNAP_METERS |
| 479 | const trailheadPosition: typeof autoImports.trailheadPosition | |
| 478 | 480 | const trailSegmentMiles: typeof autoImports.trailSegmentMiles |
| 479 | 481 | const trailDirectionSigns: typeof autoImports.trailDirectionSigns |
| 480 | 482 | const trailSurfaceTypes: typeof autoImports.trailSurfaceTypes |
| 481 | 483 | const trailGpx: typeof autoImports.trailGpx |
| 484 | const trailRouteStyle: typeof autoImports.trailRouteStyle | |
| 482 | 485 | const placeOfLocation: typeof autoImports.placeOfLocation |
| 483 | 486 | const whereLocation: typeof autoImports.whereLocation |
| 484 | 487 | const wholeTrailFigures: typeof autoImports.wholeTrailFigures |
| @@ -46,19 +46,19 @@ | ||
| 46 | 46 | "dev": "./dev local" |
| 47 | 47 | }, |
| 48 | 48 | "dependencies": { |
| 49 | "@stacksjs/config": "^0.75.219", | |
| 50 | "@stacksjs/dns": "^0.75.219", | |
| 51 | "@stacksjs/logging": "^0.75.219", | |
| 52 | "@stacksjs/path": "^0.75.219", | |
| 53 | "@stacksjs/queue": "^0.75.219", | |
| 54 | "@stacksjs/router": "^0.75.219", | |
| 55 | "@stacksjs/storage": "^0.75.219", | |
| 56 | "@stacksjs/utils": "^0.75.219", | |
| 57 | "@stacksjs/validation": "^0.75.219" | |
| 49 | "@stacksjs/config": "^0.75.221", | |
| 50 | "@stacksjs/dns": "^0.75.221", | |
| 51 | "@stacksjs/logging": "^0.75.221", | |
| 52 | "@stacksjs/path": "^0.75.221", | |
| 53 | "@stacksjs/queue": "^0.75.221", | |
| 54 | "@stacksjs/router": "^0.75.221", | |
| 55 | "@stacksjs/storage": "^0.75.221", | |
| 56 | "@stacksjs/utils": "^0.75.221", | |
| 57 | "@stacksjs/validation": "^0.75.221" | |
| 58 | 58 | }, |
| 59 | 59 | "devDependencies": { |
| 60 | "@stacksjs/cli": "^0.75.219", | |
| 60 | "@stacksjs/cli": "^0.75.221", | |
| 61 | 61 | "better-dx": "^0.2.25", |
| 62 | "@stacksjs/env": "^0.75.219" | |
| 62 | "@stacksjs/env": "^0.75.221" | |
| 63 | 63 | } |
| 64 | 64 | } |
| @@ -233,6 +233,12 @@ buddy env:check # validate env configuration | ||
| 233 | 233 | buddy env:check --file .env.production # check a specific environment's file |
| 234 | 234 | ``` |
| 235 | 235 | |
| 236 | `getEnv()` fails a whole-file read if any encrypted entry cannot be decrypted. | |
| 237 | Restore the environment's matching private key through `.env.keys` or its | |
| 238 | `DOTENV_PRIVATE_KEY_<ENVIRONMENT>` process secret. A request for one plaintext | |
| 239 | key remains readable independently. `buddy deploy` stops on an incomplete or | |
| 240 | unreadable environment instead of deploying a partial map. | |
| 241 | ||
| 236 | 242 | ## Tenant isolation on a shared box |
| 237 | 243 | |
| 238 | 244 | When several projects share one server - one owns it, the rest attach with |
| @@ -83,6 +83,28 @@ Based on `cz-git` UserConfig: | ||
| 83 | 83 | |
| 84 | 84 | ## Git Utilities |
| 85 | 85 | |
| 86 | `gitCredentialEnvironment({ url, username, password })` from `@stacksjs/git` | |
| 87 | returns environment variables for a child Git process. Pass the caller's base | |
| 88 | environment as its second argument, then merge the result after that base. | |
| 89 | Existing runtime Git configuration is preserved. The HTTP(S) URL must identify one repository without | |
| 90 | userinfo, query or fragment. The helper supplies credentials only for that | |
| 91 | protocol, host and repository path, refuses redirects for that URL, disables | |
| 92 | interactive prompts, and writes no credential or Git config file. Never log | |
| 93 | the returned environment. Use a credential scoped to the repository and the | |
| 94 | operation; this helper does not grant server permissions. | |
| 95 | ||
| 96 | ```typescript | |
| 97 | import { gitCredentialEnvironment } from '@stacksjs/git' | |
| 98 | ||
| 99 | const child = Bun.spawn(['git', 'ls-remote', 'origin'], { | |
| 100 | cwd: workspace, | |
| 101 | env: { ...process.env, ...gitCredentialEnvironment({ url: repositoryUrl, username: 'x-access-token', password: jobToken }, process.env) }, | |
| 102 | }) | |
| 103 | ``` | |
| 104 | ||
| 105 | The child and its descendants receive the credential. Use this for trusted | |
| 106 | job steps, never to transfer a host credential into an isolated guest. | |
| 107 | ||
| 86 | 108 | `buddy release` atomically pushes its commit and version tag to the branch's |
| 87 | 109 | configured upstream remote and branch. A worktree on `codex/example` tracking |
| 88 | 110 | `origin/main` releases to `origin/main`; it does not create a remote feature |
| @@ -21,7 +21,7 @@ Browser runtime bindings are documented separately in `stacks-auto-imports`. | ||
| 21 | 21 | | `@stacksjs/auth` | [`stacks-auth`](../stacks-auth/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/auth/src/index.ts) | 115 | |
| 22 | 22 | | `@stacksjs/browser` | [`stacks-browser`](../stacks-browser/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/browser/src/index.ts) | 8 | |
| 23 | 23 | | `@stacksjs/browser-extension` | [`stacks-browser-extension`](../stacks-browser-extension/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/browser-extension/src/index.ts) | 7 | |
| 24 | | `@stacksjs/buddy` | [`stacks-buddy`](../stacks-buddy/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/buddy/src/index.ts) | 149 | | |
| 24 | | `@stacksjs/buddy` | [`stacks-buddy`](../stacks-buddy/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/buddy/src/index.ts) | 150 | | |
| 25 | 25 | | `@stacksjs/build` | [`stacks-build`](../stacks-build/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/build/src/index.ts) | 2 | |
| 26 | 26 | | `@stacksjs/cache` | [`stacks-cache`](../stacks-cache/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/cache/src/index.ts) | 11 | |
| 27 | 27 | | `@stacksjs/calendar-api` | [`stacks-calendar`](../stacks-calendar/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/calendar-api/src/index.ts) | 2 | |
| @@ -45,14 +45,14 @@ Browser runtime bindings are documented separately in `stacks-auto-imports`. | ||
| 45 | 45 | | `@stacksjs/docs` | [`stacks-docs`](../stacks-docs/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/docs/src/index.ts) | 2 | |
| 46 | 46 | | `@stacksjs/email` | [`stacks-email`](../stacks-email/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/email/src/index.ts) | 21 | |
| 47 | 47 | | `@stacksjs/enums` | [`stacks-enums`](../stacks-enums/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/enums/src/index.ts) | 3 | |
| 48 | | `@stacksjs/env` | [`stacks-env`](../stacks-env/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/index.ts) | 27 | | |
| 48 | | `@stacksjs/env` | [`stacks-env`](../stacks-env/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/index.ts) | 28 | | |
| 49 | 49 | | `@stacksjs/error-handling` | [`stacks-error-handling`](../stacks-error-handling/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/error-handling/src/index.ts) | 10 | |
| 50 | 50 | | `@stacksjs/events` | [`stacks-events`](../stacks-events/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/events/src/index.ts) | 12 | |
| 51 | 51 | | `@stacksjs/faker` | [`stacks-faker`](../stacks-faker/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/faker/src/index.ts) | 1 | |
| 52 | 52 | | `@stacksjs/feature-flags` | [`stacks-feature-flags`](../stacks-feature-flags/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/feature-flags/src/index.ts) | 7 | |
| 53 | 53 | | `@stacksjs/features` | [`stacks-features`](../stacks-features/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/features/src/index.ts) | 1 | |
| 54 | 54 | | `@stacksjs/forms` | [`stacks-forms`](../stacks-forms/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/forms/src/index.ts) | 4 | |
| 55 | | `@stacksjs/git` | [`stacks-git`](../stacks-git/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/git/src/index.ts) | 1 | | |
| 55 | | `@stacksjs/git` | [`stacks-git`](../stacks-git/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/git/src/index.ts) | 2 | | |
| 56 | 56 | | `@stacksjs/github` | [`stacks-git`](../stacks-git/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/github/src/index.ts) | 7 | |
| 57 | 57 | | `@stacksjs/health` | [`stacks-health`](../stacks-health/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/health/src/index.ts) | 2 | |
| 58 | 58 | | `@stacksjs/http` | [`stacks-http`](../stacks-http/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/http/src/index.ts) | 2 | |
| @@ -92,7 +92,7 @@ Browser runtime bindings are documented separately in `stacks-auto-imports`. | ||
| 92 | 92 | | `@stacksjs/tinker` | [`stacks-repl`](../stacks-repl/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/tinker/src/index.ts) | 1 | |
| 93 | 93 | | `@stacksjs/tunnel` | [`stacks-tunnel`](../stacks-tunnel/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/tunnel/src/index.ts) | 1 | |
| 94 | 94 | | `@stacksjs/types` | [`stacks-types`](../stacks-types/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/types/src/index.ts) | 2 | |
| 95 | | `@stacksjs/ui` | [`stacks-ui`](../stacks-ui/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/ui/src/index.ts) | 5 | | |
| 95 | | `@stacksjs/ui` | [`stacks-ui`](../stacks-ui/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/ui/src/index.ts) | 6 | | |
| 96 | 96 | | `@stacksjs/utils` | [`stacks-utils`](../stacks-utils/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/utils/src/index.ts) | 15 | |
| 97 | 97 | | `@stacksjs/validation` | [`stacks-validation`](../stacks-validation/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/validation/src/index.ts) | 17 | |
| 98 | 98 | | `@stacksjs/video` | [`stacks-video`](../stacks-video/SKILL.md) | [entrypoint](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/video/src/index.ts) | 3 | |
| @@ -215,7 +215,7 @@ Read the entrypoint and package export map for re-exported or type-only surfaces | ||
| 215 | 215 | |
| 216 | 216 | Local source modules: [src/commands/index.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/buddy/src/commands/index.ts). |
| 217 | 217 | |
| 218 | Evidence directory: [149 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/buddy/tests). These files are evidence pointers, not a claim that all tests were run for this catalog. | |
| 218 | Evidence directory: [150 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/buddy/tests). These files are evidence pointers, not a claim that all tests were run for this catalog. | |
| 219 | 219 | |
| 220 | 220 | ### @stacksjs/build |
| 221 | 221 | |
| @@ -443,7 +443,7 @@ Read the entrypoint and package export map for re-exported or type-only surfaces | ||
| 443 | 443 | |
| 444 | 444 | Local source modules: [src/cli.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/cli.ts), [src/crypto.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/crypto.ts), [src/deployment.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/deployment.ts), [src/integrations.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/integrations.ts), [src/numeric-keys.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/numeric-keys.ts), [src/parser.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/parser.ts), [src/plaintext.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/plaintext.ts), [src/plugin.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/plugin.ts), [src/runtime.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/runtime.ts), [src/tenants.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/tenants.ts), [src/types.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/types.ts), [src/utils.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/src/utils.ts). |
| 445 | 445 | |
| 446 | Evidence directory: [27 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/tests). These files are evidence pointers, not a claim that all tests were run for this catalog. | |
| 446 | Evidence directory: [28 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/env/tests). These files are evidence pointers, not a claim that all tests were run for this catalog. | |
| 447 | 447 | |
| 448 | 448 | ### @stacksjs/error-handling |
| 449 | 449 | |
| @@ -509,7 +509,9 @@ Guidance: [`stacks-git`](../stacks-git/SKILL.md). Source: [storage/framework/cor | ||
| 509 | 509 | |
| 510 | 510 | Read the entrypoint and package export map for re-exported or type-only surfaces. |
| 511 | 511 | |
| 512 | Evidence directory: [1 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/git/tests). These files are evidence pointers, not a claim that all tests were run for this catalog. | |
| 512 | Local source modules: [src/credentials.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/git/src/credentials.ts). | |
| 513 | ||
| 514 | Evidence directory: [2 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/git/tests). These files are evidence pointers, not a claim that all tests were run for this catalog. | |
| 513 | 515 | |
| 514 | 516 | ### @stacksjs/github |
| 515 | 517 | |
| @@ -887,7 +889,7 @@ Declared runtime exports: `CssEngine`, `buildPageSequence`, `paginatorVariant`, | ||
| 887 | 889 | |
| 888 | 890 | Local source modules: [src/fonts.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/ui/src/fonts.ts), [src/pagination.ts](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/ui/src/pagination.ts). |
| 889 | 891 | |
| 890 | Evidence directory: [5 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/ui/tests). These files are evidence pointers, not a claim that all tests were run for this catalog. | |
| 892 | Evidence directory: [6 retained tests](https://github.com/stacksjs/stacks/blob/main/storage/framework/core/ui/tests). These files are evidence pointers, not a claim that all tests were run for this catalog. | |
| 891 | 893 | |
| 892 | 894 | ### @stacksjs/utils |
| 893 | 895 | |
| @@ -2,7 +2,7 @@ | ||
| 2 | 2 | "publisher": "Stacks", |
| 3 | 3 | "name": "vscode-stacks", |
| 4 | 4 | "displayName": "Stacks", |
| 5 | "version": "0.75.219", | |
| 5 | "version": "0.75.221", | |
| 6 | 6 | "description": "Stacks, built into VS Code: stx templates, pickier linting and formatting, encrypted env files, the dev server preview and every buddy command. No other extensions required.", |
| 7 | 7 | "license": "MIT", |
| 8 | 8 | "funding": "https://github.com/sponsors/chrisbbreuer", |
| @@ -12,40 +12,93 @@ | ||
| 12 | 12 | const mcReplay = useReactiveProp<number>('replay', 0) |
| 13 | 13 | const mcReveals = derived(() => [mcReplay()]) |
| 14 | 14 | const mcCelebrate = useReactiveProp<boolean>('celebrate', true) |
| 15 | const mcSurface = useRef('membershipSurface') | |
| 16 | const mcCanHover = useMediaQuery('(hover: hover) and (pointer: fine)') | |
| 17 | const mcReduceMotion = usePreferredReducedMotion() | |
| 18 | let mcStopHover: (() => void) | undefined | |
| 19 | ||
| 20 | onMount(() => { | |
| 21 | const surface = mcSurface.current as HTMLElement | null | |
| 22 | if (!surface) return | |
| 23 | ||
| 24 | // Measure the stationary hit area, never the rotating card, to avoid feedback jitter. | |
| 25 | const reset = () => { | |
| 26 | surface.style.setProperty('--mc-rotate-x', '0deg') | |
| 27 | surface.style.setProperty('--mc-rotate-y', '0deg') | |
| 28 | surface.style.setProperty('--mc-light-x', '50%') | |
| 29 | surface.style.setProperty('--mc-light-y', '50%') | |
| 30 | surface.style.setProperty('--mc-hover', '0') | |
| 31 | } | |
| 32 | mcStopHover = effect(() => { | |
| 33 | reset() | |
| 34 | if (!mcCanHover() || mcReduceMotion()) return | |
| 35 | const mouse = useMouse({ target: surface, touch: false, resetOnLeave: true }) | |
| 36 | const unsubscribe = mouse.subscribe(({ clientX, clientY, isInside }) => { | |
| 37 | if (!isInside) return reset() | |
| 38 | const bounds = surface.getBoundingClientRect() | |
| 39 | if (!bounds.width || !bounds.height) return reset() | |
| 40 | const x = Math.max(0, Math.min(1, (clientX - bounds.left) / bounds.width)) | |
| 41 | const y = Math.max(0, Math.min(1, (clientY - bounds.top) / bounds.height)) | |
| 42 | surface.style.setProperty('--mc-rotate-x', `${(0.5 - y) * 12}deg`) | |
| 43 | surface.style.setProperty('--mc-rotate-y', `${(x - 0.5) * 16}deg`) | |
| 44 | surface.style.setProperty('--mc-light-x', `${x * 100}%`) | |
| 45 | surface.style.setProperty('--mc-light-y', `${y * 100}%`) | |
| 46 | surface.style.setProperty('--mc-hover', '1') | |
| 47 | }) | |
| 48 | return () => { | |
| 49 | unsubscribe() | |
| 50 | mouse.stop() | |
| 51 | reset() | |
| 52 | } | |
| 53 | }) | |
| 54 | }) | |
| 55 | onDestroy(() => mcStopHover?.()) | |
| 56 | ||
| 15 | 57 | const mcPieces = [...Array(32).keys()].map(id => ({ |
| 16 | 58 | id, |
| 17 | 59 | style: `--mc-x:${(id * 37 + 11) % 100}%;--mc-drift:${(id * 29 % 160) - 80}px;--mc-delay:${id % 8 * 65}ms;--mc-turn:${180 + id * 47}deg;--mc-color:var(--membership-card-confetti-${id % 4 + 1},${['#99f6e4', '#e2e8f0', '#5eead4', '#94a3b8'][id % 4]})`, |
| 18 | 60 | })) |
| 19 | 61 | </script> |
| 20 | 62 | |
| 21 | <div :for="reveal in mcReveals" :key="reveal" class="relative mx-auto w-full mc-stage" :class="mcCelebrate ? 'mc-celebrate' : ''" data-membership-reveal> | |
| 22 | <div :if="mcCelebrate" class="absolute inset-0 overflow-hidden pointer-events-none mc-confetti" aria-hidden="true"> | |
| 23 | <span :for="piece in mcPieces" :key="piece.id" class="mc-piece" :style="piece.style"></span> | |
| 24 | </div> | |
| 25 | <article class="overflow-hidden relative mx-auto w-full mc-metal" :aria-label="mcLabel" data-membership-card> | |
| 26 | <div class="absolute inset-0 pointer-events-none mc-grain" aria-hidden="true"></div> | |
| 27 | <div class="absolute inset-0 pointer-events-none mc-shine" aria-hidden="true"></div> | |
| 28 | <div class="flex relative gap-4 items-start justify-between"> | |
| 29 | <img :if="mcLogo" class="mc-logo" :src="mcLogo" :alt="mcBrand" width="96" height="38" /> | |
| 30 | <span :if="!mcLogo" class="font-bold mc-wordmark" :text="mcBrand"></span> | |
| 31 | </div> | |
| 32 | <p class="relative mt-3 text-xs mc-label" :text="mcLabel"></p> | |
| 33 | <div class="relative mc-holder"> | |
| 34 | <div class="mc-person"> | |
| 35 | <p class="mb-2 text-xs mc-label" :text="mcNameLabel"></p> | |
| 36 | <p class="mc-engraving" :style="{ fontFamily: mcNameFont }" :text="mcName" data-membership-name></p> | |
| 63 | <div class="relative mx-auto w-full mc-stage" data-membership-reveal> | |
| 64 | <div ref="membershipSurface" class="mc-surface"> | |
| 65 | <div :for="reveal in mcReveals" :key="reveal" :class="mcCelebrate ? 'mc-celebrate' : ''"> | |
| 66 | <div :if="mcCelebrate" class="absolute inset-0 overflow-hidden pointer-events-none mc-confetti" aria-hidden="true"> | |
| 67 | <span :for="piece in mcPieces" :key="piece.id" class="mc-piece" :style="piece.style"></span> | |
| 37 | 68 | </div> |
| 38 | <div :if="mcMemberId" class="mc-identity"> | |
| 39 | <p class="text-xs mc-label" :text="mcMemberIdLabel"></p> | |
| 40 | <p class="font-semibold mc-serial" :text="mcMemberId" data-membership-id></p> | |
| 69 | <div class="mc-tilt"> | |
| 70 | <article class="overflow-hidden relative mx-auto w-full mc-metal" :aria-label="mcLabel" data-membership-card> | |
| 71 | <div class="absolute inset-0 pointer-events-none mc-grain" aria-hidden="true"></div> | |
| 72 | <div class="absolute inset-0 pointer-events-none mc-reflection" aria-hidden="true"></div> | |
| 73 | <div class="absolute inset-0 pointer-events-none mc-shine" aria-hidden="true"></div> | |
| 74 | <div class="flex relative gap-4 items-start justify-between"> | |
| 75 | <img :if="mcLogo" class="mc-logo" :src="mcLogo" :alt="mcBrand" width="96" height="38" /> | |
| 76 | <span :if="!mcLogo" class="font-bold mc-wordmark" :text="mcBrand"></span> | |
| 77 | </div> | |
| 78 | <p class="relative mt-3 text-xs mc-label" :text="mcLabel"></p> | |
| 79 | <div class="relative mc-holder"> | |
| 80 | <div class="mc-person"> | |
| 81 | <p class="mb-2 text-xs mc-label" :text="mcNameLabel"></p> | |
| 82 | <p class="mc-engraving" :style="{ fontFamily: mcNameFont }" :text="mcName" data-membership-name></p> | |
| 83 | </div> | |
| 84 | <div :if="mcMemberId" class="mc-identity"> | |
| 85 | <p class="text-xs mc-label" :text="mcMemberIdLabel"></p> | |
| 86 | <p class="font-semibold mc-serial" :text="mcMemberId" data-membership-id></p> | |
| 87 | </div> | |
| 88 | </div> | |
| 89 | <p :if="mcDetail" class="relative mt-5 text-xs mc-detail" :text="mcDetail"></p> | |
| 90 | </article> | |
| 41 | 91 | </div> |
| 42 | 92 | </div> |
| 43 | <p :if="mcDetail" class="relative mt-5 text-xs mc-detail" :text="mcDetail"></p> | |
| 44 | </article> | |
| 93 | </div> | |
| 45 | 94 | </div> |
| 46 | 95 | |
| 47 | 96 | <style> |
| 48 | 97 | .mc-stage { max-width: 28rem; padding: 2rem 0 2.5rem; perspective: 1000px; } |
| 98 | .mc-surface { isolation: isolate; } | |
| 99 | .mc-tilt { transform: perspective(1000px) rotateX(var(--mc-rotate-x, 0deg)) rotateY(var(--mc-rotate-y, 0deg)) translateY(calc(var(--mc-hover, 0) * -3px)); transition: transform 450ms cubic-bezier(0.2, 0.75, 0.25, 1); } | |
| 100 | .mc-surface:hover .mc-tilt { transition-duration: 160ms; } | |
| 101 | .mc-reflection { opacity: calc(var(--mc-hover, 0) * 0.65); background: radial-gradient(ellipse at var(--mc-light-x, 50%) var(--mc-light-y, 50%), #ffffff38, #ffffff0a 42%, transparent 72%); transition: opacity 350ms ease; } | |
| 49 | 102 | .mc-metal { |
| 50 | 103 | box-sizing: border-box; min-height: 15.5rem; padding: clamp(1.25rem, 5vw, 2rem); border-radius: 1.25rem; |
| 51 | 104 | color: var(--membership-card-color, #e2e8f0); border: 1px solid var(--membership-card-border, #87939d); |
| @@ -79,10 +132,14 @@ | ||
| 79 | 132 | .mc-celebrate .mc-metal { animation: mc-arrive 1100ms cubic-bezier(0.16, 1, 0.3, 1) both; } |
| 80 | 133 | .mc-celebrate .mc-shine { animation: mc-polish 1700ms ease-in-out 650ms both; } |
| 81 | 134 | .mc-celebrate .mc-piece { animation: mc-fall 2400ms cubic-bezier(0.2, 0.65, 0.45, 1) var(--mc-delay) both; } |
| 82 | .mc-metal:hover .mc-shine { transform: translateX(150%) skewX(-20deg); transition: transform 1400ms ease; } | |
| 135 | } | |
| 136 | @media (hover: none), (pointer: coarse) { | |
| 137 | .mc-tilt { transform: none; transition: none; } | |
| 138 | .mc-reflection { display: none; } | |
| 83 | 139 | } |
| 84 | 140 | @media (prefers-reduced-motion: reduce) { |
| 85 | .mc-confetti, .mc-shine { display: none; } | |
| 141 | .mc-confetti, .mc-shine, .mc-reflection { display: none; } | |
| 142 | .mc-tilt { transform: none; transition: none; } | |
| 86 | 143 | } |
| 87 | 144 | @keyframes mc-arrive { from { opacity: 0; transform: translateY(32px) rotateX(16deg) rotateY(-12deg) scale(0.92); } to { opacity: 1; transform: none; } } |
| 88 | 145 | @keyframes mc-polish { from { transform: translateX(-150%) skewX(-20deg); } to { transform: translateX(150%) skewX(-20deg); } } |
| @@ -1,22 +1,5 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "models": [ |
| 3 | { | |
| 4 | "id": "achievement", | |
| 5 | "name": "Achievement", | |
| 6 | "icon": "tablecells.fill", | |
| 7 | "hasDedicatedPage": false, | |
| 8 | "category": "userland", | |
| 9 | "dashboard": { | |
| 10 | "highlight": true | |
| 11 | } | |
| 12 | }, | |
| 13 | { | |
| 14 | "id": "activity-comment", | |
| 15 | "name": "ActivityComment", | |
| 16 | "icon": "tablecells.fill", | |
| 17 | "hasDedicatedPage": false, | |
| 18 | "category": "userland" | |
| 19 | }, | |
| 20 | 3 | { |
| 21 | 4 | "id": "analytics-event", |
| 22 | 5 | "name": "AnalyticsEvent", |
| @@ -132,13 +115,6 @@ | ||
| 132 | 115 | "enabled": false |
| 133 | 116 | } |
| 134 | 117 | }, |
| 135 | { | |
| 136 | "id": "card-attachment", | |
| 137 | "name": "CardAttachment", | |
| 138 | "icon": "tablecells.fill", | |
| 139 | "hasDedicatedPage": false, | |
| 140 | "category": "data" | |
| 141 | }, | |
| 142 | 118 | { |
| 143 | 119 | "id": "card-comment", |
| 144 | 120 | "name": "CardComment", |
| @@ -186,40 +162,6 @@ | ||
| 186 | 162 | "highlight": true |
| 187 | 163 | } |
| 188 | 164 | }, |
| 189 | { | |
| 190 | "id": "challenge", | |
| 191 | "name": "Challenge", | |
| 192 | "icon": "tablecells.fill", | |
| 193 | "hasDedicatedPage": false, | |
| 194 | "category": "userland", | |
| 195 | "dashboard": { | |
| 196 | "highlight": true | |
| 197 | } | |
| 198 | }, | |
| 199 | { | |
| 200 | "id": "club", | |
| 201 | "name": "Club", | |
| 202 | "icon": "tablecells.fill", | |
| 203 | "hasDedicatedPage": false, | |
| 204 | "category": "userland", | |
| 205 | "dashboard": { | |
| 206 | "highlight": true | |
| 207 | } | |
| 208 | }, | |
| 209 | { | |
| 210 | "id": "club-invite", | |
| 211 | "name": "ClubInvite", | |
| 212 | "icon": "tablecells.fill", | |
| 213 | "hasDedicatedPage": false, | |
| 214 | "category": "userland" | |
| 215 | }, | |
| 216 | { | |
| 217 | "id": "club-member", | |
| 218 | "name": "ClubMember", | |
| 219 | "icon": "tablecells.fill", | |
| 220 | "hasDedicatedPage": false, | |
| 221 | "category": "userland" | |
| 222 | }, | |
| 223 | 165 | { |
| 224 | 166 | "id": "comment", |
| 225 | 167 | "name": "Comment", |
| @@ -241,13 +183,6 @@ | ||
| 241 | 183 | "hasDedicatedPage": false, |
| 242 | 184 | "category": "data" |
| 243 | 185 | }, |
| 244 | { | |
| 245 | "id": "content-report", | |
| 246 | "name": "ContentReport", | |
| 247 | "icon": "tablecells.fill", | |
| 248 | "hasDedicatedPage": false, | |
| 249 | "category": "userland" | |
| 250 | }, | |
| 251 | 186 | { |
| 252 | 187 | "id": "coupon", |
| 253 | 188 | "name": "Coupon", |
| @@ -285,13 +220,6 @@ | ||
| 285 | 220 | "highlight": true |
| 286 | 221 | } |
| 287 | 222 | }, |
| 288 | { | |
| 289 | "id": "custom-route", | |
| 290 | "name": "CustomRoute", | |
| 291 | "icon": "tablecells.fill", | |
| 292 | "hasDedicatedPage": false, | |
| 293 | "category": "userland" | |
| 294 | }, | |
| 295 | 223 | { |
| 296 | 224 | "id": "delivery-route", |
| 297 | 225 | "name": "DeliveryRoute", |
| @@ -322,13 +250,6 @@ | ||
| 322 | 250 | "highlight": true |
| 323 | 251 | } |
| 324 | 252 | }, |
| 325 | { | |
| 326 | "id": "device-push-token", | |
| 327 | "name": "DevicePushToken", | |
| 328 | "icon": "tablecells.fill", | |
| 329 | "hasDedicatedPage": false, | |
| 330 | "category": "userland" | |
| 331 | }, | |
| 332 | 253 | { |
| 333 | 254 | "id": "digital-delivery", |
| 334 | 255 | "name": "DigitalDelivery", |
| @@ -383,44 +304,6 @@ | ||
| 383 | 304 | "enabled": false |
| 384 | 305 | } |
| 385 | 306 | }, |
| 386 | { | |
| 387 | "id": "event", | |
| 388 | "name": "Event", | |
| 389 | "icon": "tablecells.fill", | |
| 390 | "hasDedicatedPage": false, | |
| 391 | "category": "userland", | |
| 392 | "dashboard": { | |
| 393 | "highlight": true | |
| 394 | } | |
| 395 | }, | |
| 396 | { | |
| 397 | "id": "event-entrant", | |
| 398 | "name": "EventEntrant", | |
| 399 | "icon": "tablecells.fill", | |
| 400 | "hasDedicatedPage": false, | |
| 401 | "category": "userland" | |
| 402 | }, | |
| 403 | { | |
| 404 | "id": "event-lap", | |
| 405 | "name": "EventLap", | |
| 406 | "icon": "tablecells.fill", | |
| 407 | "hasDedicatedPage": false, | |
| 408 | "category": "userland" | |
| 409 | }, | |
| 410 | { | |
| 411 | "id": "feedback-token", | |
| 412 | "name": "FeedbackToken", | |
| 413 | "icon": "tablecells.fill", | |
| 414 | "hasDedicatedPage": false, | |
| 415 | "category": "data" | |
| 416 | }, | |
| 417 | { | |
| 418 | "id": "follow", | |
| 419 | "name": "Follow", | |
| 420 | "icon": "tablecells.fill", | |
| 421 | "hasDedicatedPage": false, | |
| 422 | "category": "userland" | |
| 423 | }, | |
| 424 | 307 | { |
| 425 | 308 | "id": "form", |
| 426 | 309 | "name": "Form", |
| @@ -466,13 +349,6 @@ | ||
| 466 | 349 | "hasDedicatedPage": true, |
| 467 | 350 | "category": "system" |
| 468 | 351 | }, |
| 469 | { | |
| 470 | "id": "kudos", | |
| 471 | "name": "Kudos", | |
| 472 | "icon": "tablecells.fill", | |
| 473 | "hasDedicatedPage": false, | |
| 474 | "category": "userland" | |
| 475 | }, | |
| 476 | 352 | { |
| 477 | 353 | "id": "label", |
| 478 | 354 | "name": "Label", |
| @@ -772,28 +648,11 @@ | ||
| 772 | 648 | "name": "Review", |
| 773 | 649 | "icon": "star.fill", |
| 774 | 650 | "hasDedicatedPage": true, |
| 775 | "category": "userland", | |
| 776 | "dashboard": { | |
| 777 | "highlight": true | |
| 778 | } | |
| 779 | }, | |
| 780 | { | |
| 781 | "id": "route-effort", | |
| 782 | "name": "RouteEffort", | |
| 783 | "icon": "tablecells.fill", | |
| 784 | "hasDedicatedPage": false, | |
| 785 | "category": "userland", | |
| 651 | "category": "commerce", | |
| 786 | 652 | "dashboard": { |
| 787 | 653 | "highlight": true |
| 788 | 654 | } |
| 789 | 655 | }, |
| 790 | { | |
| 791 | "id": "saved-trail", | |
| 792 | "name": "SavedTrail", | |
| 793 | "icon": "tablecells.fill", | |
| 794 | "hasDedicatedPage": false, | |
| 795 | "category": "userland" | |
| 796 | }, | |
| 797 | 656 | { |
| 798 | 657 | "id": "sender-domain", |
| 799 | 658 | "name": "SenderDomain", |
| @@ -957,50 +816,6 @@ | ||
| 957 | 816 | "enabled": false |
| 958 | 817 | } |
| 959 | 818 | }, |
| 960 | { | |
| 961 | "id": "territory", | |
| 962 | "name": "Territory", | |
| 963 | "icon": "tablecells.fill", | |
| 964 | "hasDedicatedPage": false, | |
| 965 | "category": "userland", | |
| 966 | "dashboard": { | |
| 967 | "highlight": true | |
| 968 | } | |
| 969 | }, | |
| 970 | { | |
| 971 | "id": "territory-history", | |
| 972 | "name": "TerritoryHistory", | |
| 973 | "icon": "tablecells.fill", | |
| 974 | "hasDedicatedPage": false, | |
| 975 | "category": "userland" | |
| 976 | }, | |
| 977 | { | |
| 978 | "id": "territory-stats", | |
| 979 | "name": "TerritoryStats", | |
| 980 | "icon": "tablecells.fill", | |
| 981 | "hasDedicatedPage": false, | |
| 982 | "category": "userland" | |
| 983 | }, | |
| 984 | { | |
| 985 | "id": "trail", | |
| 986 | "name": "Trail", | |
| 987 | "icon": "tablecells.fill", | |
| 988 | "hasDedicatedPage": false, | |
| 989 | "category": "userland", | |
| 990 | "dashboard": { | |
| 991 | "highlight": true | |
| 992 | } | |
| 993 | }, | |
| 994 | { | |
| 995 | "id": "trail-ingest-shard", | |
| 996 | "name": "TrailIngestShard", | |
| 997 | "icon": "tablecells.fill", | |
| 998 | "hasDedicatedPage": false, | |
| 999 | "category": "userland", | |
| 1000 | "dashboard": { | |
| 1001 | "highlight": false | |
| 1002 | } | |
| 1003 | }, | |
| 1004 | 819 | { |
| 1005 | 820 | "id": "transaction", |
| 1006 | 821 | "name": "Transaction", |
| @@ -1011,13 +826,6 @@ | ||
| 1011 | 826 | "highlight": true |
| 1012 | 827 | } |
| 1013 | 828 | }, |
| 1014 | { | |
| 1015 | "id": "trip-plan", | |
| 1016 | "name": "TripPlan", | |
| 1017 | "icon": "tablecells.fill", | |
| 1018 | "hasDedicatedPage": false, | |
| 1019 | "category": "userland" | |
| 1020 | }, | |
| 1021 | 829 | { |
| 1022 | 830 | "id": "usage-event", |
| 1023 | 831 | "name": "UsageEvent", |
| @@ -1030,42 +838,10 @@ | ||
| 1030 | 838 | "name": "User", |
| 1031 | 839 | "icon": "person.fill", |
| 1032 | 840 | "hasDedicatedPage": true, |
| 1033 | "category": "userland" | |
| 1034 | }, | |
| 1035 | { | |
| 1036 | "id": "user-achievement", | |
| 1037 | "name": "UserAchievement", | |
| 1038 | "icon": "tablecells.fill", | |
| 1039 | "hasDedicatedPage": false, | |
| 1040 | "category": "userland" | |
| 1041 | }, | |
| 1042 | { | |
| 1043 | "id": "user-block", | |
| 1044 | "name": "UserBlock", | |
| 1045 | "icon": "tablecells.fill", | |
| 1046 | "hasDedicatedPage": false, | |
| 1047 | "category": "userland" | |
| 1048 | }, | |
| 1049 | { | |
| 1050 | "id": "user-notification", | |
| 1051 | "name": "UserNotification", | |
| 1052 | "icon": "tablecells.fill", | |
| 1053 | "hasDedicatedPage": false, | |
| 1054 | "category": "userland" | |
| 1055 | }, | |
| 1056 | { | |
| 1057 | "id": "user-privacy-setting", | |
| 1058 | "name": "UserPrivacySetting", | |
| 1059 | "icon": "tablecells.fill", | |
| 1060 | "hasDedicatedPage": false, | |
| 1061 | "category": "userland" | |
| 1062 | }, | |
| 1063 | { | |
| 1064 | "id": "user-stats", | |
| 1065 | "name": "UserStats", | |
| 1066 | "icon": "tablecells.fill", | |
| 1067 | "hasDedicatedPage": false, | |
| 1068 | "category": "userland" | |
| 841 | "category": "data", | |
| 842 | "dashboard": { | |
| 843 | "highlight": true | |
| 844 | } | |
| 1069 | 845 | }, |
| 1070 | 846 | { |
| 1071 | 847 | "id": "waitlist-product", |
| @@ -8,12 +8,12 @@ | ||
| 8 | 8 | "preview": "bunpress preview" |
| 9 | 9 | }, |
| 10 | 10 | "dependencies": { |
| 11 | "@stacksjs/alias": "^0.75.219", | |
| 12 | "@stacksjs/cli": "^0.75.219", | |
| 13 | "@stacksjs/config": "^0.75.219", | |
| 14 | "@stacksjs/env": "^0.75.219", | |
| 15 | "@stacksjs/path": "^0.75.219", | |
| 16 | "@stacksjs/server": "^0.75.219", | |
| 17 | "@stacksjs/storage": "^0.75.219" | |
| 11 | "@stacksjs/alias": "^0.75.221", | |
| 12 | "@stacksjs/cli": "^0.75.221", | |
| 13 | "@stacksjs/config": "^0.75.221", | |
| 14 | "@stacksjs/env": "^0.75.221", | |
| 15 | "@stacksjs/path": "^0.75.221", | |
| 16 | "@stacksjs/server": "^0.75.221", | |
| 17 | "@stacksjs/storage": "^0.75.221" | |
| 18 | 18 | } |
| 19 | 19 | } |
| @@ -45,9 +45,9 @@ | ||
| 45 | 45 | "prepublishOnly": "bun run build" |
| 46 | 46 | }, |
| 47 | 47 | "dependencies": { |
| 48 | "@stacksjs/cache": "^0.75.219", | |
| 49 | "@stacksjs/database": "^0.75.219", | |
| 50 | "@stacksjs/error-handling": "^0.75.219" | |
| 48 | "@stacksjs/cache": "^0.75.221", | |
| 49 | "@stacksjs/database": "^0.75.221", | |
| 50 | "@stacksjs/error-handling": "^0.75.221" | |
| 51 | 51 | }, |
| 52 | 52 | "devDependencies": { |
| 53 | 53 | "better-dx": "^0.2.5" |
| @@ -48,10 +48,10 @@ | ||
| 48 | 48 | }, |
| 49 | 49 | "devDependencies": { |
| 50 | 50 | "better-dx": "^0.2.5", |
| 51 | "@stacksjs/logging": "^0.75.219", | |
| 52 | "@stacksjs/path": "^0.75.219", | |
| 53 | "@stacksjs/router": "^0.75.219", | |
| 54 | "@stacksjs/storage": "^0.75.219", | |
| 55 | "@stacksjs/utils": "^0.75.219" | |
| 51 | "@stacksjs/logging": "^0.75.221", | |
| 52 | "@stacksjs/path": "^0.75.221", | |
| 53 | "@stacksjs/router": "^0.75.221", | |
| 54 | "@stacksjs/storage": "^0.75.221", | |
| 55 | "@stacksjs/utils": "^0.75.221" | |
| 56 | 56 | } |
| 57 | 57 | } |