@mohasinac/appkit 3.6.1 → 3.6.3
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/dist/_internal/server/features/auth/permissions.js +0 -1
- package/dist/configs/tailwind.d.ts +11 -3
- package/dist/configs/tailwind.js +12 -3
- package/dist/features/auth/repository/user.repository.js +9 -1
- package/dist/features/homepage/components/HeroCarousel.js +1 -1
- package/dist/features/homepage/components/WelcomeSection.js +1 -1
- package/dist/styles.css +2 -1
- package/dist/tailwind-input.css +74 -2
- package/dist/tailwind-utilities.css +2 -1
- package/package.json +3 -2
- package/scripts/verify-css-build.mjs +7 -3
|
@@ -21,7 +21,6 @@ export async function getServerPermissions(uid) {
|
|
|
21
21
|
return { isAdmin: false, permissions: [] };
|
|
22
22
|
// userRepository.findById is cached per request in BaseRepository
|
|
23
23
|
const user = await userRepository.findById(uid);
|
|
24
|
-
console.error("[PERM-DIAG] uid:", uid, "user:", user ? JSON.stringify(user) : "null");
|
|
25
24
|
if (!user)
|
|
26
25
|
return { isAdmin: false, permissions: [] };
|
|
27
26
|
if (user.role === "admin")
|
|
@@ -3,10 +3,20 @@
|
|
|
3
3
|
*
|
|
4
4
|
* appkit ships pre-compiled dist/tailwind-utilities.css covering every class
|
|
5
5
|
* appkit components emit. The consumer's Tailwind config does NOT need to scan
|
|
6
|
-
* node_modules/@mohasinac/appkit
|
|
6
|
+
* node_modules/@mohasinac/appkit/**.
|
|
7
7
|
*
|
|
8
8
|
* Consumer must provide `content` globs for their own source files.
|
|
9
9
|
*
|
|
10
|
+
* Loaded via a Tailwind v4 `@config "./tailwind.config.js";` directive in the
|
|
11
|
+
* consumer's CSS entry point — v4's config-compat bridge still honors
|
|
12
|
+
* `content`/`theme`/`plugins`/`darkMode` from a JS config exactly like v3 did.
|
|
13
|
+
*
|
|
14
|
+
* NOTE (Tailwind v4 migration, 2026-08-16): `safelist` is NOT supported by the
|
|
15
|
+
* v4 `@config` compat bridge. Any safelist classes must live in the consumer's
|
|
16
|
+
* CSS entry point via `@source inline("...")` instead — see src/app/globals.css.
|
|
17
|
+
* Do not add a `safelist` key back to this factory's override shape; it would
|
|
18
|
+
* silently no-op under v4.
|
|
19
|
+
*
|
|
10
20
|
* @example
|
|
11
21
|
* ```js
|
|
12
22
|
* // tailwind.config.js
|
|
@@ -20,13 +30,11 @@ export interface TailwindConfigOverride {
|
|
|
20
30
|
content?: string[];
|
|
21
31
|
theme?: Record<string, unknown>;
|
|
22
32
|
plugins?: unknown[];
|
|
23
|
-
safelist?: unknown[];
|
|
24
33
|
[key: string]: unknown;
|
|
25
34
|
}
|
|
26
35
|
export declare function defineTailwindConfig(override?: TailwindConfigOverride): {
|
|
27
36
|
darkMode: "class";
|
|
28
37
|
content: string[];
|
|
29
|
-
safelist: unknown[];
|
|
30
38
|
theme: {
|
|
31
39
|
extend: {
|
|
32
40
|
colors: {
|
package/dist/configs/tailwind.js
CHANGED
|
@@ -3,10 +3,20 @@
|
|
|
3
3
|
*
|
|
4
4
|
* appkit ships pre-compiled dist/tailwind-utilities.css covering every class
|
|
5
5
|
* appkit components emit. The consumer's Tailwind config does NOT need to scan
|
|
6
|
-
* node_modules/@mohasinac/appkit
|
|
6
|
+
* node_modules/@mohasinac/appkit/**.
|
|
7
7
|
*
|
|
8
8
|
* Consumer must provide `content` globs for their own source files.
|
|
9
9
|
*
|
|
10
|
+
* Loaded via a Tailwind v4 `@config "./tailwind.config.js";` directive in the
|
|
11
|
+
* consumer's CSS entry point — v4's config-compat bridge still honors
|
|
12
|
+
* `content`/`theme`/`plugins`/`darkMode` from a JS config exactly like v3 did.
|
|
13
|
+
*
|
|
14
|
+
* NOTE (Tailwind v4 migration, 2026-08-16): `safelist` is NOT supported by the
|
|
15
|
+
* v4 `@config` compat bridge. Any safelist classes must live in the consumer's
|
|
16
|
+
* CSS entry point via `@source inline("...")` instead — see src/app/globals.css.
|
|
17
|
+
* Do not add a `safelist` key back to this factory's override shape; it would
|
|
18
|
+
* silently no-op under v4.
|
|
19
|
+
*
|
|
10
20
|
* @example
|
|
11
21
|
* ```js
|
|
12
22
|
* // tailwind.config.js
|
|
@@ -17,11 +27,10 @@
|
|
|
17
27
|
* ```
|
|
18
28
|
*/
|
|
19
29
|
export function defineTailwindConfig(override = {}) {
|
|
20
|
-
const { content: consumerContent = [], theme: consumerTheme = {}, plugins: consumerPlugins = [],
|
|
30
|
+
const { content: consumerContent = [], theme: consumerTheme = {}, plugins: consumerPlugins = [], ...rest } = override;
|
|
21
31
|
return {
|
|
22
32
|
darkMode: "class",
|
|
23
33
|
content: [...consumerContent],
|
|
24
|
-
safelist: [...consumerSafelist],
|
|
25
34
|
theme: {
|
|
26
35
|
extend: {
|
|
27
36
|
colors: {
|
|
@@ -38,7 +38,15 @@ export class UserRepository extends BaseRepository {
|
|
|
38
38
|
return this.decryptUser(raw);
|
|
39
39
|
}
|
|
40
40
|
async createWithId(id, data) {
|
|
41
|
-
|
|
41
|
+
// `uid` is a required UserDocument field read directly off the mapped
|
|
42
|
+
// object (SessionUser.uid, getServerPermissions(uid), etc.) — mapDoc()
|
|
43
|
+
// only auto-populates `.id` from the Firestore doc ID, not `.uid`. A
|
|
44
|
+
// caller passing `data` without `uid` silently produces a user record
|
|
45
|
+
// that authenticates fine but fails every downstream role/permission
|
|
46
|
+
// check with no error anywhere (found via a live RBAC bug where an
|
|
47
|
+
// admin account created this way couldn't access /admin).
|
|
48
|
+
const withUid = { uid: id, ...data };
|
|
49
|
+
const encrypted = this.encryptUserData(withUid);
|
|
42
50
|
return super.createWithId(id, encrypted);
|
|
43
51
|
}
|
|
44
52
|
async create(input) {
|
|
@@ -185,7 +185,7 @@ export function HeroCarousel({ initialSlides, push } = {}) {
|
|
|
185
185
|
return (_jsxs(Section, { ref: sectionRef, className: `relative w-full ${heightClass} overflow-hidden`, "aria-roledescription": "carousel", "aria-label": "Hero carousel", onKeyDown: handleKeyDown, onMouseEnter: () => setIsPaused(true), onMouseLeave: () => setIsPaused(false), onFocus: () => setIsPaused(true), onBlur: () => setIsPaused(false), tabIndex: 0, children: [_jsx(Div, { "aria-live": "polite", "aria-atomic": "true", className: "sr-only", children: `Slide ${currentSlide + 1} of ${slides.length}` }), _jsx(Div, { className: POSITION_FILL, children: _jsx(Div, { layout: "flex", ref: slidesRef, onScroll: handleSlidesScroll, className: "h-full overflow-x-auto scroll-smooth snap-x snap-mandatory [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", style: { gap: 0 }, children: slides.map((slide, slideIndex) => {
|
|
186
186
|
const slideHeightClass = getSlideHeightClass(slide.settings?.height);
|
|
187
187
|
const hasCards = slide.cards && slide.cards.length > 0;
|
|
188
|
-
return (_jsxs(Div, { className: `snap-start flex-none w-full relative self-stretch bg-neutral-900 ${slideHeightClass}`, children: [_jsx(Div, { className: POSITION_FILL, children: _jsx(SlideBackground, { bg: slide.background, legacy: slide.media, mobileLegacy: slide.mobileMedia, isMobile: isMobile, priority: slideIndex === 0 }) }), slide.overlay && (_jsxs(Div, { layout: "flex-col", align: "start", justify: "center", className: `${POSITION_FILL} text-left md:px-[4rem] lg:px-[8rem]`, padding: "x-lg", children: [slide.overlay.subtitle && (_jsx(Text, { color: "inverse", shadow: "sm", className: "stagger-1 !/80 mb-1 md:mb-2 tracking-widest", mdSize: "sm", size: "xs", transform: "uppercase", children: slide.overlay.subtitle })), slide.overlay.title && (_jsx(Heading, { color: "inverse", level: 1, className: "stagger-2 font-display ! mb-2 md:mb-4", size: "4xl", shadow: "2xl", mdSize: "6xl", lgSize: "8xl", children: slide.overlay.title })), slide.overlay.description && (_jsx(Text, { color: "inverse", shadow: "sm", className: "stagger-3 !/90 mb-4 md:mb-8 max-w-2xl", mdSize: "lg", lgSize: "xl", size: "sm", children: slide.overlay.description })), slide.overlay.button && (_jsx(Div, { className: "stagger-4", children: _jsx(Button, { variant: slide.overlay.button.variant, size: "sm", onClick: makeButtonClickHandler(slide.overlay.button.link, slide.overlay.button.openInNewTab, push), children: slide.overlay.button.text }) }))] })), hasCards && (_jsx(Div, { className: `${POSITION_FILL} grid gap-[var(--appkit-space-2)] md:gap-[var(--appkit-space-4)] md:p-[var(--appkit-space-8)]`, padding: "md", style: {
|
|
188
|
+
return (_jsxs(Div, { className: `snap-start flex-none w-full relative self-stretch bg-neutral-900 ${slideHeightClass}`, children: [_jsx(Div, { className: POSITION_FILL, children: _jsx(SlideBackground, { bg: slide.background, legacy: slide.media, mobileLegacy: slide.mobileMedia, isMobile: isMobile, priority: slideIndex === 0 }) }), slide.overlay && (_jsxs(Div, { layout: "flex-col", align: "start", justify: "center", className: `${POSITION_FILL} text-left md:px-[4rem] lg:px-[8rem]`, padding: "x-lg", children: [slide.overlay.subtitle && (_jsx(Text, { color: "inverse", shadow: "sm", className: "stagger-1 !/80 mb-1 md:mb-2 tracking-widest", mdSize: "sm", size: "xs", transform: "uppercase", children: slide.overlay.subtitle })), slide.overlay.title && (_jsx(Heading, { color: "inverse", level: 1, className: "stagger-2 font-display ! mb-2 md:mb-4 break-words", size: "4xl", shadow: "2xl", mdSize: "6xl", lgSize: "8xl", children: slide.overlay.title })), slide.overlay.description && (_jsx(Text, { color: "inverse", shadow: "sm", className: "stagger-3 !/90 mb-4 md:mb-8 max-w-2xl", mdSize: "lg", lgSize: "xl", size: "sm", children: slide.overlay.description })), slide.overlay.button && (_jsx(Div, { className: "stagger-4", children: _jsx(Button, { variant: slide.overlay.button.variant, size: "sm", onClick: makeButtonClickHandler(slide.overlay.button.link, slide.overlay.button.openInNewTab, push), children: slide.overlay.button.text }) }))] })), hasCards && (_jsx(Div, { className: `${POSITION_FILL} grid gap-[var(--appkit-space-2)] md:gap-[var(--appkit-space-4)] md:p-[var(--appkit-space-8)]`, padding: "md", style: {
|
|
189
189
|
gridTemplateRows: "repeat(2, 1fr)",
|
|
190
190
|
gridTemplateColumns: isMobile ? "1fr" : "repeat(3, 1fr)",
|
|
191
191
|
alignContent: "center",
|
|
@@ -9,5 +9,5 @@ export function WelcomeSection({ title, subtitle, pillLabel, showCTA = true, cta
|
|
|
9
9
|
if (isLoading) {
|
|
10
10
|
return (_jsx(Section, { className: `relative overflow-hidden md:py-[6rem] ${className}`, paddingY: "y-4xl", paddingX: "x-md", children: _jsxs(Div, { className: "animate-pulse max-w-4xl mx-auto text-left", children: [_jsx(Div, { className: "h-6 w-52 mb-6", surface: "subtle", rounded: "full" }), _jsx(Div, { className: "h-20 mb-4 max-w-2xl", surface: "subtle", rounded: "lg" }), _jsx(Div, { className: "h-6 mb-8 max-w-lg", surface: "subtle", rounded: "lg" }), _jsxs(Row, { justify: "start", gap: "md", children: [_jsx(Div, { className: "h-12 w-36", surface: "subtle", rounded: "xl" }), _jsx(Div, { className: "h-12 w-36", surface: "subtle", rounded: "xl" })] })] }) }));
|
|
11
11
|
}
|
|
12
|
-
return (_jsxs(Section, { className: `relative overflow-hidden md:py-[7rem] ${className}`, paddingY: "y-5xl", paddingX: "x-md", children: [_jsx(Div, { className: "pointer-events-none absolute -top-32 -left-32 w-[28rem] h-[28rem] bg-primary/10 blur-3xl", rounded: "full", "aria-hidden": "true" }), _jsx(Div, { className: "pointer-events-none absolute -bottom-40 -right-40 w-[36rem] h-[36rem] bg-secondary/10 dark:bg-secondary/15 blur-3xl", rounded: "full", "aria-hidden": "true" }), _jsx(Div, { className: "relative z-10 max-w-7xl mx-auto", children: _jsxs(Grid, { align: "center", gap: "2xl", className: "grid-cols-1 lg:grid-cols-2 xl:grid-cols-2 2xl:grid-cols-2", children: [_jsxs(Div, { className: "text-left", children: [pillLabel && (_jsx(Div, { children: _jsxs(Span, { layout: "inline-flex", gap: "md", size: "xs", weight: "medium", className: "border border-primary-[500]/30 bg-primary-500/10 px-[1.25rem] py-[0.375rem] tracking-[0.2em] text-primary-700 dark:text-primary-400 backdrop-blur-sm", rounded: "full", transform: "uppercase", children: [_jsx(Span, { className: "w-1.5 h-1.5 bg-primary-500 inline-block", rounded: "full", "aria-hidden": "true" }), pillLabel, _jsx(Span, { className: "w-1.5 h-1.5 bg-primary-500 inline-block", rounded: "full", "aria-hidden": "true" })] }) })), _jsx(Heading, { level: 1, variant: "none", gradient: "brand-tri", className: "mt-4 font-display leading-[1.1] tracking-tight", size: "5xl", mdSize: "6xl", lgSize: "7xl", xlSize: "
|
|
12
|
+
return (_jsxs(Section, { className: `relative overflow-hidden md:py-[7rem] ${className}`, paddingY: "y-5xl", paddingX: "x-md", children: [_jsx(Div, { className: "pointer-events-none absolute -top-32 -left-32 w-[28rem] h-[28rem] bg-primary/10 blur-3xl", rounded: "full", "aria-hidden": "true" }), _jsx(Div, { className: "pointer-events-none absolute -bottom-40 -right-40 w-[36rem] h-[36rem] bg-secondary/10 dark:bg-secondary/15 blur-3xl", rounded: "full", "aria-hidden": "true" }), _jsx(Div, { className: "relative z-10 max-w-7xl mx-auto", children: _jsxs(Grid, { align: "center", gap: "2xl", className: "grid-cols-1 lg:grid-cols-2 xl:grid-cols-2 2xl:grid-cols-2", children: [_jsxs(Div, { className: "text-left", children: [pillLabel && (_jsx(Div, { children: _jsxs(Span, { layout: "inline-flex", gap: "md", size: "xs", weight: "medium", className: "border border-primary-[500]/30 bg-primary-500/10 px-[1.25rem] py-[0.375rem] tracking-[0.2em] text-primary-700 dark:text-primary-400 backdrop-blur-sm", rounded: "full", transform: "uppercase", children: [_jsx(Span, { className: "w-1.5 h-1.5 bg-primary-500 inline-block", rounded: "full", "aria-hidden": "true" }), pillLabel, _jsx(Span, { className: "w-1.5 h-1.5 bg-primary-500 inline-block", rounded: "full", "aria-hidden": "true" })] }) })), _jsx(Heading, { level: 1, variant: "none", gradient: "brand-tri", className: "mt-4 font-display leading-[1.1] tracking-tight break-words", size: "5xl", mdSize: "6xl", lgSize: "7xl", xlSize: "7xl", children: title }), subtitle && (_jsx(Text, { className: `mt-4 ${themed.textSecondary} max-w-xl leading-relaxed`, size: "xl", children: subtitle })), showCTA && (_jsxs(Row, { wrap: true, gap: "md", className: "mt-8", justify: "start", children: [ctaHref ? (_jsx(TextLink, { rounded: "xl", href: ctaHref, className: "inline-flex items-[center] justify-[center] px-[2rem] py-[0.875rem] !bg-primary hover:!bg-primary-600 text-white dark:!bg-primary dark:hover:!bg-primary-600 dark:text-white btn-glow transition-all hover:scale-[1.02]", size: "base", weight: "bold", children: ctaLabel })) : (_jsx(Button, { variant: "primary", size: "lg", onClick: onCtaClick, children: ctaLabel })), secondaryCtaHref ? (_jsx(TextLink, { rounded: "xl", href: secondaryCtaHref, className: "inline-flex items-[center] justify-[center] border-2 border-cobalt/40 dark:border-cobalt-400/40 px-[2rem] py-[0.875rem] text-cobalt-700 dark:text-cobalt-300 hover:bg-cobalt-50 dark:hover:bg-cobalt-900/20 transition-all hover:scale-[1.02]", size: "base", weight: "semibold", children: secondaryCtaLabel })) : (_jsx(Button, { variant: "outline", size: "lg", onClick: onSecondaryCtaClick, children: secondaryCtaLabel }))] })), trustChips.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", className: "mt-6", justify: "start", children: trustChips.map((chip) => (_jsxs(Span, { layout: "inline-flex", gap: "xs", size: "xs", weight: "medium", className: "border border-zinc-[200] dark:border-slate-[700] px-[0.875rem] py-[0.375rem]", rounded: "full", surface: "subtle", color: "muted", children: [chip.emoji, " ", chip.label] }, chip.key))) }))] }), _jsx(Div, { className: "hidden lg:block", children: _jsxs(Div, { className: `relative overflow-hidden aspect-[4/3] bg-[image:var(--appkit-gradient-section-mesh)]`, shadow: "2xl", border: "default", rounded: "3xl", children: [_jsx(Div, { className: "absolute inset-0 bg-[image:var(--appkit-gradient-glass)]" }), _jsx(Row, { centered: true, className: `absolute inset-0 ${flex.center} px-[2.5rem]`, children: _jsx(SiteLogo, { title: brandLogoText || "LetItRip.in", size: "hero" }) })] }) })] }) })] }));
|
|
13
13
|
}
|