rahman-resources 1.12.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/contract-types.ts +15 -35
- package/lib/contract-validate.ts +12 -21
- package/lib/contract.ts +2 -4
- package/lib/manifest.json +59 -10
- package/package.json +2 -2
package/lib/contract-types.ts
CHANGED
|
@@ -91,31 +91,20 @@ export interface SliceContractProvides {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
// ---------------------------------------------------------------------------
|
|
94
|
-
//
|
|
94
|
+
// Generalisation gate
|
|
95
95
|
// ---------------------------------------------------------------------------
|
|
96
|
+
// (Formerly nested inside the Wave N+3 `bidir` block. BSDL was removed in
|
|
97
|
+
// Sesi 2; the sync policy died with it, but the generalisation contract is
|
|
98
|
+
// still consumed by scripts/validation/check-forbidden-terms.mjs — so it
|
|
99
|
+
// lives on as a top-level field.)
|
|
96
100
|
|
|
97
101
|
/**
|
|
98
|
-
*
|
|
102
|
+
* Portability level of the slice source.
|
|
99
103
|
*
|
|
100
|
-
* - `
|
|
101
|
-
*
|
|
102
|
-
* the kitab. Reserved for slices with strict generalisation gates.
|
|
103
|
-
* - `notify`: surface in the scan report; no auto-action.
|
|
104
|
-
* - `manual`: default — operator picks up via `/rr-prep` + `/rr-send`.
|
|
105
|
-
* - `frozen`: kitab refuses both UP and DOWN sync. Lock for retired slices.
|
|
106
|
-
*/
|
|
107
|
-
export type SliceSyncPolicy = "auto-pr" | "notify" | "manual" | "frozen";
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Generalisation level a consumer-side `.kitab.json` MUST claim before
|
|
111
|
-
* `rr-send` accepts the push back into the kitab.
|
|
112
|
-
*
|
|
113
|
-
* - `portable`: no consumer-specific business terms baked in. UP-sync allowed.
|
|
114
|
-
* - `needs-adapter`: requires a thin adapter wired by the consumer; UP-sync
|
|
115
|
-
* blocked until blockers are addressed (or the contract drops the slice
|
|
116
|
-
* to `consumer-locked`).
|
|
104
|
+
* - `portable`: no consumer-specific business terms baked in.
|
|
105
|
+
* - `needs-adapter`: requires a thin adapter wired by the consumer.
|
|
117
106
|
* - `consumer-locked`: contains business-specific logic that cannot be
|
|
118
|
-
* generalised.
|
|
107
|
+
* generalised.
|
|
119
108
|
*/
|
|
120
109
|
export type GeneralizationLevel =
|
|
121
110
|
| "portable"
|
|
@@ -123,14 +112,15 @@ export type GeneralizationLevel =
|
|
|
123
112
|
| "consumer-locked";
|
|
124
113
|
|
|
125
114
|
/**
|
|
126
|
-
* Generalisation contract — what the
|
|
127
|
-
*
|
|
115
|
+
* Generalisation contract — what the forbidden-terms audit scans for, and
|
|
116
|
+
* which props the consumer MUST inject.
|
|
128
117
|
*/
|
|
129
118
|
export interface SliceGeneralization {
|
|
130
119
|
level: GeneralizationLevel;
|
|
131
120
|
/**
|
|
132
121
|
* Identifiers / business terms that MUST NOT appear in the slice source
|
|
133
|
-
* tree.
|
|
122
|
+
* tree. check-forbidden-terms.mjs scans .ts/.tsx files. Empty when the
|
|
123
|
+
* slice is generic.
|
|
134
124
|
*/
|
|
135
125
|
forbiddenTerms?: string[];
|
|
136
126
|
/**
|
|
@@ -140,16 +130,6 @@ export interface SliceGeneralization {
|
|
|
140
130
|
requiredProps?: string[];
|
|
141
131
|
}
|
|
142
132
|
|
|
143
|
-
/**
|
|
144
|
-
* Bidirectional sync block. Optional, additive — slices without it default to
|
|
145
|
-
* `{ syncPolicy: "manual", generalization: { level: "portable" } }` for
|
|
146
|
-
* legacy compatibility with Wave N+1 contracts.
|
|
147
|
-
*/
|
|
148
|
-
export interface SliceBidirContract {
|
|
149
|
-
syncPolicy: SliceSyncPolicy;
|
|
150
|
-
generalization: SliceGeneralization;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
133
|
// ---------------------------------------------------------------------------
|
|
154
134
|
// Top-level contract
|
|
155
135
|
// ---------------------------------------------------------------------------
|
|
@@ -179,6 +159,6 @@ export interface SliceContract {
|
|
|
179
159
|
conflicts?: string[];
|
|
180
160
|
/** Map of previous-version → migration script id. */
|
|
181
161
|
migrationFrom?: Record<string, string>;
|
|
182
|
-
/**
|
|
183
|
-
|
|
162
|
+
/** Portability gate — feeds the forbidden-terms audit. */
|
|
163
|
+
generalization?: SliceGeneralization;
|
|
184
164
|
}
|
package/lib/contract-validate.ts
CHANGED
|
@@ -86,54 +86,45 @@ export function validateConvex(c: SliceContract): void {
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
export function
|
|
90
|
-
if (c.
|
|
91
|
-
if (!c.
|
|
92
|
-
throw new Error(`defineSliceContract(${c.id}): bidir must be an object`);
|
|
93
|
-
}
|
|
94
|
-
const policies = ["auto-pr", "notify", "manual", "frozen"];
|
|
95
|
-
if (!policies.includes(c.bidir.syncPolicy)) {
|
|
96
|
-
throw new Error(
|
|
97
|
-
`defineSliceContract(${c.id}): bidir.syncPolicy "${String(c.bidir.syncPolicy)}" must be one of ${policies.join("|")}`,
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
if (!c.bidir.generalization || typeof c.bidir.generalization !== "object") {
|
|
89
|
+
export function validateGeneralization(c: SliceContract): void {
|
|
90
|
+
if (c.generalization === undefined) return;
|
|
91
|
+
if (!c.generalization || typeof c.generalization !== "object") {
|
|
101
92
|
throw new Error(
|
|
102
|
-
`defineSliceContract(${c.id}):
|
|
93
|
+
`defineSliceContract(${c.id}): generalization must be an object`,
|
|
103
94
|
);
|
|
104
95
|
}
|
|
105
96
|
const levels = ["portable", "needs-adapter", "consumer-locked"];
|
|
106
|
-
if (!levels.includes(c.
|
|
97
|
+
if (!levels.includes(c.generalization.level)) {
|
|
107
98
|
throw new Error(
|
|
108
|
-
`defineSliceContract(${c.id}):
|
|
99
|
+
`defineSliceContract(${c.id}): generalization.level "${String(c.generalization.level)}" must be one of ${levels.join("|")}`,
|
|
109
100
|
);
|
|
110
101
|
}
|
|
111
|
-
const ft = c.
|
|
102
|
+
const ft = c.generalization.forbiddenTerms;
|
|
112
103
|
if (ft !== undefined) {
|
|
113
104
|
if (!Array.isArray(ft)) {
|
|
114
105
|
throw new Error(
|
|
115
|
-
`defineSliceContract(${c.id}):
|
|
106
|
+
`defineSliceContract(${c.id}): generalization.forbiddenTerms must be an array`,
|
|
116
107
|
);
|
|
117
108
|
}
|
|
118
109
|
for (const t of ft) {
|
|
119
110
|
if (typeof t !== "string" || t.length === 0) {
|
|
120
111
|
throw new Error(
|
|
121
|
-
`defineSliceContract(${c.id}):
|
|
112
|
+
`defineSliceContract(${c.id}): generalization.forbiddenTerms entries must be non-empty strings`,
|
|
122
113
|
);
|
|
123
114
|
}
|
|
124
115
|
}
|
|
125
116
|
}
|
|
126
|
-
const rp = c.
|
|
117
|
+
const rp = c.generalization.requiredProps;
|
|
127
118
|
if (rp !== undefined) {
|
|
128
119
|
if (!Array.isArray(rp)) {
|
|
129
120
|
throw new Error(
|
|
130
|
-
`defineSliceContract(${c.id}):
|
|
121
|
+
`defineSliceContract(${c.id}): generalization.requiredProps must be an array`,
|
|
131
122
|
);
|
|
132
123
|
}
|
|
133
124
|
for (const p of rp) {
|
|
134
125
|
if (typeof p !== "string" || p.length === 0) {
|
|
135
126
|
throw new Error(
|
|
136
|
-
`defineSliceContract(${c.id}):
|
|
127
|
+
`defineSliceContract(${c.id}): generalization.requiredProps entries must be non-empty strings`,
|
|
137
128
|
);
|
|
138
129
|
}
|
|
139
130
|
}
|
package/lib/contract.ts
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
validateHeader,
|
|
14
14
|
validateRbac,
|
|
15
15
|
validateConvex,
|
|
16
|
-
|
|
16
|
+
validateGeneralization,
|
|
17
17
|
validateConflicts,
|
|
18
18
|
} from "./contract-validate";
|
|
19
19
|
|
|
@@ -24,10 +24,8 @@ export type {
|
|
|
24
24
|
ConvexNamespace,
|
|
25
25
|
SliceContractRequires,
|
|
26
26
|
SliceContractProvides,
|
|
27
|
-
SliceSyncPolicy,
|
|
28
27
|
GeneralizationLevel,
|
|
29
28
|
SliceGeneralization,
|
|
30
|
-
SliceBidirContract,
|
|
31
29
|
SliceContract,
|
|
32
30
|
} from "./contract-types";
|
|
33
31
|
|
|
@@ -62,7 +60,7 @@ export function defineSliceContract(c: SliceContract): SliceContract {
|
|
|
62
60
|
validateHeader(c);
|
|
63
61
|
validateRbac(c);
|
|
64
62
|
validateConvex(c);
|
|
65
|
-
|
|
63
|
+
validateGeneralization(c);
|
|
66
64
|
validateConflicts(c);
|
|
67
65
|
return c;
|
|
68
66
|
}
|
package/lib/manifest.json
CHANGED
|
@@ -2084,12 +2084,12 @@
|
|
|
2084
2084
|
"slug": "rate-limit",
|
|
2085
2085
|
"title": "Rate Limit",
|
|
2086
2086
|
"category": "infra",
|
|
2087
|
-
"description": "Convex-backed per-key request counter. Atomic check-and-increment via `consume` mutation; expired rows pruned by `_pruneExpired` internalMutation wired to a 5-min cron. Replaces single-replica in-memory Map so multi-replica Next deployments share buckets.
|
|
2087
|
+
"description": "Convex-backed per-key request counter. Atomic check-and-increment via `consume` mutation; expired rows pruned by `_pruneExpired` internalMutation wired to a 5-min cron. Replaces single-replica in-memory Map so multi-replica Next deployments share buckets. Limits live in an in-code POLICY map keyed by namespace prefix (admin-login:<ip>, mcp:<ip>) — never caller-supplied; optional RATE_LIMIT_SERVER_KEY env gates anonymous calls. Lifted 2026-05-16 from rahmanef.com; hardened 2026-06-07.",
|
|
2088
2088
|
"source": "rahmanef.com",
|
|
2089
2089
|
"install": "npx rahman-resources add rate-limit",
|
|
2090
2090
|
"npmPackages": [],
|
|
2091
2091
|
"exampleCode": "",
|
|
2092
|
-
"agentRecipe": "Run `npx rr add rate-limit`. Compose `rateLimitTables` into root convex/schema.ts. Wire `internal.features.rate_limit.mutations._pruneExpired` into convex/crons.ts every 5 min.
|
|
2092
|
+
"agentRecipe": "Run `npx rr add rate-limit`. Compose `rateLimitTables` into root convex/schema.ts. Wire `internal.features.rate_limit.mutations._pruneExpired` into convex/crons.ts every 5 min. Add your namespace to the in-code POLICY map, then call `api.features.rate_limit.mutations.consume({ key, serverKey })` from server-side handlers — keep a fail-open wrapper so a Convex outage doesn't 503 the route. Set RATE_LIMIT_SERVER_KEY on the deployment to block anonymous consume calls.",
|
|
2093
2093
|
"tags": [
|
|
2094
2094
|
"infra",
|
|
2095
2095
|
"rate-limit",
|
|
@@ -2634,6 +2634,26 @@
|
|
|
2634
2634
|
"basics"
|
|
2635
2635
|
]
|
|
2636
2636
|
},
|
|
2637
|
+
{
|
|
2638
|
+
"slug": "loading-states",
|
|
2639
|
+
"title": "Loading States — skeletons + spinners SSOT",
|
|
2640
|
+
"category": "ui",
|
|
2641
|
+
"description": "Configurable LoadingSkeleton composing the shadcn Skeleton primitive: kind presets text / card / list / table / form / page / block with overridable count + columns. Spinner-based LoadingState (inline / block / overlay) covers in-flight work where a skeleton would be wrong. The page kind drops straight into a route loading.tsx (recipe in README).",
|
|
2642
|
+
"source": "rr original",
|
|
2643
|
+
"install": "npx rahman-resources add loading-states",
|
|
2644
|
+
"npmPackages": [],
|
|
2645
|
+
"exampleCode": "",
|
|
2646
|
+
"agentRecipe": "Run `npx rr add loading-states`. Pick the LoadingSkeleton kind that mirrors the streamed content (<LoadingSkeleton kind=\"table\" count={8} />); use kind=\"page\" inside route loading.tsx. For in-flight work (submits, refetches) use <LoadingState variant=\"inline|block|overlay\" /> instead of a skeleton.",
|
|
2647
|
+
"tags": [
|
|
2648
|
+
"loading",
|
|
2649
|
+
"skeleton",
|
|
2650
|
+
"spinner",
|
|
2651
|
+
"suspense",
|
|
2652
|
+
"fallback",
|
|
2653
|
+
"placeholder",
|
|
2654
|
+
"basics"
|
|
2655
|
+
]
|
|
2656
|
+
},
|
|
2637
2657
|
{
|
|
2638
2658
|
"slug": "marketing-chrome",
|
|
2639
2659
|
"title": "Marketing Chrome — Header + Footer",
|
|
@@ -2700,7 +2720,7 @@
|
|
|
2700
2720
|
"title": "Image Editor — layered raster editor",
|
|
2701
2721
|
"category": "os",
|
|
2702
2722
|
"kind": "ui",
|
|
2703
|
-
"version": "2.0.
|
|
2723
|
+
"version": "2.0.2",
|
|
2704
2724
|
"description": "A Photoshop-style raster image editor built on Konva. Layers panel (reorder, opacity, visibility, lock, 16 blend modes), free transform (move/scale/rotate/flip via a Transformer), image + text + shape + paint layers, brush & eraser with size/opacity/hardness, non-destructive adjustments + filters, canvas resize/aspect presets, and LAYER STYLES: stroke, drop shadow, outer glow, clipping mask. One-click BACKGROUND REMOVAL runs fully in-browser via @imgly/background-removal (free, no API key — downloads a small ONNX model on first use). Undo/redo, zoom/pan, shortcuts, PNG/JPG/WebP export. v2 adds an AI FUNCTION-CALLING layer: every editor operation is a named, schema'd command (EDITOR_COMMANDS registry + useEditorCommands binding) driven by an in-editor chat; the streaming bridge is injectable via configureAgentStream(fn) and everything except the chat works without it. A headless server barrel (server.ts) runs commands against documents with no DOM. Image I/O via props (initialImage / onSave).",
|
|
2705
2725
|
"source": "rahmanef63/os-vps",
|
|
2706
2726
|
"slicePath": "frontend/slices/image-editor",
|
|
@@ -2748,7 +2768,7 @@
|
|
|
2748
2768
|
"title": "Reel — video timeline editor",
|
|
2749
2769
|
"category": "os",
|
|
2750
2770
|
"kind": "ui",
|
|
2751
|
-
"version": "1.0.
|
|
2771
|
+
"version": "1.0.2",
|
|
2752
2772
|
"description": "A complete in-browser video editor. Real media clips (image/video/audio) on a layered multi-track timeline — the top row renders frontmost, with ▲▼ reorder and per-track lock/hide/mute. ONE Canvas-2D draw path is shared by the live preview and the realtime MediaRecorder exporter, so what you see is exactly what renders (WebM with real mixed audio: per-clip volume/fades/auto-duck through a streaming audio graph). Per-clip trim/speed (0.25–4×)/reverse, dissolve/wipe/slide transitions via clip overlap, keyframes (opacity/scale/x/y/rotation) with easing + one-click In/Out animation presets, text styling with preset grid, color grading + vignette, filmstrip thumbnails + real waveforms, snapping, split/duplicate. The workspace is config-driven: 6 resizable layout presets (react-resizable-panels v4) incl. quick-import files-pane layouts, plus custom composition size. Drafts auto-save to localStorage. Self-contained: toasts via sonner, the files pane runs on an injectable fs adapter (configureReelFs; in-memory mock by default), and shell hooks (inspector/activity) are inert seams in lib/host.ts.",
|
|
2753
2773
|
"source": "rahmanef63/os-vps",
|
|
2754
2774
|
"slicePath": "frontend/slices/reel-editor",
|
|
@@ -2826,7 +2846,7 @@
|
|
|
2826
2846
|
"title": "Code — overlay syntax editor",
|
|
2827
2847
|
"category": "os",
|
|
2828
2848
|
"kind": "ui",
|
|
2829
|
-
"version": "1.0.
|
|
2849
|
+
"version": "1.0.2",
|
|
2830
2850
|
"description": "A lightweight code editor in the VS-Code spirit without the weight: a transparent textarea layered over a highlighted pre (regex tokenizer for JS/TS/JSON/CSS) gives real editing with live syntax color and a line-number gutter; a tab strip tracks dirty buffers with Cmd/Ctrl+S save; a status bar shows path, Ln/Col, tab size, language and save state. The explorer is a lazy per-directory tree — each folder lists on expand, with inline new-file/new-folder affordances — rendered as a rail on desktop and a Sheet on mobile, and the new-file form is a responsive dialog ⇄ bottom drawer. The filesystem is INJECTED via a small CodeFsAdapter (list/read/write/mkdir): point configureCodeFs at a real API or use the bundled writable in-memory mock (seeded sample tree) so it works with zero backend. Writes are best-effort — a read-only host flags the save but keeps the local buffer. Pairs with file-explorer (onOpenFile → payload) and appshell.",
|
|
2831
2851
|
"source": "rahmanef63/os-vps",
|
|
2832
2852
|
"slicePath": "frontend/slices/code-editor",
|
|
@@ -2957,7 +2977,7 @@
|
|
|
2957
2977
|
"title": "Browser — remote headless-browser chrome",
|
|
2958
2978
|
"category": "os",
|
|
2959
2979
|
"kind": "ui",
|
|
2960
|
-
"version": "1.0.
|
|
2980
|
+
"version": "1.0.2",
|
|
2961
2981
|
"description": "Full browser chrome for a REMOTE headless browser: omnibar with search-or-URL detection, bookmark bar, history view (localStorage-persisted), favicons with globe fallback, busy states, and a screenshot viewport that forwards clicks/typing/keys/scroll into the remote page. The backend is INJECTED via a small BrowserAdapter (state/screenshot/act): point configureBrowser at a real headless-Chromium service (e.g. Playwright behind an authed route — any site renders, no X-Frame-Options problem) or keep the bundled offline canvas demo renderer that fakes the viewport so the whole chrome works with zero backend. Self-contained: shell inspector hooks are inert seams in lib/host.ts.",
|
|
2962
2982
|
"source": "rahmanef63/os-vps",
|
|
2963
2983
|
"slicePath": "frontend/slices/browser",
|
|
@@ -3063,7 +3083,7 @@
|
|
|
3063
3083
|
"title": "AppShell — Desktop + Mobile OS Shell",
|
|
3064
3084
|
"category": "os",
|
|
3065
3085
|
"kind": "full",
|
|
3066
|
-
"version": "1.
|
|
3086
|
+
"version": "1.3.1",
|
|
3067
3087
|
"description": "Generic, brand-free OS-style shell framework. One <AppShell manifest> wrapper provider gives a project a macOS-style window manager (drag/snap/maximize, dock, menu bar, Spotlight) AND an iOS-style mobile surface (home pager, app library, control center, widgets), driven entirely by a manifest: brand, apps, features, surface regions, capabilities, persistence, keymap. Five shell features (search, inspector, notifications, control-center, widgets) are bundled as defineFeature() contributions inside the slice and mount via named <Slot>s. Responsiveness is a single ResponsiveProvider + 4 DRY primitives (AppFrame, MasterDetail, ResponsiveToolbar, TouchList). Imports nothing project-specific — the consumer injects data/auth/AI through manifest.capabilities. Lifted from os-vps (Topside).",
|
|
3068
3088
|
"source": "rahmanef63/os-vps",
|
|
3069
3089
|
"slicePath": "frontend/slices/appshell",
|
|
@@ -4135,8 +4155,8 @@
|
|
|
4135
4155
|
"title": "Rate Limit",
|
|
4136
4156
|
"category": "infra",
|
|
4137
4157
|
"kind": "backend",
|
|
4138
|
-
"version": "0.
|
|
4139
|
-
"description": "Convex-backed per-key request counter. Atomic check-and-increment via `consume` mutation; expired rows pruned by `_pruneExpired` internalMutation wired to a 5-min cron. Replaces single-replica in-memory Map so multi-replica Next deployments share buckets.
|
|
4158
|
+
"version": "0.2.0",
|
|
4159
|
+
"description": "Convex-backed per-key request counter. Atomic check-and-increment via `consume` mutation; expired rows pruned by `_pruneExpired` internalMutation wired to a 5-min cron. Replaces single-replica in-memory Map so multi-replica Next deployments share buckets. Limits live in an in-code POLICY map keyed by namespace prefix (admin-login:<ip>, mcp:<ip>) — never caller-supplied; optional RATE_LIMIT_SERVER_KEY env gates anonymous calls. Lifted 2026-05-16 from rahmanef.com; hardened 2026-06-07.",
|
|
4140
4160
|
"source": "rahmanef.com",
|
|
4141
4161
|
"slicePath": "frontend/slices/rate-limit",
|
|
4142
4162
|
"convexPaths": [
|
|
@@ -4154,7 +4174,7 @@
|
|
|
4154
4174
|
"backend",
|
|
4155
4175
|
"throttle"
|
|
4156
4176
|
],
|
|
4157
|
-
"agentRecipe": "Run `npx rr add rate-limit`. Compose `rateLimitTables` into root convex/schema.ts. Wire `internal.features.rate_limit.mutations._pruneExpired` into convex/crons.ts every 5 min.
|
|
4177
|
+
"agentRecipe": "Run `npx rr add rate-limit`. Compose `rateLimitTables` into root convex/schema.ts. Wire `internal.features.rate_limit.mutations._pruneExpired` into convex/crons.ts every 5 min. Add your namespace to the in-code POLICY map, then call `api.features.rate_limit.mutations.consume({ key, serverKey })` from server-side handlers — keep a fail-open wrapper so a Convex outage doesn't 503 the route. Set RATE_LIMIT_SERVER_KEY on the deployment to block anonymous consume calls."
|
|
4158
4178
|
},
|
|
4159
4179
|
{
|
|
4160
4180
|
"slug": "testimonials",
|
|
@@ -5135,6 +5155,35 @@
|
|
|
5135
5155
|
],
|
|
5136
5156
|
"agentRecipe": "Run `npx rr add empty-states`. Drop <EmptyState kind=\"no-results\" /> into zero-data spots; wrap with <ErrorPage kind=\"404\" /> in app/not-found.tsx and kind=\"500\" in app/error.tsx. Every preset's icon/title/description/action overridable per use."
|
|
5137
5157
|
},
|
|
5158
|
+
{
|
|
5159
|
+
"slug": "loading-states",
|
|
5160
|
+
"title": "Loading States — skeletons + spinners SSOT",
|
|
5161
|
+
"category": "ui",
|
|
5162
|
+
"kind": "ui",
|
|
5163
|
+
"version": "0.1.0",
|
|
5164
|
+
"description": "Configurable LoadingSkeleton composing the shadcn Skeleton primitive: kind presets text / card / list / table / form / page / block with overridable count + columns. Spinner-based LoadingState (inline / block / overlay) covers in-flight work where a skeleton would be wrong. The page kind drops straight into a route loading.tsx (recipe in README).",
|
|
5165
|
+
"source": "rr original",
|
|
5166
|
+
"slicePath": "frontend/slices/loading-states",
|
|
5167
|
+
"convexPaths": [],
|
|
5168
|
+
"npm": [],
|
|
5169
|
+
"shadcn": [
|
|
5170
|
+
"skeleton",
|
|
5171
|
+
"spinner"
|
|
5172
|
+
],
|
|
5173
|
+
"env": [],
|
|
5174
|
+
"peers": [],
|
|
5175
|
+
"providers": [],
|
|
5176
|
+
"tags": [
|
|
5177
|
+
"loading",
|
|
5178
|
+
"skeleton",
|
|
5179
|
+
"spinner",
|
|
5180
|
+
"suspense",
|
|
5181
|
+
"fallback",
|
|
5182
|
+
"placeholder",
|
|
5183
|
+
"basics"
|
|
5184
|
+
],
|
|
5185
|
+
"agentRecipe": "Run `npx rr add loading-states`. Pick the LoadingSkeleton kind that mirrors the streamed content (<LoadingSkeleton kind=\"table\" count={8} />); use kind=\"page\" inside route loading.tsx. For in-flight work (submits, refetches) use <LoadingState variant=\"inline|block|overlay\" /> instead of a skeleton."
|
|
5186
|
+
},
|
|
5138
5187
|
{
|
|
5139
5188
|
"slug": "marketing-chrome",
|
|
5140
5189
|
"title": "Marketing Chrome — Header + Footer",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rahman-resources",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Rahman Resources (rr)
|
|
3
|
+
"version": "1.13.0",
|
|
4
|
+
"description": "Rahman Resources (rr) — shadcn-style installer for vertical slices. `npx resources add <slug>` copies slice into your project's `slices/<slug>/`. You own the files.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Rahman <casadezian@gmail.com>",
|