@akanjs/cli 3.0.0-beta.30 → 3.0.0-beta.32

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.
Files changed (85) hide show
  1. package/.build-stamp +1 -1
  2. package/{CodeAgent-xnfyyz8h.js → CodeAgent-2vmp9qe0.js} +2 -2
  3. package/{CodeTui-h66xb1fh.js → CodeTui-5ed6bg2a.js} +1 -1
  4. package/{agent.command-c4v7kjff.js → agent.command-gn73rhe7.js} +5 -5
  5. package/akanCode.js +2 -2
  6. package/{application.command-yzsswrz6.js → application.command-e968t0hw.js} +14 -9
  7. package/{applicationBuildRunner-qkt6spd5.js → applicationBuildRunner-64taxseg.js} +3 -3
  8. package/buildBatch.proc.js +3 -3
  9. package/{cloud.command-v8h688jc.js → cloud.command-0jm301rr.js} +9 -9
  10. package/cloud.script-nemnzjpv.js +20 -0
  11. package/{code.command-jff3tw7y.js → code.command-18kcm7hq.js} +6 -6
  12. package/{context.command-y5sevk3c.js → context.command-trd9bn18.js} +13 -13
  13. package/context.runner-y5hwh673.js +21 -0
  14. package/{guideline.command-11cndccb.js → guideline.command-bdmdn2de.js} +2 -2
  15. package/guidelines/conventions/conventions.instruction.md +7 -4
  16. package/guidelines/runtimeRule/runtimeRule.instruction.md +29 -6
  17. package/incrementalBuilder.proc.js +2 -2
  18. package/{index-mk3b9rk1.js → index-0wpybxtr.js} +3 -3
  19. package/{index-n3jk2501.js → index-3w94e7n3.js} +27 -4
  20. package/{index-wfwgcnyc.js → index-4npz05nm.js} +4 -4
  21. package/{index-6x9p8dpb.js → index-6he6gvfg.js} +1 -1
  22. package/{index-0ncc0mbk.js → index-93j60em0.js} +3 -3
  23. package/{index-tzc1gtrg.js → index-9z771ptz.js} +2 -2
  24. package/{index-hqd0h1pb.js → index-abxpehf8.js} +7 -7
  25. package/{index-a4s954kq.js → index-by9pxkr0.js} +2 -2
  26. package/{index-5rp0h6na.js → index-drehwpdt.js} +2 -2
  27. package/{index-rtwhw4aq.js → index-f3c78nd6.js} +7 -7
  28. package/{index-kb16nzyt.js → index-gbggbqcp.js} +96 -35
  29. package/{index-tsb0f8ya.js → index-hsmtysz4.js} +2 -2
  30. package/{index-35mmm9ca.js → index-jmrn54tt.js} +1 -1
  31. package/{index-376gpdds.js → index-kcdj1z5v.js} +2 -2
  32. package/{index-hf6f4xnb.js → index-kqabytj7.js} +4 -4
  33. package/{index-se643zvp.js → index-mbxbhay5.js} +1 -1
  34. package/{index-gxamdf18.js → index-mgxpx3f2.js} +2 -2
  35. package/{index-3rh7thcx.js → index-p993efkq.js} +33 -18
  36. package/{index-qcejqgts.js → index-pmhqttv3.js} +5 -5
  37. package/{index-8tv7356t.js → index-tqm7jjcn.js} +1 -1
  38. package/{index-xb7qhxa8.js → index-vpj42xwt.js} +1 -1
  39. package/{index-bxmejydw.js → index-wprnsjms.js} +3 -3
  40. package/{index-hmdq5pqm.js → index-y3mphr1m.js} +1 -1
  41. package/{index-167c77aq.js → index-yg7jhz8x.js} +4 -4
  42. package/index.js +21 -21
  43. package/{library.command-n9kp7yms.js → library.command-9h749a1x.js} +3 -3
  44. package/{localRegistry.command-jx6kxt2x.js → localRegistry.command-2bqqhxyz.js} +8 -8
  45. package/{module.command-q2xjf6cx.js → module.command-nv8nzgft.js} +5 -5
  46. package/{package.command-q5a3nsw5.js → package.command-mq4bggp6.js} +3 -3
  47. package/package.json +2 -2
  48. package/{page.command-ppenrxjn.js → page.command-j639n707.js} +3 -3
  49. package/{primitive.command-dvgg8nem.js → primitive.command-kz6849th.js} +6 -6
  50. package/{quality.command-pz7kg3pc.js → quality.command-4hyabwkb.js} +2 -2
  51. package/{repair.command-a6py9xvz.js → repair.command-a9awpbqr.js} +5 -5
  52. package/{scalar.command-7f3xfpen.js → scalar.command-ma4fpm9m.js} +4 -4
  53. package/{subspace.command-z7nqs508.js → subspace.command-3k2pmrs4.js} +4 -4
  54. package/templates/app/page/_index.tsx +257 -258
  55. package/templates/app/page/_layout.tsx +5 -1
  56. package/templates/app/page/_overrides.tsx +12 -0
  57. package/templates/app/page/styles.css.template +316 -14
  58. package/templates/app/public/apple-touch-icon.png +0 -0
  59. package/templates/app/public/favicon.ico +0 -0
  60. package/templates/app/public/jelly/cloud.webp +0 -0
  61. package/templates/app/public/jelly/comet.webp +0 -0
  62. package/templates/app/public/jelly/moon.webp +0 -0
  63. package/templates/app/public/jelly/planet.webp +0 -0
  64. package/templates/app/public/jelly/rocket.webp +0 -0
  65. package/templates/app/public/jelly/star.webp +0 -0
  66. package/templates/app/public/jelly/sugar.webp +0 -0
  67. package/templates/app/public/logo.png +0 -0
  68. package/templates/app/ui/Recipe/index.ts +9 -0
  69. package/templates/app/ui/Recipe/jellyButton.ts +63 -0
  70. package/templates/appSample/lib/task/Task.Unit.tsx +1 -1
  71. package/templates/appSample/lib/task/Task.Util.tsx +10 -13
  72. package/templates/appSample/lib/task/Task.View.tsx +2 -2
  73. package/templates/appSample/lib/task/Task.Zone.tsx +5 -2
  74. package/templates/appSample/page/task/[taskId]/_index.tsx +6 -6
  75. package/templates/appSample/page/task/[taskId]/edit.tsx +5 -4
  76. package/templates/appSample/page/task/_index.tsx +4 -3
  77. package/templates/appSample/page/task/_layout.tsx +1 -1
  78. package/templates/appSample/page/task/new.tsx +5 -4
  79. package/templates/appSample/ui/GlobalLoading.tsx +2 -1
  80. package/templates/appSample/ui/QuantityControl.tsx +3 -3
  81. package/{tunnel.command-zy4wbcgt.js → tunnel.command-xwr46b66.js} +2 -2
  82. package/{workflow.command-f142t17s.js → workflow.command-t7wqe4h7.js} +10 -10
  83. package/{workspace.command-gpk1149x.js → workspace.command-4derhzrh.js} +24 -21
  84. package/cloud.script-yct1sk4k.js +0 -20
  85. package/context.runner-90hsmacr.js +0 -21
@@ -1,17 +1,319 @@
1
1
  @import "tailwindcss";
2
2
  @import "akanjs/ui/styles.css";
3
3
 
4
- /* The theme system (brand-neutral default palette, @theme inline mapping, dark variant, and runtime
5
- data-theme switching) is fully provided by akanjs/ui/styles.css. This app works from the import alone.
6
-
7
- To use this app's own brand colors, override the tokens like this:
8
-
9
- :root,
10
- [data-theme="dark"] {
11
- --primary: #xxxxxx;
12
- --primary-foreground: #ffffff;
13
- }
14
- [data-theme="light"] {
15
- --primary: #xxxxxx;
16
- }
17
- */
4
+ /* The jelly star palette. akanjs/ui/styles.css owns the theme system (token mapping, dark variant, runtime
5
+ data-theme switching), so a rebrand only overrides the token values below. The star red (--jelly) is a fill
6
+ color; --primary carries white text, so it is one step darker to keep 4.5:1 contrast. */
7
+ :root,
8
+ [data-theme="light"] {
9
+ --background: #f1f1ee;
10
+ --foreground: #0d0d0e;
11
+ --primary: #e02d27;
12
+ --primary-foreground: #ffffff;
13
+ --secondary: #0d0d0e;
14
+ --secondary-foreground: #f6f6f3;
15
+ --accent: #7650d8;
16
+ --accent-foreground: #ffffff;
17
+ --neutral: #26262a;
18
+ --neutral-foreground: #f6f6f3;
19
+ --info: #1f6fe0;
20
+ --info-foreground: #ffffff;
21
+ --success: #3f8f1f;
22
+ --success-foreground: #ffffff;
23
+ --warning: #f2b51c;
24
+ --warning-foreground: #0d0d0e;
25
+ --destructive: #c4161c;
26
+ --destructive-foreground: #ffffff;
27
+ --open: #5aa82a;
28
+ --open-foreground: #0d0d0e;
29
+ --muted: #e6e6e1;
30
+ --muted-foreground: #66666b;
31
+ --card: #fbfbf9;
32
+ --card-foreground: #0d0d0e;
33
+ --popover: #ffffff;
34
+ --popover-foreground: #0d0d0e;
35
+ --border: #dcdcd6;
36
+ --input: #d4d4ce;
37
+ --ring: #e02d27;
38
+ --jelly: #ef3a2d;
39
+ --studio-shadow: color-mix(in oklab, #3a2a20 16%, transparent);
40
+ --sky-top: #dfe5f7;
41
+ --sky-bottom: #f6eadf;
42
+ }
43
+
44
+ [data-theme="dark"] {
45
+ --background: #0f1129;
46
+ --foreground: #f3f1fb;
47
+ --primary: #e02d27;
48
+ --primary-foreground: #ffffff;
49
+ --secondary: #f3f1fb;
50
+ --secondary-foreground: #0f1129;
51
+ --accent: #a585e0;
52
+ --accent-foreground: #0f1129;
53
+ --neutral: #262a4d;
54
+ --neutral-foreground: #f3f1fb;
55
+ --info: #4b8ef0;
56
+ --info-foreground: #0f1129;
57
+ --success: #7cc23a;
58
+ --success-foreground: #0f1129;
59
+ --warning: #f6c437;
60
+ --warning-foreground: #0f1129;
61
+ --destructive: #e5383b;
62
+ --destructive-foreground: #ffffff;
63
+ --open: #7cc23a;
64
+ --open-foreground: #0f1129;
65
+ --muted: #1a1d3b;
66
+ --muted-foreground: #a6a8c9;
67
+ --card: #161935;
68
+ --card-foreground: #f3f1fb;
69
+ --popover: #1b1e40;
70
+ --popover-foreground: #f3f1fb;
71
+ --border: #2b2f57;
72
+ --input: #33376a;
73
+ --ring: #f0392e;
74
+ --jelly: #f2402f;
75
+ --studio-shadow: color-mix(in oklab, #02030f 65%, transparent);
76
+ --sky-top: #25215f;
77
+ --sky-bottom: #2d1749;
78
+ }
79
+
80
+ :root {
81
+ --planet: #1f6fe0;
82
+ --rocket: #82c43c;
83
+ --moon: #f6e27a;
84
+ --cloud: #a585e0;
85
+ --comet: #e3228e;
86
+ }
87
+
88
+ @theme inline {
89
+ --color-jelly: var(--jelly);
90
+ --color-planet: var(--planet);
91
+ --color-rocket: var(--rocket);
92
+ --color-moon: var(--moon);
93
+ --color-cloud: var(--cloud);
94
+ --color-comet: var(--comet);
95
+ --radius-box: 1.5rem;
96
+ --radius-field: 0.875rem;
97
+ }
98
+
99
+ /* A sampled damped spring: overshoots the target by 18%, wobbles twice and settles. */
100
+ @theme {
101
+ --ease-jelly: linear(0, 0.42 6%, 0.86 13%, 1.12 21%, 1.18 26%, 1.13 32%, 1 41%, 0.95 48%, 0.97 56%, 1.01 66%, 1.005 78%, 1);
102
+ }
103
+
104
+ /* html and body both paint a background color, so body's background is not propagated to the canvas: the sky
105
+ sits on body, and isolation keeps the grain layer above body's own background. */
106
+ @layer base {
107
+ body {
108
+ isolation: isolate;
109
+ background-image:
110
+ radial-gradient(120% 60% at 50% -14%, var(--sky-top), transparent 64%),
111
+ radial-gradient(110% 55% at 50% 114%, var(--sky-bottom), transparent 68%);
112
+ background-attachment: fixed;
113
+ }
114
+
115
+ body::before {
116
+ content: "";
117
+ position: fixed;
118
+ inset: 0;
119
+ z-index: -1;
120
+ pointer-events: none;
121
+ opacity: 0.06;
122
+ mix-blend-mode: multiply;
123
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
124
+ }
125
+
126
+ [data-theme="dark"] body::before {
127
+ opacity: 0.1;
128
+ mix-blend-mode: overlay;
129
+ }
130
+
131
+ h1,
132
+ h2 {
133
+ letter-spacing: -0.035em;
134
+ }
135
+
136
+ ::selection {
137
+ background-color: color-mix(in oklab, var(--jelly) 24%, transparent);
138
+ }
139
+ }
140
+
141
+ @keyframes jelly-intro {
142
+ from {
143
+ opacity: 0;
144
+ translate: 0 1.25rem;
145
+ filter: blur(6px);
146
+ }
147
+ }
148
+
149
+ @keyframes jelly-breathe {
150
+ 0%,
151
+ 100% {
152
+ scale: 1 1;
153
+ }
154
+ 50% {
155
+ scale: 1.035 0.965;
156
+ }
157
+ }
158
+
159
+ @keyframes jelly-wobble {
160
+ 0% {
161
+ scale: 1 1;
162
+ }
163
+ 16% {
164
+ scale: 1.16 0.86;
165
+ }
166
+ 32% {
167
+ scale: 0.9 1.1;
168
+ }
169
+ 48% {
170
+ scale: 1.06 0.95;
171
+ }
172
+ 64% {
173
+ scale: 0.97 1.03;
174
+ }
175
+ 82% {
176
+ scale: 1.01 0.99;
177
+ }
178
+ 100% {
179
+ scale: 1 1;
180
+ }
181
+ }
182
+
183
+ @keyframes jelly-float {
184
+ 0%,
185
+ 100% {
186
+ translate: 0 0;
187
+ rotate: calc(var(--float-tilt, 4deg) * -1);
188
+ }
189
+ 50% {
190
+ translate: 0 calc(var(--float-rise, 0.9rem) * -1);
191
+ rotate: var(--float-tilt, 4deg);
192
+ }
193
+ }
194
+
195
+ @utility intro-rise {
196
+ @media (prefers-reduced-motion: no-preference) {
197
+ animation: jelly-intro 1.1s cubic-bezier(0.16, 1, 0.3, 1) both;
198
+ animation-delay: var(--intro-delay, 0ms);
199
+ }
200
+ }
201
+
202
+ @utility jelly-breathe {
203
+ transform-origin: 50% 100%;
204
+ @media (prefers-reduced-motion: no-preference) {
205
+ animation: jelly-breathe 3.2s ease-in-out infinite;
206
+ }
207
+ }
208
+
209
+ @utility jelly-wobble {
210
+ transform-origin: 50% 100%;
211
+ @media (prefers-reduced-motion: no-preference) {
212
+ animation: jelly-wobble 0.9s ease-out both;
213
+ }
214
+ }
215
+
216
+ @utility jelly-float {
217
+ @media (prefers-reduced-motion: no-preference) {
218
+ animation: jelly-float var(--float-duration, 6s) ease-in-out var(--float-delay, 0s) infinite;
219
+ }
220
+ }
221
+
222
+ @utility squish {
223
+ transition:
224
+ scale 700ms var(--ease-jelly),
225
+ translate 700ms var(--ease-jelly);
226
+ &:hover {
227
+ scale: 1.04 0.97;
228
+ }
229
+ &:active {
230
+ scale: 1.08 0.9;
231
+ transition-duration: 90ms;
232
+ }
233
+ }
234
+
235
+ @utility tint-* {
236
+ --tint: --value(--color-*);
237
+ }
238
+
239
+ /* A gummy surface. public/jelly/sugar.webp is a high-pass of the logo star's sugar coating (128 gray = no change),
240
+ laid on in hard-light so every tint gets the same sugar grain. */
241
+ @utility jelly {
242
+ --jelly-tint: var(--tint, var(--primary));
243
+ --jelly-deep: color-mix(in oklab, var(--jelly-tint) 70%, black);
244
+ --jelly-haze: color-mix(in oklab, var(--jelly-tint) 55%, white);
245
+ background-color: var(--jelly-tint);
246
+ background-image:
247
+ url("/jelly/sugar.webp"),
248
+ radial-gradient(120% 95% at 26% -8%, color-mix(in oklab, white 26%, transparent), transparent 58%),
249
+ radial-gradient(110% 90% at 60% 125%, color-mix(in oklab, var(--jelly-deep) 30%, transparent), transparent 60%);
250
+ background-size:
251
+ 100px 100px,
252
+ 100% 100%,
253
+ 100% 100%;
254
+ background-repeat: repeat, no-repeat, no-repeat;
255
+ background-blend-mode: hard-light, normal, normal;
256
+ box-shadow:
257
+ inset 0 0 0 1px color-mix(in oklab, var(--jelly-haze) 40%, transparent),
258
+ inset 0 0 8px 1px color-mix(in oklab, white 20%, transparent),
259
+ inset 0 -3px 6px color-mix(in oklab, var(--jelly-deep) 28%, transparent),
260
+ 0 6px 10px -6px color-mix(in oklab, var(--jelly-deep) 65%, transparent),
261
+ 0 1px 2px color-mix(in oklab, black 12%, transparent);
262
+ }
263
+
264
+ @utility jelly-glass {
265
+ --jelly-tint: var(--tint, var(--card));
266
+ background-color: color-mix(in oklab, var(--card) 95%, var(--jelly-tint));
267
+ background-image:
268
+ radial-gradient(120% 70% at 20% -20%, color-mix(in oklab, white 55%, transparent), transparent 60%),
269
+ radial-gradient(90% 60% at 100% 120%, color-mix(in oklab, var(--jelly-tint) 9%, transparent), transparent 70%);
270
+ box-shadow:
271
+ inset 0 1px 0 color-mix(in oklab, white 70%, transparent),
272
+ inset 0 -1px 0 color-mix(in oklab, var(--jelly-tint) 14%, transparent),
273
+ 0 22px 44px -28px var(--studio-shadow),
274
+ 0 2px 6px -3px var(--studio-shadow);
275
+ @variant dark {
276
+ background-color: color-mix(in oklab, var(--card) 92%, var(--jelly-tint));
277
+ background-image:
278
+ radial-gradient(120% 70% at 20% -20%, color-mix(in oklab, white 7%, transparent), transparent 60%),
279
+ radial-gradient(90% 60% at 100% 120%, color-mix(in oklab, var(--jelly-tint) 14%, transparent), transparent 70%);
280
+ box-shadow:
281
+ inset 0 1px 0 color-mix(in oklab, white 11%, transparent),
282
+ inset 0 -1px 0 color-mix(in oklab, var(--jelly-tint) 18%, transparent),
283
+ 0 22px 44px -28px var(--studio-shadow),
284
+ 0 2px 6px -3px var(--studio-shadow);
285
+ }
286
+ }
287
+
288
+ /* The same sugar, clipped to the glyphs of plain text-primary text. Clipping replaces the element's background, so
289
+ elements with a surface of their own (bg-*, jelly, glass) and currentColor drawings are skipped, and icons inside
290
+ get their color back. */
291
+ .text-primary:not(svg, [class*="bg-"], [class*="jelly"], [class*="glass"], [class*="-current"]) {
292
+ background-color: var(--primary);
293
+ background-image:
294
+ url("/jelly/sugar.webp"),
295
+ linear-gradient(
296
+ to bottom,
297
+ color-mix(in oklab, black 8%, var(--primary)),
298
+ color-mix(in oklab, white 14%, var(--primary)) 22%,
299
+ var(--primary) 60%,
300
+ color-mix(in oklab, black 8%, var(--primary))
301
+ );
302
+ background-size:
303
+ 100px 100px,
304
+ 100% 1lh;
305
+ background-origin: content-box;
306
+ background-blend-mode: hard-light, normal;
307
+ -webkit-background-clip: text;
308
+ background-clip: text;
309
+ color: transparent;
310
+ text-decoration-color: var(--primary);
311
+ & :is(svg, [class*="-current"]) {
312
+ color: var(--primary);
313
+ }
314
+ /* A heading's tight leading pushes descenders out of the box, where a text-clipped background is never painted. */
315
+ :is(h1, h2, h3, h4, h5, h6) & {
316
+ padding-block: 0.15em;
317
+ margin-block: -0.15em;
318
+ }
319
+ }
Binary file
Binary file
@@ -0,0 +1,9 @@
1
+ import type { AppInfo, LibInfo } from "akanjs";
2
+
3
+ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { appName: string }) {
4
+ return {
5
+ filename: "index.ts",
6
+ content: `export { type JellyButtonVariants, jellyButtonRecipe } from "./jellyButton";
7
+ `,
8
+ };
9
+ }
@@ -0,0 +1,63 @@
1
+ import type { AppInfo, LibInfo } from "akanjs";
2
+
3
+ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { appName: string }) {
4
+ return {
5
+ filename: "jellyButton.ts",
6
+ content: `import { recipe, tv } from "akanjs/ui";
7
+
8
+ /** Sugared gummy button with the same variants as akanjs's \`buttonRecipe\`; \`page/_overrides.tsx\` binds it to every akanjs/ui Button. ghost/outline/link stay surfaceless. */
9
+ export const jellyButtonRecipe = recipe(
10
+ tv({
11
+ base: "squish inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-full font-bold focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
12
+ variants: {
13
+ variant: {
14
+ default: "text-foreground",
15
+ primary: "tint-primary text-primary-foreground",
16
+ secondary: "tint-secondary text-secondary-foreground",
17
+ accent: "tint-accent text-accent-foreground",
18
+ neutral: "tint-neutral text-neutral-foreground",
19
+ outline: "border border-foreground/12 bg-background/70 text-foreground hover:bg-foreground/5",
20
+ ghost: "text-foreground/75 hover:bg-foreground/6 hover:text-foreground",
21
+ destructive: "tint-destructive text-destructive-foreground",
22
+ success: "tint-success text-success-foreground",
23
+ warning: "tint-warning text-warning-foreground",
24
+ info: "tint-info text-info-foreground",
25
+ link: "text-primary underline-offset-4 hover:underline",
26
+ },
27
+ size: {
28
+ xs: "h-6 px-2.5 text-xs",
29
+ sm: "h-8 px-3.5 text-sm",
30
+ md: "h-10 px-5 text-sm",
31
+ lg: "h-12 px-7 text-base",
32
+ icon: "h-10 w-10 px-0",
33
+ },
34
+ shape: {
35
+ default: "",
36
+ square: "aspect-square px-0",
37
+ circle: "aspect-square px-0",
38
+ },
39
+ outline: { true: "border bg-transparent", false: "" },
40
+ },
41
+ compoundVariants: [
42
+ { variant: "default", outline: false, class: "jelly-glass" },
43
+ {
44
+ variant: ["primary", "secondary", "accent", "neutral", "destructive", "success", "warning", "info"],
45
+ outline: false,
46
+ class: "jelly",
47
+ },
48
+ { variant: "primary", outline: true, class: "border-primary text-primary hover:bg-primary/8" },
49
+ { variant: "secondary", outline: true, class: "border-secondary text-secondary hover:bg-secondary/8" },
50
+ { variant: "accent", outline: true, class: "border-accent text-accent hover:bg-accent/8" },
51
+ { variant: "neutral", outline: true, class: "border-neutral text-neutral hover:bg-neutral/8" },
52
+ { variant: "destructive", outline: true, class: "border-destructive text-destructive hover:bg-destructive/8" },
53
+ { variant: "success", outline: true, class: "border-success text-success hover:bg-success/8" },
54
+ { variant: "warning", outline: true, class: "border-warning text-warning hover:bg-warning/8" },
55
+ { variant: "info", outline: true, class: "border-info text-info hover:bg-info/8" },
56
+ ],
57
+ defaultVariants: { variant: "primary", size: "md", shape: "default" },
58
+ }),
59
+ );
60
+ export type JellyButtonVariants = NonNullable<Parameters<typeof jellyButtonRecipe>[0]>;
61
+ `,
62
+ };
63
+ }
@@ -30,7 +30,7 @@ export const Card = ({ task, className, href }: CardProps) => {
30
30
  return (
31
31
  <Layout.Unit
32
32
  className={cn(
33
- "rounded-lg border border-foreground/10 bg-background p-4 transition-shadow hover:shadow-md",
33
+ "jelly-glass block rounded-box p-5 transition-transform duration-300 ease-jelly hover:-translate-y-0.5",
34
34
  className,
35
35
  )}
36
36
  href={href}
@@ -5,7 +5,8 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { a
5
5
  filename: "Task.Util.tsx",
6
6
  content: `"use client";
7
7
  import { fetch, st, usePage } from "@apps/${dict.appName}/client";
8
- import { buttonRecipe, Dropdown, Model } from "akanjs/ui";
8
+ import { jellyButtonRecipe } from "@apps/${dict.appName}/ui";
9
+ import { Dropdown, Model } from "akanjs/ui";
9
10
 
10
11
  // ===== Task.Util.tsx =====
11
12
  // Convention: lib/<module>/ — PascalCase .tsx, Util suffix = action buttons/utility components.
@@ -19,10 +20,7 @@ interface StartProps {
19
20
  }
20
21
 
21
22
  export const Start = ({ taskId }: StartProps) => (
22
- <button
23
- className={buttonRecipe({ size: "xs" }, "border-primary/20 bg-primary/10 text-primary hover:bg-primary hover:text-primary-foreground")}
24
- onClick={() => st.do.startTask(taskId)}
25
- >
23
+ <button className={jellyButtonRecipe({ variant: "primary", size: "xs" })} onClick={() => st.do.startTask(taskId)}>
26
24
  {usePage().l("task.taskStart")}
27
25
  </button>
28
26
  );
@@ -32,10 +30,7 @@ interface CompleteProps {
32
30
  }
33
31
 
34
32
  export const Complete = ({ taskId }: CompleteProps) => (
35
- <button
36
- className={buttonRecipe({ size: "xs" }, "border-success/20 bg-success/10 text-success hover:bg-success hover:text-success-foreground")}
37
- onClick={() => st.do.completeTask(taskId)}
38
- >
33
+ <button className={jellyButtonRecipe({ variant: "success", size: "xs" })} onClick={() => st.do.completeTask(taskId)}>
39
34
  {usePage().l("task.taskComplete")}
40
35
  </button>
41
36
  );
@@ -46,7 +41,9 @@ interface RemoveProps {
46
41
 
47
42
  export const Remove = ({ taskId }: RemoveProps) => (
48
43
  <Model.Remove modelId={taskId} slice={fetch.slice.task}>
49
- <button className={buttonRecipe({ variant: "ghost", size: "xs" }, "text-destructive")}>{usePage().l("task.taskRemove")}</button>
44
+ <button className={jellyButtonRecipe({ variant: "ghost", size: "xs" }, "text-destructive")}>
45
+ {usePage().l("task.taskRemove")}
46
+ </button>
50
47
  </Model.Remove>
51
48
  );
52
49
 
@@ -59,9 +56,9 @@ export const Toolbox = ({ taskId, status }: ToolboxProps) => {
59
56
  const { l } = usePage();
60
57
  return (
61
58
  <Dropdown
62
- buttonClassName={buttonRecipe({ variant: "ghost", size: "xs" })}
59
+ buttonClassName={jellyButtonRecipe({ variant: "ghost", size: "xs" })}
63
60
  value={<span>···</span>}
64
- dropdownClassName="w-40 rounded-box border border-foreground/10 bg-background p-2"
61
+ dropdownClassName="jelly-glass w-40 rounded-box p-2"
65
62
  content={
66
63
  <>
67
64
  {status === "todo" && (
@@ -96,7 +93,7 @@ export const Toolbox = ({ taskId, status }: ToolboxProps) => {
96
93
  // New Task Button: Model.NewWrapper — triggers new Task creation form
97
94
  // export const NewTask = () => (
98
95
  // <Model.NewWrapper partial={{}}>
99
- // <button className={buttonRecipe({ variant: "primary", size: "sm" })}>+ New Task</button>
96
+ // <button className={jellyButtonRecipe({ variant: "primary", size: "sm" })}>+ New Task</button>
100
97
  // </Model.NewWrapper>
101
98
  // );
102
99
  `,
@@ -29,12 +29,12 @@ export const General = ({ className, task }: GeneralProps) => {
29
29
  return (
30
30
  <div className={cn("flex w-full flex-col gap-4", className)}>
31
31
  <div>
32
- <h1 className="font-bold text-2xl text-foreground">{task.title}</h1>
32
+ <h1 className="font-black text-4xl text-foreground">{task.title}</h1>
33
33
  <div className={cn("mt-1 font-medium text-sm", statusColor)}>{l(\`taskStatus.\${task.status}\`)}</div>
34
34
  </div>
35
35
 
36
36
  {task.content && (
37
- <div className="rounded-lg border border-foreground/10 bg-background p-4">
37
+ <div className="jelly-glass rounded-box p-5">
38
38
  <p className="whitespace-pre-wrap text-foreground/80 text-sm">{task.content}</p>
39
39
  </div>
40
40
  )}
@@ -5,8 +5,9 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { a
5
5
  filename: "Task.Zone.tsx",
6
6
  content: `"use client";
7
7
  import { type cnst, Task, usePage } from "@apps/${dict.appName}/client";
8
+ import { jellyButtonRecipe } from "@apps/${dict.appName}/ui";
8
9
  import type { ClientInit, ClientView } from "akanjs/fetch";
9
- import { Link, Load, buttonRecipe } from "akanjs/ui";
10
+ import { Link, Load } from "akanjs/ui";
10
11
 
11
12
  // ===== Task.Zone.tsx =====
12
13
  // Convention: lib/<module>/ — PascalCase .tsx, Zone suffix = composition layer between pages and UI.
@@ -30,7 +31,9 @@ export const Card = ({ className, init }: CardProps) => {
30
31
  <div className="flex flex-col items-center gap-4 py-16 text-center">
31
32
  <div className="text-foreground/40 text-lg">{l("task.taskNoTasks")}</div>
32
33
  <Link href="/task/new">
33
- <button className={buttonRecipe({ variant: "primary", size: "sm" })}>{l("task.taskCreateFirst")}</button>
34
+ <button className={jellyButtonRecipe({ variant: "primary", size: "sm" })}>
35
+ {l("task.taskCreateFirst")}
36
+ </button>
34
37
  </Link>
35
38
  </div>
36
39
  )}
@@ -3,11 +3,11 @@ import type { AppInfo, LibInfo } from "akanjs";
3
3
  export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { appName: string }) {
4
4
  return {
5
5
  filename: "_index.tsx",
6
- content: `import { fetch, usePage } from "@apps/${dict.appName}/client";
7
- import { Task } from "@apps/${dict.appName}/lib/task";
6
+ content: `import { fetch, Task, usePage } from "@apps/${dict.appName}/client";
7
+ import { jellyButtonRecipe } from "@apps/${dict.appName}/ui";
8
8
  import { ID } from "akanjs/base";
9
9
  import { page } from "akanjs/client";
10
- import { Link, buttonRecipe } from "akanjs/ui";
10
+ import { Link } from "akanjs/ui";
11
11
 
12
12
  export default page()
13
13
  .param("taskId", ID)
@@ -17,10 +17,10 @@ export default page()
17
17
  return (
18
18
  <main className="mx-auto max-w-4xl px-6 py-8">
19
19
  <div className="mb-6 flex items-center justify-between">
20
- <a href="/task" className={buttonRecipe({ variant: "ghost", size: "sm" })}>
20
+ <Link href="/task" className={jellyButtonRecipe({ variant: "ghost", size: "sm" })}>
21
21
  {l("task.taskBackToTasks")}
22
- </a>
23
- <Link href={\`/task/\${taskId}/edit\`} className={buttonRecipe({ variant: "primary", size: "sm" })}>
22
+ </Link>
23
+ <Link href={\`/task/\${taskId}/edit\`} className={jellyButtonRecipe({ variant: "primary", size: "sm" })}>
24
24
  {l("task.taskEdit")}
25
25
  </Link>
26
26
  </div>
@@ -4,9 +4,10 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { a
4
4
  return {
5
5
  filename: "edit.tsx",
6
6
  content: `import { fetch, Task } from "@apps/${dict.appName}/client";
7
+ import { jellyButtonRecipe } from "@apps/${dict.appName}/ui";
7
8
  import { ID } from "akanjs/base";
8
9
  import { page } from "akanjs/client";
9
- import { Load, Link, buttonRecipe } from "akanjs/ui";
10
+ import { Link, Load } from "akanjs/ui";
10
11
 
11
12
  export default page()
12
13
  .param("taskId", ID)
@@ -16,15 +17,15 @@ export default page()
16
17
  return (
17
18
  <main className="mx-auto max-w-2xl px-6 py-8">
18
19
  <div className="mb-6">
19
- <Link href={\`/task/\${taskId}\`} className={buttonRecipe({ variant: "ghost", size: "sm" })}>
20
+ <Link href={\`/task/\${taskId}\`} className={jellyButtonRecipe({ variant: "ghost", size: "sm" })}>
20
21
  ← Back to Task
21
22
  </Link>
22
23
  </div>
23
24
  <div className="mb-6">
24
- <h1 className="font-extrabold text-3xl text-foreground">Edit Task</h1>
25
+ <h1 className="font-black text-4xl text-foreground">Edit Task</h1>
25
26
  <p className="mt-1 text-foreground/60 text-sm">Update the task details</p>
26
27
  </div>
27
- <div className="rounded-xl border border-foreground/10 bg-background p-6 shadow-sm">
28
+ <div className="jelly-glass rounded-box p-6">
28
29
  <Load.Edit slice={fetch.slice.taskInPublic} edit={taskEdit} type="form" onSubmit={\`/task/\${taskId}\`}>
29
30
  <Task.Template.General />
30
31
  </Load.Edit>
@@ -4,8 +4,9 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { a
4
4
  return {
5
5
  filename: "_index.tsx",
6
6
  content: `import { fetch, Task, usePage } from "@apps/${dict.appName}/client";
7
+ import { jellyButtonRecipe } from "@apps/${dict.appName}/ui";
7
8
  import { page } from "akanjs/client";
8
- import { Link, buttonRecipe } from "akanjs/ui";
9
+ import { Link } from "akanjs/ui";
9
10
 
10
11
  export default page().render(async () => {
11
12
  const { l } = usePage();
@@ -14,10 +15,10 @@ export default page().render(async () => {
14
15
  <main className="mx-auto max-w-4xl px-6 py-8">
15
16
  <div className="mb-6 flex items-center justify-between">
16
17
  <div>
17
- <h1 className="font-extrabold text-3xl text-foreground">{l("task.modelName")}</h1>
18
+ <h1 className="font-black text-4xl text-foreground">{l("task.modelName")}</h1>
18
19
  <p className="mt-1 text-foreground/60 text-sm">{l("task.modelDesc")}</p>
19
20
  </div>
20
- <Link href="/task/new" className={buttonRecipe({ variant: "primary", size: "sm" })}>
21
+ <Link href="/task/new" className={jellyButtonRecipe({ variant: "primary", size: "sm" })}>
21
22
  {l("task.taskNew")}
22
23
  </Link>
23
24
  </div>
@@ -6,7 +6,7 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { a
6
6
  content: `import { layout } from "akanjs/client";
7
7
 
8
8
  export default layout().render(({ children }) => {
9
- return <div className="min-h-screen bg-muted">{children}</div>;
9
+ return <div className="min-h-screen">{children}</div>;
10
10
  });
11
11
  `,
12
12
  };