rahman-resources 0.9.2 → 0.11.1

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/bin/cli.js CHANGED
@@ -58,6 +58,9 @@ async function main() {
58
58
  return runInit(rest);
59
59
  case "add":
60
60
  return runAdd(rest);
61
+ case "update":
62
+ case "update-slice":
63
+ return runUpdate(rest);
61
64
  case "add-skill":
62
65
  return runAddSkill(rest);
63
66
  case "scaffold-slice":
@@ -435,6 +438,35 @@ async function runAdd(rest) {
435
438
  if (kind === "recipe") return addRecipe(entry);
436
439
  }
437
440
 
441
+ /**
442
+ * `update <slug> [target] [--dry-run]` — re-pull a slice from the kitab into
443
+ * an existing consumer directory. Use after `@rahman/shared` bumps or when
444
+ * the source slice has shipped patches. `--dry-run` previews changes via
445
+ * `runLift`'s built-in diff.
446
+ *
447
+ * For slices only (not layouts/features/recipes — those use `add`).
448
+ */
449
+ async function runUpdate(rest) {
450
+ const { positional, flags } = parseFlags(rest);
451
+ const [slug, targetArg = "."] = positional;
452
+ if (!slug) {
453
+ console.error(kleur.red("Missing slug. Usage: rahman-resources update <slug> [target] [--dry-run]"));
454
+ process.exit(1);
455
+ }
456
+ const found = findEntry(slug);
457
+ if (!found) throw new Error(`"${slug}" not found. Run ${kleur.cyan("npx rahman-resources list slices")}.`);
458
+ const { kind, entry } = found;
459
+ if (kind !== "slice") {
460
+ console.error(kleur.red(`update only supported for slices. "${slug}" is a ${kind} — use \`add\` instead.`));
461
+ process.exit(1);
462
+ }
463
+ console.log(kleur.bold(`\n→ Re-pulling ${kleur.cyan(entry.slug)} into ${kleur.dim(targetArg)}\n`));
464
+ const liftArgs = [`rahman:${entry.slug}`];
465
+ if (targetArg !== ".") liftArgs.push("--target", targetArg);
466
+ if (flags["dry-run"]) liftArgs.push("--dry-run");
467
+ return runLift(liftArgs);
468
+ }
469
+
438
470
  async function addLayout(t, target, targetArg, flags = {}) {
439
471
  console.log(kleur.bold(`\n→ Installing ${kleur.cyan(t.title)} into ${kleur.dim(target)}\n`));
440
472
  if (!t.pullPaths || t.pullPaths.length === 0) {
package/lib/manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 2,
3
- "generatedAt": "2026-05-11T06:12:19.160Z",
3
+ "generatedAt": "2026-05-13T13:37:06.031Z",
4
4
  "repo": "rahmanef63/resource-site",
5
5
  "branch": "main",
6
6
  "layouts": [
@@ -804,177 +804,7 @@
804
804
  "primaryFile": "app/preview/riset-kit/public/page.tsx"
805
805
  }
806
806
  ],
807
- "recipes": [
808
- {
809
- "slug": "block-editor",
810
- "title": "Notion-Style Block Editor",
811
- "description": "21-block contenteditable editor with slash command menu, markdown shortcuts, drag handles. Real-time via Convex.",
812
- "source": "notion-page-clone",
813
- "files": [
814
- "frontend/slices/notion/slices/editor/BlockEditor.tsx",
815
- "frontend/slices/notion/slices/editor/SlashMenu.tsx",
816
- "frontend/slices/notion/slices/editor/blockSpecs.ts"
817
- ],
818
- "exampleCode": "import { BlockEditor } from \"@/frontend/slices/notion/slices/editor/BlockEditor\";\n\n<BlockEditor pageId={pageId} />",
819
- "agentRecipe": "Already copied at frontend/slices/notion/slices/editor/. See PORT-NOTION.md for Vite→Next port checklist (routing rewrite, use-client markers, Convex API surface rename).",
820
- "tags": [
821
- "editor",
822
- "notion",
823
- "blocks",
824
- "real-time"
825
- ]
826
- },
827
- {
828
- "slug": "page-tree-sidebar",
829
- "title": "Page Tree Sidebar",
830
- "description": "Hierarchical workspace sidebar with @dnd-kit drag-drop reordering, favorites, recents.",
831
- "source": "notion-page-clone",
832
- "files": [
833
- "frontend/slices/notion/slices/workspace-sidebar/components/WorkspaceSidebar.tsx",
834
- "frontend/slices/notion/slices/workspace-sidebar/components/SortablePageRow.tsx"
835
- ],
836
- "exampleCode": "import { WorkspaceSidebar } from \"@/frontend/slices/notion/slices/workspace-sidebar/components/WorkspaceSidebar\";\n\n<WorkspaceSidebar />",
837
- "agentRecipe": "Mount WorkspaceSidebar inside the left slot of <ThreeColumnLayout>. State backed by Zustand store at frontend/slices/notion/shared/lib/store.tsx.",
838
- "tags": [
839
- "sidebar",
840
- "tree",
841
- "dnd-kit",
842
- "navigation"
843
- ]
844
- },
845
- {
846
- "slug": "multi-block-selection",
847
- "title": "Multi-Block Selection",
848
- "description": "Marquee + click+shift selection of editor blocks with floating toolbar. Bulk actions: delete, duplicate, convert.",
849
- "source": "notion-page-clone",
850
- "files": [
851
- "frontend/slices/notion/slices/block-selection/components/BlockSelectionProvider.tsx",
852
- "frontend/slices/notion/slices/block-selection/components/MarqueeOverlay.tsx"
853
- ],
854
- "exampleCode": "import { BlockSelectionProvider } from \"@/frontend/slices/notion/slices/block-selection/components/BlockSelectionProvider\";\n\n<BlockSelectionProvider>\n <BlockEditor />\n</BlockSelectionProvider>",
855
- "agentRecipe": "Wrap BlockEditor with BlockSelectionProvider. The marquee overlay attaches to document; toolbar floats above the selection bounding box.",
856
- "tags": [
857
- "selection",
858
- "editor",
859
- "bulk",
860
- "marquee"
861
- ]
862
- },
863
- {
864
- "slug": "database-views",
865
- "title": "Database Views (11 types)",
866
- "description": "Properties+rows database with 11 view types: table, board, calendar, timeline, chart, gallery, map. Per-view filter/sort/group.",
867
- "source": "notion-page-clone",
868
- "files": [
869
- "frontend/slices/notion/slices/databases/DatabaseBlock.tsx",
870
- "frontend/slices/notion/slices/databases/views/TableView.tsx",
871
- "frontend/slices/notion/slices/databases/views/BoardView.tsx"
872
- ],
873
- "exampleCode": "import { DatabaseBlock } from \"@/frontend/slices/notion/slices/databases/DatabaseBlock\";\n\n<DatabaseBlock databaseId={dbId} />",
874
- "agentRecipe": "DatabaseBlock auto-routes to the active view component. Add custom property types by extending PropertyCell.tsx.",
875
- "tags": [
876
- "database",
877
- "views",
878
- "table",
879
- "kanban",
880
- "calendar"
881
- ]
882
- },
883
- {
884
- "slug": "command-palette",
885
- "title": "Command Palette (⌘K)",
886
- "description": "Cmd+K modal: feature navigation, workspace switching, theme, sign-out, custom commands. Auto-builds from feature registry.",
887
- "source": "kitab-core + notion-page-clone",
888
- "files": [
889
- "frontend/shared/foundation/utils/system/command-menu/components.tsx"
890
- ],
891
- "exampleCode": "import { CommandMenu } from \"@/frontend/shared/foundation/utils/system/command-menu/components\";\n\n<CommandMenu actions={customActions} />",
892
- "agentRecipe": "Mount CommandMenu once at the app shell level. It listens for Cmd+K globally. Pass extra commands via the actions prop or register via the command-registry.",
893
- "tags": [
894
- "palette",
895
- "cmd-k",
896
- "navigation"
897
- ]
898
- },
899
- {
900
- "slug": "comments-threaded",
901
- "title": "Threaded Comments",
902
- "description": "Page + block-level threaded comments with resolved state. Real-time via Convex.",
903
- "source": "notion-page-clone",
904
- "files": [
905
- "frontend/slices/notion/slices/comments/components/BlockCommentsPopover.tsx",
906
- "frontend/slices/notion/slices/comments/hooks/useComments.ts"
907
- ],
908
- "exampleCode": "import { BlockCommentsPopover } from \"@/frontend/slices/notion/slices/comments/components/BlockCommentsPopover\";\n\n<BlockCommentsPopover blockId={blockId} pageId={pageId} />",
909
- "agentRecipe": "Anchor comments by passing pageId (always) and optional blockId. Use useComments(blockId) hook for the reactive list.",
910
- "tags": [
911
- "comments",
912
- "real-time",
913
- "threading"
914
- ]
915
- },
916
- {
917
- "slug": "theme-preset-switcher",
918
- "title": "Theme Preset Switcher",
919
- "description": "Runtime theme swap (colors + fonts + shadows + tracking). OKLch CSS vars per preset. Persists to localStorage + Convex.",
920
- "source": "rahmanef.com",
921
- "files": [
922
- "frontend/shared/theme/theme-presets.ts",
923
- "frontend/shared/ui/components/theme-preset-switcher.tsx"
924
- ],
925
- "exampleCode": "import { ThemePresetSwitcher } from \"@/frontend/shared/ui/components/theme-preset-switcher\";\n\n<ThemePresetSwitcher />",
926
- "agentRecipe": "Add a new preset by appending a CSS block in app/globals.css with [data-theme=\"<name>\"], then register in preset-groups.ts.",
927
- "tags": [
928
- "theme",
929
- "presets",
930
- "oklch",
931
- "design-system"
932
- ]
933
- },
934
- {
935
- "slug": "icon-picker",
936
- "title": "Notion-Style Icon Picker",
937
- "description": "Emoji + lucide icon picker with search, 10-color Notion palette, Twemoji/native toggle. One string stores emoji OR lucide:Name OR with ?c=hex tint — backwards-compat with raw-emoji fields.",
938
- "source": "notion-page-clone",
939
- "files": [
940
- "frontend/slices/notion/slices/icon-picker/components/IconPicker.tsx",
941
- "frontend/slices/notion/slices/icon-picker/components/DynamicIcon.tsx",
942
- "frontend/slices/notion/slices/icon-picker/lib/parse.ts",
943
- "frontend/slices/notion/slices/icon-picker/lib/colors.ts",
944
- "frontend/slices/notion/slices/icon-picker/lib/emoji-catalog.ts",
945
- "frontend/slices/notion/slices/icon-picker/lib/lucide-catalog.ts",
946
- "frontend/slices/notion/slices/icon-picker/lib/twemoji.ts",
947
- "frontend/slices/notion/slices/icon-picker/lib/style-pref.ts"
948
- ],
949
- "exampleCode": "import { IconPickerPopover, DynamicIcon } from \"@/frontend/slices/notion/slices/icon-picker\";\n\n<IconPickerPopover value={page.icon} onChange={(v) => updateIcon(v)} onClear={() => updateIcon(\"\")} />\n<DynamicIcon value={page.icon} className=\"text-2xl\" />",
950
- "agentRecipe": "Single icon field stores emoji or 'lucide:Name' plus optional '?c=hex'. parseIconValue() decodes; lucideValue()/withColor() build. Add 'icon: v.string()' to Convex table — no migration needed for existing emoji fields. Popover variant for inline UI, Inline for sheets/dialogs.",
951
- "tags": [
952
- "icon",
953
- "emoji",
954
- "lucide",
955
- "picker",
956
- "twemoji",
957
- "notion"
958
- ]
959
- },
960
- {
961
- "slug": "contact-form-resend",
962
- "title": "Contact Form + Resend",
963
- "description": "Contact form posting to Resend email API. Server Action + Zod input validation.",
964
- "source": "cescadesigns",
965
- "files": [
966
- "recipes/contact-form-resend/src/page.tsx"
967
- ],
968
- "exampleCode": "// app/api/contact/route.ts\nimport { Resend } from \"resend\";\nconst resend = new Resend(process.env.RESEND_API_KEY!);\n\nexport async function POST(req: Request) {\n const data = await req.formData();\n await resend.emails.send({\n from: \"form@yourdomain.com\",\n to: \"you@yourdomain.com\",\n subject: `From ${data.get(\"name\")}`,\n html: `<p>${data.get(\"message\")}</p>`,\n });\n return Response.json({ ok: true });\n}",
969
- "agentRecipe": "Wire ContactForm.tsx (form action /api/contact) to the route handler. Always validate inputs with Zod server-side.",
970
- "tags": [
971
- "form",
972
- "email",
973
- "resend",
974
- "server-action"
975
- ]
976
- }
977
- ],
807
+ "recipes": [],
978
808
  "features": [
979
809
  {
980
810
  "slug": "convex-auth",
@@ -998,6 +828,27 @@
998
828
  "no-clerk"
999
829
  ]
1000
830
  },
831
+ {
832
+ "slug": "doku-payment",
833
+ "title": "DOKU — Indonesia Payment",
834
+ "category": "payment",
835
+ "description": "Pembayaran lokal Indonesia via DOKU — Checkout (hosted) + Direct (VA / QRIS / e-Wallet / PayLater / Minimarket / Kartu). HMAC-signed REST + signature-verified webhook + idempotent retries. Sibling slice to midtrans-payment dengan paymentOrders schema yang dishare.",
836
+ "source": "rahmanef63/resource-site",
837
+ "docsUrl": "https://sandbox.doku.com/integration",
838
+ "install": "",
839
+ "npmPackages": [],
840
+ "exampleCode": "",
841
+ "agentRecipe": "DOKU dual-mode: Checkout (hosted, all channels) atau Direct (single channel, returns VA/QRIS/deeplink). Webhook di /webhooks/doku verify HMAC-SHA256 (canonical: Client-Id + Request-Id + Request-Timestamp + Request-Target + Digest). Idempotency by request_id index. Server-only — no NEXT_PUBLIC_*. Sandbox default (api-sandbox.doku.com); flip DOKU_IS_PRODUCTION=true for live.",
842
+ "tags": [
843
+ "payment",
844
+ "doku",
845
+ "indonesia",
846
+ "qris",
847
+ "virtual-account",
848
+ "ewallet",
849
+ "checkout"
850
+ ]
851
+ },
1001
852
  {
1002
853
  "slug": "midtrans-payment",
1003
854
  "title": "Midtrans — Indonesia Payment",
@@ -1125,6 +976,107 @@
1125
976
  "bookings"
1126
977
  ]
1127
978
  },
979
+ {
980
+ "slug": "full-width-toggle",
981
+ "title": "Full Width Toggle",
982
+ "category": "ui",
983
+ "description": "Page-container width toggle (contained / wide / full). Per-device localStorage + cross-tab sync. Ships useFullWidth hook + FullWidthToggle button (icon/button/segment variants) + WidthContainer wrapper. Zero backend, zero env.",
984
+ "source": "rahmanef63/resource-site",
985
+ "docsUrl": "",
986
+ "install": "",
987
+ "npmPackages": [],
988
+ "exampleCode": "",
989
+ "agentRecipe": "Drop <WidthContainer> around page content, <FullWidthToggle variant='icon' /> in topbar. Variant 'segment' best for settings page. Hook useFullWidth() returns [mode, setMode, cycle]. SSR-safe — defaults to 'contained' until hydrate.",
990
+ "tags": [
991
+ "ui",
992
+ "layout",
993
+ "preference",
994
+ "localstorage",
995
+ "dashboard"
996
+ ]
997
+ },
998
+ {
999
+ "slug": "command-menu",
1000
+ "title": "Command Menu (⌘K)",
1001
+ "category": "ui",
1002
+ "description": "Global ⌘K command palette. Auto-builds entries from your feature registry (defineFeature) + custom actions. Fuzzy search via cmdk. Mount once at app shell; listens globally. Facade slice — pulls from template-base/frontend/slices/command-menu which re-exports shared/foundation/utils/system/command-menu.",
1003
+ "source": "superspace + kitab-core",
1004
+ "docsUrl": "https://cmdk.paco.me",
1005
+ "install": "npm i cmdk",
1006
+ "npmPackages": [
1007
+ "cmdk"
1008
+ ],
1009
+ "exampleCode": "",
1010
+ "agentRecipe": "Mount <CommandMenu actions={...} /> once at the dashboard shell level. Listens for Cmd+K globally. Actions auto-build from feature registry + workspace + theme/sign-out. Add custom commands via actions prop or by registering in command-registry.ts.",
1011
+ "tags": [
1012
+ "ui",
1013
+ "palette",
1014
+ "cmd-k",
1015
+ "navigation",
1016
+ "keyboard"
1017
+ ]
1018
+ },
1019
+ {
1020
+ "slug": "motion-primitives",
1021
+ "title": "Motion Primitives (8)",
1022
+ "category": "ui",
1023
+ "description": "Eight ready-to-style motion components: marquee, kinetic-heading, magnetic, cursor-spotlight, stat-counter, reading-progress, grain, lightbox. Framer-Motion-powered, tree-shakeable. Facade slice — pulls from template-base/frontend/slices/motion-primitives.",
1024
+ "source": "rahmanef.com",
1025
+ "docsUrl": "",
1026
+ "install": "npm i framer-motion",
1027
+ "npmPackages": [
1028
+ "framer-motion"
1029
+ ],
1030
+ "exampleCode": "",
1031
+ "agentRecipe": "Each primitive is independently importable from @/features/motion-primitives. Use marquee for logo strips, kinetic-heading for hero text, magnetic for CTA buttons, cursor-spotlight for hover-reveal panels, stat-counter for animated numbers, reading-progress for blog top bar, grain for film texture, lightbox for image gallery.",
1032
+ "tags": [
1033
+ "ui",
1034
+ "motion",
1035
+ "animation",
1036
+ "marquee",
1037
+ "framer-motion"
1038
+ ]
1039
+ },
1040
+ {
1041
+ "slug": "responsive-dialog",
1042
+ "title": "Responsive Dialog (Sheet ↔ Modal)",
1043
+ "category": "ui",
1044
+ "description": "ResponsiveDialog — auto-switches between bottom Sheet (mobile) and centered Dialog (desktop) at the md breakpoint. Same API as shadcn Dialog. Kitab forbids raw <dialog>; use this everywhere. Facade slice — pulls from template-base/frontend/slices/responsive-dialog.",
1045
+ "source": "superspace",
1046
+ "docsUrl": "",
1047
+ "install": "",
1048
+ "npmPackages": [],
1049
+ "exampleCode": "",
1050
+ "agentRecipe": "Drop-in for shadcn Dialog. Use <ResponsiveDialog><ResponsiveDialogTrigger>…</ResponsiveDialogTrigger><ResponsiveDialogContent>…</ResponsiveDialogContent></ResponsiveDialog>. On mobile renders as Sheet sliding from bottom; on desktop as centered Dialog. Threshold via useMediaQuery('(min-width: 768px)').",
1051
+ "tags": [
1052
+ "ui",
1053
+ "dialog",
1054
+ "modal",
1055
+ "sheet",
1056
+ "responsive",
1057
+ "primitive"
1058
+ ]
1059
+ },
1060
+ {
1061
+ "slug": "dashboard-shell",
1062
+ "title": "Dashboard Shell — Responsive",
1063
+ "category": "ui",
1064
+ "description": "ResponsiveDashboardShell — desktop sidebar + topbar, mobile dock + sheet sidebar, breakpoint-aware. Ports superspace's layout/dashboard/{Desktop,Mobile,Responsive}DashboardShell + sidebar primary/secondary slots. Facade slice — pulls from template-base/frontend/slices/dashboard-shell.",
1065
+ "source": "superspace",
1066
+ "docsUrl": "",
1067
+ "install": "",
1068
+ "npmPackages": [],
1069
+ "exampleCode": "",
1070
+ "agentRecipe": "Wraps app/(admin) routes. <ResponsiveDashboardShell sidebar={<AppSidebar />} topbar={<TopBar />}>{children}</ResponsiveDashboardShell>. Mobile: sidebar collapses to <Sheet>. Desktop: persistent sidebar + topbar. Embed FullWidthToggle in topbar for instant container resize.",
1071
+ "tags": [
1072
+ "ui",
1073
+ "layout",
1074
+ "dashboard",
1075
+ "sidebar",
1076
+ "topbar",
1077
+ "responsive"
1078
+ ]
1079
+ },
1128
1080
  {
1129
1081
  "slug": "broadcast-channel-sync",
1130
1082
  "title": "BroadcastChannel — Cross-tab Sync",
@@ -1142,6 +1094,115 @@
1142
1094
  "broadcast-channel",
1143
1095
  "demo-pattern"
1144
1096
  ]
1097
+ },
1098
+ {
1099
+ "slug": "rbac-roles",
1100
+ "title": "RBAC — Tiered System Roles",
1101
+ "category": "auth",
1102
+ "description": "Workspace-scoped RBAC with 6 system roles (owner/admin/manager/staff/client/guest) and three tier presets — solo, influencer, organization. Env-based platform admin bypass via PLATFORM_ADMIN_EMAILS. checkPermission / requirePermission helpers, role seeding mutation, @convex-dev/auth aware (no Clerk).",
1103
+ "source": "superspace",
1104
+ "install": "npx rahman-resources add rbac-roles",
1105
+ "npmPackages": [],
1106
+ "exampleCode": "",
1107
+ "agentRecipe": "Three tier presets pick which system roles to seed: solo (owner+admin), influencer (+manager), organization (6 roles). Platform admin via env PLATFORM_ADMIN_EMAILS bypasses all checks. Resolution: platform admin → workspace owner → membership.additionalPermissions → role.permissions.",
1108
+ "tags": [
1109
+ "rbac",
1110
+ "auth",
1111
+ "permissions",
1112
+ "roles",
1113
+ "workspaces"
1114
+ ]
1115
+ },
1116
+ {
1117
+ "slug": "admin-panel",
1118
+ "title": "Admin Panel — Unified Product Admin",
1119
+ "category": "infra",
1120
+ "description": "17-section admin surface (events, funnels, attribution, users, A/B, flags, pricing, CMS, email, audit, ...) gated by RBAC. Auto-filters sidebar by tier (solo/influencer/organization) and user permissions. Single backend resolver (getMyAdminAccess) mirrors frontend gate so UI can never leak.",
1121
+ "source": "superspace + spec",
1122
+ "install": "npx rahman-resources add admin-panel",
1123
+ "npmPackages": [],
1124
+ "exampleCode": "",
1125
+ "agentRecipe": "Wrap pages with <AdminPage workspaceId tier>. AccessGate hides UI for non-admins, AdminShell renders 2-col layout with sidebar filtered by tier+perms. ADMIN_SECTIONS in config.ts is SSOT (17 entries). Personal-brand-os = tier 'solo' = owner sees everything.",
1126
+ "tags": [
1127
+ "admin",
1128
+ "owner",
1129
+ "platform",
1130
+ "rbac",
1131
+ "instrumentation",
1132
+ "panel"
1133
+ ]
1134
+ },
1135
+ {
1136
+ "slug": "event-tracking",
1137
+ "title": "Event Tracking — P0 Instrumentation",
1138
+ "category": "data",
1139
+ "description": "Client SDK + Convex ingestion endpoint for structured product events. Auto-captures page_view/signup/login + UTM/referrer/first-touch attribution. Batched flush via requestIdleCallback. Targets <100ms p99 ingestion.",
1140
+ "source": "spec + superspace analytics",
1141
+ "install": "npx rahman-resources add event-tracking",
1142
+ "npmPackages": [],
1143
+ "exampleCode": "",
1144
+ "agentRecipe": "Writes to analyticsEvents table (no new schema). Anonymous page_view allowed pre-signup; other events require workspaceId. Session id per tab (sessionStorage), first-touch UTM in localStorage. Flush every ~500ms via requestIdleCallback. Cap retry queue at 500.",
1145
+ "tags": [
1146
+ "events",
1147
+ "analytics",
1148
+ "instrumentation",
1149
+ "attribution",
1150
+ "utm",
1151
+ "p0"
1152
+ ]
1153
+ },
1154
+ {
1155
+ "slug": "theme-preset-switcher",
1156
+ "title": "Theme Preset Switcher",
1157
+ "category": "ui",
1158
+ "description": "Runtime theme swap (colors + fonts + shadows + tracking). OKLch CSS vars per preset. Persists to localStorage + Convex. Add a new preset by appending a CSS block in app/globals.css with [data-theme=\"<name>\"], then register in preset-groups.ts.",
1159
+ "source": "rahmanef.com",
1160
+ "install": "npx rahman-resources add theme-preset-switcher",
1161
+ "npmPackages": [],
1162
+ "exampleCode": "",
1163
+ "agentRecipe": "Import ThemePresetSwitcher from @/frontend/shared/ui/components/theme-preset-switcher and mount in the topbar. Presets live in theme-presets.ts; preset-groups.ts groups them for the picker UI.",
1164
+ "tags": [
1165
+ "theme",
1166
+ "presets",
1167
+ "oklch",
1168
+ "design-system"
1169
+ ]
1170
+ },
1171
+ {
1172
+ "slug": "icon-picker",
1173
+ "title": "Notion-Style Icon Picker",
1174
+ "category": "ui",
1175
+ "description": "Emoji + lucide icon picker with search, 10-color Notion palette, Twemoji/native toggle. One string stores emoji OR lucide:Name OR with ?c=hex tint — backwards-compat with raw-emoji fields. Single icon field stores emoji or 'lucide:Name' plus optional '?c=hex'.",
1176
+ "source": "notion-page-clone",
1177
+ "install": "npx rahman-resources add icon-picker",
1178
+ "npmPackages": [],
1179
+ "exampleCode": "",
1180
+ "agentRecipe": "parseIconValue() decodes; lucideValue()/withColor() build. Add 'icon: v.string()' to Convex table — no migration needed for existing emoji fields. Popover variant for inline UI, Inline for sheets/dialogs.",
1181
+ "tags": [
1182
+ "icon",
1183
+ "emoji",
1184
+ "lucide",
1185
+ "picker",
1186
+ "twemoji",
1187
+ "notion"
1188
+ ]
1189
+ },
1190
+ {
1191
+ "slug": "contact-form-resend",
1192
+ "title": "Contact Form + Resend",
1193
+ "category": "email",
1194
+ "description": "Contact form posting to Resend email API. Server Action + Zod input validation. Convex mutation for storage + Resend send.",
1195
+ "source": "cescadesigns",
1196
+ "install": "npx rahman-resources add contact-form-resend",
1197
+ "npmPackages": [],
1198
+ "exampleCode": "",
1199
+ "agentRecipe": "Wire contactMessages.send mutation in convex/. Server emails via Resend from form@yourdomain.com. Always validate inputs with Zod or v.* server-side. Anonymous allowed.",
1200
+ "tags": [
1201
+ "form",
1202
+ "email",
1203
+ "resend",
1204
+ "convex"
1205
+ ]
1145
1206
  }
1146
1207
  ],
1147
1208
  "slices": [
@@ -1149,6 +1210,7 @@
1149
1210
  "slug": "convex-auth",
1150
1211
  "title": "Convex Auth — Email Magic Link",
1151
1212
  "category": "auth",
1213
+ "kind": "backend",
1152
1214
  "version": "0.1.0",
1153
1215
  "description": "@convex-dev/auth with email magic link via Resend. Self-hosted Convex friendly. Hard mandate per kitab CLAUDE.md (no Clerk).",
1154
1216
  "source": "rahmanef63/resource-site",
@@ -1197,10 +1259,75 @@
1197
1259
  ],
1198
1260
  "agentRecipe": "Run `rr add convex-auth`. Then create convex/auth.ts using the kitab pattern (Resend provider). Set env via `npx convex env set` for self-hosted."
1199
1261
  },
1262
+ {
1263
+ "slug": "doku-payment",
1264
+ "title": "DOKU — Indonesia Payment",
1265
+ "category": "payment",
1266
+ "kind": "full",
1267
+ "version": "0.1.0",
1268
+ "description": "Pembayaran lokal Indonesia via DOKU — Checkout (hosted) + Direct (VA / QRIS / e-Wallet / PayLater / Minimarket / Kartu). HMAC-signed REST + signature-verified webhook + idempotent retries. Sibling slice to midtrans-payment dengan paymentOrders schema yang dishare.",
1269
+ "source": "rahmanef63/resource-site",
1270
+ "slicePath": "frontend/slices/doku-payment",
1271
+ "convexPaths": [
1272
+ "convex/features/payment"
1273
+ ],
1274
+ "npm": [],
1275
+ "shadcn": [
1276
+ "card",
1277
+ "button",
1278
+ "dialog",
1279
+ "input",
1280
+ "label",
1281
+ "select",
1282
+ "badge",
1283
+ "skeleton"
1284
+ ],
1285
+ "env": [
1286
+ {
1287
+ "name": "DOKU_CLIENT_ID",
1288
+ "scope": "convex",
1289
+ "required": true
1290
+ },
1291
+ {
1292
+ "name": "DOKU_SECRET_KEY",
1293
+ "scope": "convex",
1294
+ "required": true
1295
+ },
1296
+ {
1297
+ "name": "DOKU_IS_PRODUCTION",
1298
+ "scope": "convex"
1299
+ },
1300
+ {
1301
+ "name": "DOKU_NOTIFY_PATH",
1302
+ "scope": "convex"
1303
+ }
1304
+ ],
1305
+ "peers": [
1306
+ {
1307
+ "slug": "convex-auth",
1308
+ "range": "^0.1",
1309
+ "reason": "Order ownership requires authenticated user."
1310
+ }
1311
+ ],
1312
+ "providers": [
1313
+ "doku"
1314
+ ],
1315
+ "tags": [
1316
+ "payment",
1317
+ "doku",
1318
+ "indonesia",
1319
+ "qris",
1320
+ "virtual-account",
1321
+ "ewallet",
1322
+ "checkout"
1323
+ ],
1324
+ "agentRecipe": "DOKU dual-mode: Checkout (hosted, all channels) atau Direct (single channel, returns VA/QRIS/deeplink). Webhook di /webhooks/doku verify HMAC-SHA256 (canonical: Client-Id + Request-Id + Request-Timestamp + Request-Target + Digest). Idempotency by request_id index. Server-only — no NEXT_PUBLIC_*. Sandbox default (api-sandbox.doku.com); flip DOKU_IS_PRODUCTION=true for live."
1325
+ },
1200
1326
  {
1201
1327
  "slug": "midtrans-payment",
1202
1328
  "title": "Midtrans — Indonesia Payment",
1203
1329
  "category": "payment",
1330
+ "kind": "full",
1204
1331
  "version": "0.1.0",
1205
1332
  "description": "Pembayaran lokal Indonesia via Midtrans Snap (BCA, Mandiri, BRI, e-wallet GoPay/OVO/Dana, QRIS). Webhook untuk konfirmasi. Provider-isolated under components/providers/midtrans + actions/midtrans so Doku/Stripe land as siblings.",
1206
1333
  "source": "rahmanef63/resource-site",
@@ -1257,6 +1384,7 @@
1257
1384
  "slug": "resend-newsletter",
1258
1385
  "title": "Resend — Transactional & Newsletter",
1259
1386
  "category": "email",
1387
+ "kind": "backend",
1260
1388
  "version": "0.1.0",
1261
1389
  "description": "Transactional email + newsletter blast via Resend. Double opt-in flow + audience segmentation. Magic-link delivery for Convex Auth.",
1262
1390
  "source": "rahmanef63/resource-site",
@@ -1299,6 +1427,7 @@
1299
1427
  "slug": "ai-router",
1300
1428
  "title": "AI Router (OpenRouter)",
1301
1429
  "category": "ai",
1430
+ "kind": "backend",
1302
1431
  "version": "0.1.0",
1303
1432
  "description": "Tier-routed LLM access via OpenRouter — nano (Haiku) for classification, mid (Sonnet) for chat, flagship (Opus) for deep reasoning. Per-call usage log.",
1304
1433
  "source": "rahmanef63/resource-site",
@@ -1334,6 +1463,7 @@
1334
1463
  "slug": "vector-search",
1335
1464
  "title": "Convex Vector Search",
1336
1465
  "category": "search",
1466
+ "kind": "full",
1337
1467
  "version": "0.1.0",
1338
1468
  "description": "Embeddings-based search via Convex's built-in vector index. Embed via OpenAI text-embedding-3-small (1536-dim), query via vectorIndex().",
1339
1469
  "source": "rahmanef63/resource-site",
@@ -1370,6 +1500,7 @@
1370
1500
  "slug": "mdx-blog",
1371
1501
  "title": "MDX Blog",
1372
1502
  "category": "content",
1503
+ "kind": "ui",
1373
1504
  "version": "0.1.0",
1374
1505
  "description": "Markdown-with-JSX untuk blog post. File-based under content/blog/*.mdx. Auto-generate ToC, reading-time, syntax highlight, plus embed React components inline.",
1375
1506
  "source": "rahmanef63/resource-site",
@@ -1398,6 +1529,7 @@
1398
1529
  "slug": "cal-com-booking",
1399
1530
  "title": "Cal.com Booking",
1400
1531
  "category": "data",
1532
+ "kind": "full",
1401
1533
  "version": "0.1.0",
1402
1534
  "description": "Embedded Cal.com booking widget + webhook receiver to mirror bookings into Convex.",
1403
1535
  "source": "rahmanef63/resource-site",
@@ -1433,10 +1565,156 @@
1433
1565
  ],
1434
1566
  "agentRecipe": "Embed Cal.com via @calcom/embed-react di halaman services. Configure webhook di Cal.com dashboard → POST ke /api/cal-webhook → upsert booking di Convex."
1435
1567
  },
1568
+ {
1569
+ "slug": "full-width-toggle",
1570
+ "title": "Full Width Toggle",
1571
+ "category": "ui",
1572
+ "kind": "ui",
1573
+ "version": "0.1.0",
1574
+ "description": "Page-container width toggle (contained / wide / full). Per-device localStorage + cross-tab sync. Ships useFullWidth hook + FullWidthToggle button (icon/button/segment variants) + WidthContainer wrapper. Zero backend, zero env.",
1575
+ "source": "rahmanef63/resource-site",
1576
+ "slicePath": "frontend/slices/full-width-toggle",
1577
+ "convexPaths": [],
1578
+ "npm": [],
1579
+ "shadcn": [
1580
+ "button"
1581
+ ],
1582
+ "env": [],
1583
+ "peers": [],
1584
+ "providers": [],
1585
+ "tags": [
1586
+ "ui",
1587
+ "layout",
1588
+ "preference",
1589
+ "localstorage",
1590
+ "dashboard"
1591
+ ],
1592
+ "agentRecipe": "Drop <WidthContainer> around page content, <FullWidthToggle variant='icon' /> in topbar. Variant 'segment' best for settings page. Hook useFullWidth() returns [mode, setMode, cycle]. SSR-safe — defaults to 'contained' until hydrate."
1593
+ },
1594
+ {
1595
+ "slug": "command-menu",
1596
+ "title": "Command Menu (⌘K)",
1597
+ "category": "ui",
1598
+ "kind": "ui",
1599
+ "version": "0.1.0",
1600
+ "description": "Global ⌘K command palette. Auto-builds entries from your feature registry (defineFeature) + custom actions. Fuzzy search via cmdk. Mount once at app shell; listens globally. Facade slice — pulls from template-base/frontend/slices/command-menu which re-exports shared/foundation/utils/system/command-menu.",
1601
+ "source": "superspace + kitab-core",
1602
+ "slicePath": "template-base/frontend/slices/command-menu",
1603
+ "convexPaths": [],
1604
+ "npm": [
1605
+ "cmdk@^1.0.0"
1606
+ ],
1607
+ "shadcn": [
1608
+ "command",
1609
+ "dialog"
1610
+ ],
1611
+ "env": [],
1612
+ "peers": [],
1613
+ "providers": [],
1614
+ "tags": [
1615
+ "ui",
1616
+ "palette",
1617
+ "cmd-k",
1618
+ "navigation",
1619
+ "keyboard"
1620
+ ],
1621
+ "agentRecipe": "Mount <CommandMenu actions={...} /> once at the dashboard shell level. Listens for Cmd+K globally. Actions auto-build from feature registry + workspace + theme/sign-out. Add custom commands via actions prop or by registering in command-registry.ts."
1622
+ },
1623
+ {
1624
+ "slug": "motion-primitives",
1625
+ "title": "Motion Primitives (8)",
1626
+ "category": "ui",
1627
+ "kind": "ui",
1628
+ "version": "0.1.0",
1629
+ "description": "Eight ready-to-style motion components: marquee, kinetic-heading, magnetic, cursor-spotlight, stat-counter, reading-progress, grain, lightbox. Framer-Motion-powered, tree-shakeable. Facade slice — pulls from template-base/frontend/slices/motion-primitives.",
1630
+ "source": "rahmanef.com",
1631
+ "slicePath": "template-base/frontend/slices/motion-primitives",
1632
+ "convexPaths": [],
1633
+ "npm": [
1634
+ "framer-motion@^11.0.0"
1635
+ ],
1636
+ "shadcn": [],
1637
+ "env": [],
1638
+ "peers": [],
1639
+ "providers": [],
1640
+ "tags": [
1641
+ "ui",
1642
+ "motion",
1643
+ "animation",
1644
+ "marquee",
1645
+ "framer-motion"
1646
+ ],
1647
+ "agentRecipe": "Each primitive is independently importable from @/features/motion-primitives. Use marquee for logo strips, kinetic-heading for hero text, magnetic for CTA buttons, cursor-spotlight for hover-reveal panels, stat-counter for animated numbers, reading-progress for blog top bar, grain for film texture, lightbox for image gallery."
1648
+ },
1649
+ {
1650
+ "slug": "responsive-dialog",
1651
+ "title": "Responsive Dialog (Sheet ↔ Modal)",
1652
+ "category": "ui",
1653
+ "kind": "ui",
1654
+ "version": "0.1.0",
1655
+ "description": "ResponsiveDialog — auto-switches between bottom Sheet (mobile) and centered Dialog (desktop) at the md breakpoint. Same API as shadcn Dialog. Kitab forbids raw <dialog>; use this everywhere. Facade slice — pulls from template-base/frontend/slices/responsive-dialog.",
1656
+ "source": "superspace",
1657
+ "slicePath": "template-base/frontend/slices/responsive-dialog",
1658
+ "convexPaths": [],
1659
+ "npm": [],
1660
+ "shadcn": [
1661
+ "dialog",
1662
+ "sheet"
1663
+ ],
1664
+ "env": [],
1665
+ "peers": [],
1666
+ "providers": [],
1667
+ "tags": [
1668
+ "ui",
1669
+ "dialog",
1670
+ "modal",
1671
+ "sheet",
1672
+ "responsive",
1673
+ "primitive"
1674
+ ],
1675
+ "agentRecipe": "Drop-in for shadcn Dialog. Use <ResponsiveDialog><ResponsiveDialogTrigger>…</ResponsiveDialogTrigger><ResponsiveDialogContent>…</ResponsiveDialogContent></ResponsiveDialog>. On mobile renders as Sheet sliding from bottom; on desktop as centered Dialog. Threshold via useMediaQuery('(min-width: 768px)')."
1676
+ },
1677
+ {
1678
+ "slug": "dashboard-shell",
1679
+ "title": "Dashboard Shell — Responsive",
1680
+ "category": "ui",
1681
+ "kind": "ui",
1682
+ "version": "0.1.0",
1683
+ "description": "ResponsiveDashboardShell — desktop sidebar + topbar, mobile dock + sheet sidebar, breakpoint-aware. Ports superspace's layout/dashboard/{Desktop,Mobile,Responsive}DashboardShell + sidebar primary/secondary slots. Facade slice — pulls from template-base/frontend/slices/dashboard-shell.",
1684
+ "source": "superspace",
1685
+ "slicePath": "template-base/frontend/slices/dashboard-shell",
1686
+ "convexPaths": [],
1687
+ "npm": [],
1688
+ "shadcn": [
1689
+ "sheet",
1690
+ "scroll-area",
1691
+ "separator",
1692
+ "tooltip"
1693
+ ],
1694
+ "env": [],
1695
+ "peers": [
1696
+ {
1697
+ "slug": "full-width-toggle",
1698
+ "range": "^0.1",
1699
+ "reason": "Topbar surfaces the width toggle when this is mounted."
1700
+ }
1701
+ ],
1702
+ "providers": [],
1703
+ "tags": [
1704
+ "ui",
1705
+ "layout",
1706
+ "dashboard",
1707
+ "sidebar",
1708
+ "topbar",
1709
+ "responsive"
1710
+ ],
1711
+ "agentRecipe": "Wraps app/(admin) routes. <ResponsiveDashboardShell sidebar={<AppSidebar />} topbar={<TopBar />}>{children}</ResponsiveDashboardShell>. Mobile: sidebar collapses to <Sheet>. Desktop: persistent sidebar + topbar. Embed FullWidthToggle in topbar for instant container resize."
1712
+ },
1436
1713
  {
1437
1714
  "slug": "broadcast-channel-sync",
1438
1715
  "title": "BroadcastChannel — Cross-tab Sync",
1439
1716
  "category": "realtime",
1717
+ "kind": "ui",
1440
1718
  "version": "0.1.0",
1441
1719
  "description": "Same-origin cross-tab + cross-iframe state sync via BroadcastChannel API. Tiny, no backend, no install.",
1442
1720
  "source": "Web Platform — BroadcastChannel API",
@@ -1454,6 +1732,211 @@
1454
1732
  "demo-pattern"
1455
1733
  ],
1456
1734
  "agentRecipe": "Use BroadcastChannel only for demo / cross-iframe state mirroring. Production data still goes through Convex realtime. Use the useBroadcastSync(channelName, initial) hook from @/features/broadcast-channel-sync."
1735
+ },
1736
+ {
1737
+ "slug": "rbac-roles",
1738
+ "title": "RBAC — Tiered System Roles",
1739
+ "category": "auth",
1740
+ "kind": "backend",
1741
+ "version": "0.1.0",
1742
+ "description": "Workspace-scoped RBAC with 6 system roles (owner/admin/manager/staff/client/guest) and three tier presets — solo, influencer, organization. Env-based platform admin bypass via PLATFORM_ADMIN_EMAILS. checkPermission / requirePermission helpers, role seeding mutation, @convex-dev/auth aware (no Clerk).",
1743
+ "source": "superspace",
1744
+ "slicePath": "",
1745
+ "convexPaths": [
1746
+ "template-base/convex/lib/rbac"
1747
+ ],
1748
+ "npm": [],
1749
+ "shadcn": [],
1750
+ "env": [
1751
+ {
1752
+ "name": "PLATFORM_ADMIN_EMAILS",
1753
+ "scope": "convex",
1754
+ "description": "Comma-separated emails granted cross-workspace admin."
1755
+ }
1756
+ ],
1757
+ "peers": [
1758
+ {
1759
+ "slug": "convex-auth",
1760
+ "range": "^0.1",
1761
+ "reason": "RBAC checks the authed user identity."
1762
+ }
1763
+ ],
1764
+ "providers": [],
1765
+ "tags": [
1766
+ "rbac",
1767
+ "auth",
1768
+ "permissions",
1769
+ "roles",
1770
+ "workspaces"
1771
+ ],
1772
+ "agentRecipe": "Three tier presets pick which system roles to seed: solo (owner+admin), influencer (+manager), organization (6 roles). Platform admin via env PLATFORM_ADMIN_EMAILS bypasses all checks. Resolution: platform admin → workspace owner → membership.additionalPermissions → role.permissions."
1773
+ },
1774
+ {
1775
+ "slug": "admin-panel",
1776
+ "title": "Admin Panel — Unified Product Admin",
1777
+ "category": "infra",
1778
+ "kind": "full",
1779
+ "version": "0.1.0",
1780
+ "description": "17-section admin surface (events, funnels, attribution, users, A/B, flags, pricing, CMS, email, audit, ...) gated by RBAC. Auto-filters sidebar by tier (solo/influencer/organization) and user permissions. Single backend resolver (getMyAdminAccess) mirrors frontend gate so UI can never leak.",
1781
+ "source": "superspace + spec",
1782
+ "slicePath": "template-base/frontend/slices/admin-panel",
1783
+ "convexPaths": [
1784
+ "template-base/convex/features/admin-panel"
1785
+ ],
1786
+ "npm": [],
1787
+ "shadcn": [
1788
+ "card",
1789
+ "badge",
1790
+ "button"
1791
+ ],
1792
+ "env": [],
1793
+ "peers": [
1794
+ {
1795
+ "slug": "rbac-roles",
1796
+ "range": "^0.1",
1797
+ "reason": "Admin sections require RBAC perms — must seed roles first."
1798
+ }
1799
+ ],
1800
+ "providers": [],
1801
+ "tags": [
1802
+ "admin",
1803
+ "owner",
1804
+ "platform",
1805
+ "rbac",
1806
+ "instrumentation",
1807
+ "panel"
1808
+ ],
1809
+ "agentRecipe": "Wrap pages with <AdminPage workspaceId tier>. AccessGate hides UI for non-admins, AdminShell renders 2-col layout with sidebar filtered by tier+perms. ADMIN_SECTIONS in config.ts is SSOT (17 entries). Personal-brand-os = tier 'solo' = owner sees everything."
1810
+ },
1811
+ {
1812
+ "slug": "event-tracking",
1813
+ "title": "Event Tracking — P0 Instrumentation",
1814
+ "category": "data",
1815
+ "kind": "full",
1816
+ "version": "0.1.0",
1817
+ "description": "Client SDK + Convex ingestion endpoint for structured product events. Auto-captures page_view/signup/login + UTM/referrer/first-touch attribution. Batched flush via requestIdleCallback. Targets <100ms p99 ingestion.",
1818
+ "source": "spec + superspace analytics",
1819
+ "slicePath": "template-base/frontend/slices/admin-panel/slices/events",
1820
+ "convexPaths": [
1821
+ "template-base/convex/features/admin-panel",
1822
+ "template-base/convex/features/analytics"
1823
+ ],
1824
+ "npm": [],
1825
+ "shadcn": [],
1826
+ "env": [],
1827
+ "peers": [
1828
+ {
1829
+ "slug": "admin-panel",
1830
+ "range": "^0.1",
1831
+ "reason": "Lives under admin slice events subfolder."
1832
+ }
1833
+ ],
1834
+ "providers": [],
1835
+ "tags": [
1836
+ "events",
1837
+ "analytics",
1838
+ "instrumentation",
1839
+ "attribution",
1840
+ "utm",
1841
+ "p0"
1842
+ ],
1843
+ "agentRecipe": "Writes to analyticsEvents table (no new schema). Anonymous page_view allowed pre-signup; other events require workspaceId. Session id per tab (sessionStorage), first-touch UTM in localStorage. Flush every ~500ms via requestIdleCallback. Cap retry queue at 500."
1844
+ },
1845
+ {
1846
+ "slug": "theme-preset-switcher",
1847
+ "title": "Theme Preset Switcher",
1848
+ "category": "ui",
1849
+ "kind": "ui",
1850
+ "version": "0.1.0",
1851
+ "description": "Runtime theme swap (colors + fonts + shadows + tracking). OKLch CSS vars per preset. Persists to localStorage + Convex. Add a new preset by appending a CSS block in app/globals.css with [data-theme=\"<name>\"], then register in preset-groups.ts.",
1852
+ "source": "rahmanef.com",
1853
+ "slicePath": "template-base/frontend/shared/theme",
1854
+ "convexPaths": [],
1855
+ "npm": [],
1856
+ "shadcn": [],
1857
+ "env": [],
1858
+ "peers": [],
1859
+ "providers": [],
1860
+ "tags": [
1861
+ "theme",
1862
+ "presets",
1863
+ "oklch",
1864
+ "design-system"
1865
+ ],
1866
+ "agentRecipe": "Import ThemePresetSwitcher from @/frontend/shared/ui/components/theme-preset-switcher and mount in the topbar. Presets live in theme-presets.ts; preset-groups.ts groups them for the picker UI."
1867
+ },
1868
+ {
1869
+ "slug": "icon-picker",
1870
+ "title": "Notion-Style Icon Picker",
1871
+ "category": "ui",
1872
+ "kind": "ui",
1873
+ "version": "0.1.0",
1874
+ "description": "Emoji + lucide icon picker with search, 10-color Notion palette, Twemoji/native toggle. One string stores emoji OR lucide:Name OR with ?c=hex tint — backwards-compat with raw-emoji fields. Single icon field stores emoji or 'lucide:Name' plus optional '?c=hex'.",
1875
+ "source": "notion-page-clone",
1876
+ "slicePath": "template-base/frontend/slices/notion/slices/icon-picker",
1877
+ "convexPaths": [],
1878
+ "npm": [],
1879
+ "shadcn": [
1880
+ "popover",
1881
+ "button",
1882
+ "input"
1883
+ ],
1884
+ "env": [],
1885
+ "peers": [],
1886
+ "providers": [],
1887
+ "tags": [
1888
+ "icon",
1889
+ "emoji",
1890
+ "lucide",
1891
+ "picker",
1892
+ "twemoji",
1893
+ "notion"
1894
+ ],
1895
+ "agentRecipe": "parseIconValue() decodes; lucideValue()/withColor() build. Add 'icon: v.string()' to Convex table — no migration needed for existing emoji fields. Popover variant for inline UI, Inline for sheets/dialogs."
1896
+ },
1897
+ {
1898
+ "slug": "contact-form-resend",
1899
+ "title": "Contact Form + Resend",
1900
+ "category": "email",
1901
+ "kind": "full",
1902
+ "version": "0.1.0",
1903
+ "description": "Contact form posting to Resend email API. Server Action + Zod input validation. Convex mutation for storage + Resend send.",
1904
+ "source": "cescadesigns",
1905
+ "slicePath": "template-base/frontend/slices/contact-form-resend",
1906
+ "convexPaths": [],
1907
+ "npm": [
1908
+ "resend@^4.0.0",
1909
+ "framer-motion@^11.0.0"
1910
+ ],
1911
+ "shadcn": [
1912
+ "card",
1913
+ "button",
1914
+ "input",
1915
+ "label",
1916
+ "textarea"
1917
+ ],
1918
+ "env": [
1919
+ {
1920
+ "name": "RESEND_API_KEY",
1921
+ "scope": "convex",
1922
+ "required": true
1923
+ }
1924
+ ],
1925
+ "peers": [
1926
+ {
1927
+ "slug": "convex-auth",
1928
+ "range": "^0.1",
1929
+ "reason": "Optional — anonymous submission works without auth."
1930
+ }
1931
+ ],
1932
+ "providers": [],
1933
+ "tags": [
1934
+ "form",
1935
+ "email",
1936
+ "resend",
1937
+ "convex"
1938
+ ],
1939
+ "agentRecipe": "Wire contactMessages.send mutation in convex/. Server emails via Resend from form@yourdomain.com. Always validate inputs with Zod or v.* server-side. Anonymous allowed."
1457
1940
  }
1458
1941
  ]
1459
1942
  }
@@ -43,6 +43,11 @@
43
43
  "infra"
44
44
  ]
45
45
  },
46
+ "kind": {
47
+ "type": "string",
48
+ "enum": ["ui", "backend", "full"],
49
+ "description": "Slice shape — ui (no convex), backend (no UI), full (both)."
50
+ },
46
51
  "title": { "type": "string", "minLength": 3 },
47
52
  "description": { "type": "string", "minLength": 10 },
48
53
  "namespace": {
@@ -53,12 +58,11 @@
53
58
  "convex": {
54
59
  "type": "object",
55
60
  "additionalProperties": false,
56
- "required": ["rootPaths"],
57
61
  "properties": {
58
62
  "tablesExport": {
59
63
  "type": "string",
60
- "pattern": "^[a-zA-Z][a-zA-Z0-9_]*$",
61
- "description": "Name of the table-fragment export in schema.ts (e.g., midtransTables)."
64
+ "pattern": "^([a-zA-Z][a-zA-Z0-9_]*)?$",
65
+ "description": "Name of the table-fragment export in schema.ts (e.g., midtransTables). Empty for ui-only slices."
62
66
  },
63
67
  "schemaPath": {
64
68
  "type": "string",
@@ -66,25 +70,25 @@
66
70
  },
67
71
  "rootPaths": {
68
72
  "type": "array",
69
- "minItems": 1,
70
73
  "items": { "type": "string" },
71
- "description": "Convex folders the lift pulls into the consumer's convex/."
74
+ "description": "Convex folders the lift pulls into the consumer's convex/. Empty for ui-only slices."
72
75
  }
73
76
  }
74
77
  },
75
78
  "frontend": {
76
79
  "type": "object",
77
80
  "additionalProperties": false,
78
- "required": ["slicePath", "configExport"],
81
+ "required": ["slicePath"],
79
82
  "properties": {
80
83
  "slicePath": {
81
84
  "type": "string",
82
- "pattern": "^frontend/slices/[a-z0-9_/-]+$"
85
+ "pattern": "^(template-base/)?frontend/slices/[a-z0-9_/-]+$",
86
+ "description": "Portable home → frontend/slices/<slug>. Foundation home → template-base/frontend/slices/<slug>."
83
87
  },
84
88
  "configExport": {
85
89
  "type": "string",
86
- "pattern": "^[a-zA-Z][a-zA-Z0-9_]*$",
87
- "description": "Name of the defineFeature(...) export the registry generators look for."
90
+ "pattern": "^([a-zA-Z][a-zA-Z0-9_]*)?$",
91
+ "description": "Name of the defineFeature(...) export the registry generators look for. Empty for ui-only slices."
88
92
  }
89
93
  }
90
94
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rahman-resources",
3
- "version": "0.9.2",
3
+ "version": "0.11.1",
4
4
  "description": "Scaffolder + installer for Rahman Resources kitab — npx rahman-resources init/add/lift/scaffold-slice/publish-slice. Tier-3 portable feature slices + manifest + skills + CRUD workflows.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -26,9 +26,12 @@
26
26
  "gen": "node scripts/gen-manifest.mjs",
27
27
  "validate": "node scripts/validate.mjs",
28
28
  "validate:slices": "node scripts/validate-slice.mjs",
29
+ "validate:parity": "node scripts/validate-slice-parity.mjs",
30
+ "validate:structure": "node scripts/validate-structure.mjs",
31
+ "validate:all": "node scripts/validate.mjs && node scripts/validate-slice.mjs --check && node scripts/validate-slice-parity.mjs && node scripts/validate-structure.mjs",
29
32
  "sync:skills": "node scripts/sync-skills.mjs",
30
33
  "sync:skills:check": "node scripts/sync-skills.mjs --check",
31
- "prepublishOnly": "node scripts/sync-skills.mjs --check && node scripts/validate.mjs && node scripts/validate-slice.mjs --check"
34
+ "prepublishOnly": "node scripts/sync-skills.mjs --check && node scripts/validate.mjs && node scripts/validate-slice.mjs --check && node scripts/validate-slice-parity.mjs && node scripts/validate-structure.mjs"
32
35
  },
33
36
  "dependencies": {
34
37
  "kleur": "^4.1.5",