@stacksjs/defaults 0.70.257 → 0.70.259
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/ide/cursor/rules/auto-imports.mdc +1 -1
- package/ide/cursor/rules/code-style.mdc +1 -1
- package/ide/cursor/rules/error-handling-validation.mdc +2 -2
- package/ide/cursor/rules/key-conventions.mdc +1 -1
- package/ide/cursor/rules/project-structure.mdc +1 -1
- package/ide/cursor/rules/syntax-formatting.mdc +1 -1
- package/ide/cursor/rules/testing.mdc +1 -1
- package/ide/vscode/package.json +1 -1
- package/package.json +14 -3
- package/project/buddy +5 -1
- package/resources/components/Container.stx +1 -1
- package/resources/components/Docs/Demo/ComboboxDemo.stx +1 -1
- package/resources/components/Docs/Demo/DocsPlayground.stx +2 -2
- package/resources/components/Docs/Demo/DropdownDemo.stx +3 -3
- package/resources/components/Docs/Demo/ListboxDemo.stx +2 -2
- package/resources/components/Docs/Demo/PopoverDemo.stx +1 -1
- package/resources/components/Docs/Demo/RadioGroupDemo.stx +3 -3
- package/resources/components/Docs/Demo/StepperDemo.stx +6 -6
- package/resources/components/Docs/Demo/SwitchDemo.stx +2 -2
- package/resources/components/Forum/ForumBreadcrumb.stx +2 -2
- package/resources/components/Forum/ForumCategoryItem.stx +1 -1
- package/resources/components/Forum/ForumLayout.stx +4 -4
- package/resources/components/Forum/ForumPagination.stx +3 -3
- package/resources/components/Forum/ForumReplyForm.stx +1 -1
- package/resources/components/Marketing/Feature.stx +3 -3
- package/resources/components/Marketing/Fields.stx +3 -3
- package/resources/components/Marketing/Layout.stx +1 -1
- package/resources/components/Marketing/Plan.stx +5 -5
- package/resources/components/Marketing/Pricing.stx +5 -5
- package/resources/components/Marketing/PrimaryFeatures.stx +4 -4
- package/resources/components/Marketing/PrimaryFeatures2.stx +4 -4
- package/resources/components/Marketing/SecondaryFeatures.stx +3 -3
- package/views/[...all].stx +0 -1
- package/views/auth/signup.stx +62 -27
- package/views/dashboard/layouts/default.stx +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Error Handling and Validation specifics
|
|
3
|
-
globs:
|
|
3
|
+
globs:
|
|
4
4
|
---
|
|
5
5
|
## Error Handling and Validation
|
|
6
6
|
|
|
@@ -8,4 +8,4 @@ globs:
|
|
|
8
8
|
- Use early returns and guard clauses
|
|
9
9
|
- Implement proper error logging and user-friendly messages
|
|
10
10
|
- Use error boundaries for unexpected errors
|
|
11
|
-
- Use `@stacksjs/error-handling`, which is similar to neverthrow, to handle errors and responses
|
|
11
|
+
- Use `@stacksjs/error-handling`, which is similar to neverthrow, to handle errors and responses
|
package/ide/vscode/package.json
CHANGED
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/defaults",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.70.
|
|
5
|
+
"version": "0.70.259",
|
|
6
6
|
"description": "The complete managed Stacks application scaffold, including runtime defaults, AI guidance, editor metadata, and npm-backed project support files.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"license": "MIT",
|
|
@@ -29,8 +29,19 @@
|
|
|
29
29
|
"project"
|
|
30
30
|
],
|
|
31
31
|
"exports": {
|
|
32
|
-
"./*":
|
|
33
|
-
|
|
32
|
+
"./*": {
|
|
33
|
+
"types": "./dist/*.d.ts",
|
|
34
|
+
"bun": "./dist/*.js",
|
|
35
|
+
"import": "./dist/*.js",
|
|
36
|
+
"default": "./dist/*.js"
|
|
37
|
+
},
|
|
38
|
+
"./package.json": "./package.json",
|
|
39
|
+
"./*.js": {
|
|
40
|
+
"types": "./dist/*.d.ts",
|
|
41
|
+
"bun": "./dist/*.js",
|
|
42
|
+
"import": "./dist/*.js",
|
|
43
|
+
"default": "./dist/*.js"
|
|
44
|
+
}
|
|
34
45
|
},
|
|
35
46
|
"dependencies": {
|
|
36
47
|
"@iconify-json/f7": "^1.2.2",
|
package/project/buddy
CHANGED
|
@@ -53,7 +53,11 @@ run_with_timeout() {
|
|
|
53
53
|
cmd_pid=$!
|
|
54
54
|
|
|
55
55
|
(
|
|
56
|
-
|
|
56
|
+
# Do not let the timer inherit a caller's stdout/stderr pipe. If the
|
|
57
|
+
# watchdog shell is terminated after the command finishes, its sleep may
|
|
58
|
+
# briefly remain orphaned; inherited deploy pipes would otherwise stay
|
|
59
|
+
# open until the full timeout elapsed.
|
|
60
|
+
sleep "${timeout_seconds}" </dev/null >/dev/null 2>&1
|
|
57
61
|
|
|
58
62
|
if kill -0 "${cmd_pid}" 2>/dev/null; then
|
|
59
63
|
echo "Command timed out after ${timeout_seconds}s: $*" >&2
|
|
@@ -56,7 +56,7 @@ function filterItems(items, query) {
|
|
|
56
56
|
data-item-id="{{ item.id }}"
|
|
57
57
|
data-item-name="{{ item.name }}"
|
|
58
58
|
>
|
|
59
|
-
<span class="block truncate selected selected.id
|
|
59
|
+
<span class="block truncate {{ selected && selected.id == item.id ? 'font-medium' : 'font-normal' }}">
|
|
60
60
|
{{ item.name }}
|
|
61
61
|
</span>
|
|
62
62
|
@if(selected && selected.id == item.id)
|
|
@@ -10,13 +10,13 @@ const { isPreviewMode = true } = defineProps<DocsPlaygroundProps>()
|
|
|
10
10
|
<div class="relative bg-gray-100 dark:bg-neutral-800 rounded-lg">
|
|
11
11
|
<div class="flex items-stretch justify-end px-3 py-1 md:m-1 bg-gray-100 dark:bg-neutral-800 rounded-t-[10px] md:rounded-lg">
|
|
12
12
|
<button
|
|
13
|
-
class="
|
|
13
|
+
class="pointer-events-auto mr-1 flex rounded-md px-3 py-2 text-xs font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/50 {{ isPreviewMode ? 'bg-white text-blue-500' : 'text-gray-600 dark:text-neutral-300 hover:bg-gray-300 dark:hover:bg-neutral-600' }}"
|
|
14
14
|
data-action="toggle-preview"
|
|
15
15
|
>
|
|
16
16
|
Preview
|
|
17
17
|
</button>
|
|
18
18
|
<button
|
|
19
|
-
class="flex px-3 py-2
|
|
19
|
+
class="pointer-events-auto flex rounded-md px-3 py-2 text-xs font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/50 {{ !isPreviewMode ? 'bg-white text-blue-500' : 'text-gray-600 dark:text-neutral-300 hover:bg-gray-300 dark:hover:bg-neutral-600' }}"
|
|
20
20
|
data-action="toggle-code"
|
|
21
21
|
>
|
|
22
22
|
Code
|
|
@@ -45,7 +45,7 @@ function getMenuSections(items) {
|
|
|
45
45
|
data-action="toggle-dropdown"
|
|
46
46
|
>
|
|
47
47
|
Options
|
|
48
|
-
<div class="
|
|
48
|
+
<div class="i-hugeicons-chevron-down-20-solid w-5 h-5 transition-transform duration-200 {{ isOpen ? 'rotate-180' : '' }}"></div>
|
|
49
49
|
</button>
|
|
50
50
|
|
|
51
51
|
@if(isOpen)
|
|
@@ -55,11 +55,11 @@ function getMenuSections(items) {
|
|
|
55
55
|
@foreach(section as item)
|
|
56
56
|
<button
|
|
57
57
|
type="button"
|
|
58
|
-
class="
|
|
58
|
+
class="group relative flex w-full items-center rounded-md px-2 py-2 text-sm outline-none transition-colors {{ item.variant == 'danger' ? 'text-red-600 hover:bg-red-50' : 'text-gray-700 hover:bg-gray-50' }}"
|
|
59
59
|
data-action="menu-item"
|
|
60
60
|
data-item-label="{{ item.label }}"
|
|
61
61
|
>
|
|
62
|
-
<div class="mr-2 h-5 w-5
|
|
62
|
+
<div class="{{ item.icon }} mr-2 h-5 w-5 {{ item.variant == 'danger' ? 'text-red-600' : 'text-gray-500 group-hover:text-gray-700' }}"></div>
|
|
63
63
|
{{ item.label }}
|
|
64
64
|
</button>
|
|
65
65
|
@endforeach
|
|
@@ -32,12 +32,12 @@ const { people = [, selectedPerson = people[0], isOpen = false } = defineProps<L
|
|
|
32
32
|
<div class="absolute overflow-auto mt-1 py-1 max-h-60 w-full text-base sm:text-sm bg-white outline-none ring-1 ring-black/5 rounded-md focus:outline-none shadow-lg">
|
|
33
33
|
@foreach(people as person)
|
|
34
34
|
<div
|
|
35
|
-
class="relative
|
|
35
|
+
class="list-none relative cursor-default select-none py-2 pl-10 pr-4 hover:bg-amber-100 hover:text-amber-900 {{ selectedPerson.id == person.id ? 'bg-amber-100 text-amber-900' : 'text-gray-900' }}"
|
|
36
36
|
data-action="select-person"
|
|
37
37
|
data-person-id="{{ person.id }}"
|
|
38
38
|
data-person-name="{{ person.name }}"
|
|
39
39
|
>
|
|
40
|
-
<span class="block truncate selectedPerson.id
|
|
40
|
+
<span class="block truncate {{ selectedPerson.id == person.id ? 'font-medium' : 'font-normal' }}">
|
|
41
41
|
{{ person.name }}
|
|
42
42
|
</span>
|
|
43
43
|
@if(selectedPerson.id == person.id)
|
|
@@ -34,7 +34,7 @@ const { solutions = [, isOpen = false } = defineProps<PopoverDemoProps>()
|
|
|
34
34
|
<div class="items-center mx-auto my-12 space-y-4 h-[150px] w-3/5">
|
|
35
35
|
<div class="relative">
|
|
36
36
|
<button
|
|
37
|
-
class="inline-flex
|
|
37
|
+
class="group inline-flex items-center justify-center gap-2 px-4 py-2.5 text-sm font-medium rounded-lg shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition-all duration-200 {{ isOpen ? 'text-white' : 'text-white/90' }}"
|
|
38
38
|
data-action="toggle-popover"
|
|
39
39
|
>
|
|
40
40
|
<span>Example</span>
|
|
@@ -16,17 +16,17 @@ const { plans = [, selected = plans[0] } = defineProps<RadioGroupDemoProps>()
|
|
|
16
16
|
<div class="space-y-2">
|
|
17
17
|
@foreach(plans as plan)
|
|
18
18
|
<div
|
|
19
|
-
class="flex
|
|
19
|
+
class="relative flex cursor-pointer rounded-lg px-5 py-4 shadow-md focus:outline-none {{ selected.name == plan.name ? 'bg-sky-900/75 text-white ring-2 ring-white/60 ring-offset-2 ring-offset-sky-300' : 'bg-white' }}"
|
|
20
20
|
data-action="select-plan"
|
|
21
21
|
data-plan-name="{{ plan.name }}"
|
|
22
22
|
>
|
|
23
23
|
<div class="flex items-center justify-between w-full">
|
|
24
24
|
<div class="flex items-center">
|
|
25
25
|
<div class="text-sm">
|
|
26
|
-
<p class="font-medium selected.name
|
|
26
|
+
<p class="font-medium {{ selected.name == plan.name ? 'text-white' : 'text-gray-900' }}">
|
|
27
27
|
{{ plan.name }}
|
|
28
28
|
</p>
|
|
29
|
-
<span class="inline selected.name
|
|
29
|
+
<span class="inline {{ selected.name == plan.name ? 'text-sky-100' : 'text-gray-500' }}">
|
|
30
30
|
<span>{{ plan.ram }}/{{ plan.cpus }}</span>
|
|
31
31
|
<span aria-hidden="true"> · </span>
|
|
32
32
|
<span>{{ plan.disk }}</span>
|
|
@@ -30,19 +30,19 @@ function getStepContent(step) {
|
|
|
30
30
|
<!-- Stepper indicator -->
|
|
31
31
|
<div class="flex items-center justify-between mb-12">
|
|
32
32
|
@foreach(steps as step)
|
|
33
|
-
<div class="flex items-center
|
|
33
|
+
<div class="flex items-center {{ loop.last ? '' : 'flex-1' }}">
|
|
34
34
|
<div class="flex flex-col items-center">
|
|
35
|
-
<div class="flex items-center justify-center
|
|
35
|
+
<div class="flex items-center justify-center w-8 h-8 rounded-full {{ loop.index <= currentStep ? 'bg-blue-100 text-blue-600' : 'bg-gray-100 text-gray-500' }} {{ loop.index == currentStep ? 'ring-2 ring-blue-600 ring-offset-2' : '' }}">
|
|
36
36
|
@if(loop.index < currentStep)
|
|
37
37
|
<div class="h-5 w-5 i-heroicons-check"></div>
|
|
38
38
|
@else
|
|
39
39
|
{{ loop.index + 1 }}
|
|
40
40
|
@endif
|
|
41
41
|
</div>
|
|
42
|
-
<span class="mt-2
|
|
42
|
+
<span class="mt-2 text-sm {{ loop.index == currentStep ? 'text-blue-600 font-medium' : 'text-gray-500' }}">{{ step.title }}</span>
|
|
43
43
|
</div>
|
|
44
44
|
@if(!loop.last)
|
|
45
|
-
<div class="flex-1
|
|
45
|
+
<div class="flex-1 h-0.5 mx-4 {{ loop.index < currentStep ? 'bg-blue-500' : 'bg-gray-200' }}"></div>
|
|
46
46
|
@endif
|
|
47
47
|
</div>
|
|
48
48
|
@endforeach
|
|
@@ -108,14 +108,14 @@ function getStepContent(step) {
|
|
|
108
108
|
|
|
109
109
|
<div class="flex gap-4 justify-center">
|
|
110
110
|
<button
|
|
111
|
-
class="px-4 py-2 font-medium text-gray-700
|
|
111
|
+
class="rounded-md bg-gray-50 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 {{ currentStep == 0 ? 'opacity-50 cursor-not-allowed' : '' }}"
|
|
112
112
|
data-action="previous-step"
|
|
113
113
|
{{ currentStep == 0 ? 'disabled' : '' }}
|
|
114
114
|
>
|
|
115
115
|
Previous
|
|
116
116
|
</button>
|
|
117
117
|
<button
|
|
118
|
-
class="px-4 py-2 font-medium text-
|
|
118
|
+
class="rounded-md bg-blue-500 px-4 py-2 text-sm font-medium text-white hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 {{ currentStep == steps.length - 1 ? 'opacity-50 cursor-not-allowed' : '' }}"
|
|
119
119
|
data-action="next-step"
|
|
120
120
|
{{ currentStep == steps.length - 1 ? 'disabled' : '' }}
|
|
121
121
|
>
|
|
@@ -9,13 +9,13 @@ const { enabled = false } = defineProps<SwitchDemoProps>()
|
|
|
9
9
|
<DocsPlayground>
|
|
10
10
|
<div class="flex gap-2 items-center justify-center my-3">
|
|
11
11
|
<button
|
|
12
|
-
class="
|
|
12
|
+
class="relative h-[38px] w-[74px] inline-flex shrink-0 cursor-pointer border-2 border-transparent rounded-full transition-colors duration-200 ease-in-out focus:outline-none focus-visible:ring-2 focus-visible:ring-white/75 {{ enabled ? 'bg-blue-500' : 'bg-gray-400' }}"
|
|
13
13
|
data-action="toggle-switch"
|
|
14
14
|
>
|
|
15
15
|
<span class="sr-only">Use setting</span>
|
|
16
16
|
<span
|
|
17
17
|
aria-hidden="true"
|
|
18
|
-
class="inline-block h-[34px] w-[34px] bg-white ring-0
|
|
18
|
+
class="pointer-events-none inline-block h-[34px] w-[34px] transform rounded-full bg-white shadow-lg ring-0 transition duration-200 ease-in-out {{ enabled ? 'translate-x-9' : 'translate-x-0' }}"
|
|
19
19
|
></span>
|
|
20
20
|
</button>
|
|
21
21
|
</div>
|
|
@@ -20,13 +20,13 @@ const { items = [] } = defineProps<ForumBreadcrumbProps>()
|
|
|
20
20
|
@if(item.link)
|
|
21
21
|
<a
|
|
22
22
|
href="{{ item.link }}"
|
|
23
|
-
class="font-medium text-gray-700
|
|
23
|
+
class="text-sm font-medium text-gray-700 hover:text-blue-600 {{ loop.index > 0 ? 'ml-1' : '' }}"
|
|
24
24
|
>
|
|
25
25
|
{{ item.label }}
|
|
26
26
|
</a>
|
|
27
27
|
@else
|
|
28
28
|
<span
|
|
29
|
-
class="font-medium text-gray-500
|
|
29
|
+
class="text-sm font-medium text-gray-500 {{ loop.index > 0 ? 'ml-1' : '' }}"
|
|
30
30
|
>
|
|
31
31
|
{{ item.label }}
|
|
32
32
|
</span>
|
|
@@ -13,7 +13,7 @@ const { title = '', description = '', link = '', topicCount = 0, postCount = 0,
|
|
|
13
13
|
</script>
|
|
14
14
|
<div class="flex items-start p-4 hover:bg-gray-50">
|
|
15
15
|
<div class="flex-shrink-0 mr-4">
|
|
16
|
-
<div class="
|
|
16
|
+
<div class="{{ icon }} w-10 h-10 bg-gray-100 rounded-full flex items-center justify-center text-gray-500"></div>
|
|
17
17
|
</div>
|
|
18
18
|
|
|
19
19
|
<div class="flex-grow min-w-0">
|
|
@@ -19,10 +19,10 @@ function isActiveRoute(currentRoute, targetRoute) {
|
|
|
19
19
|
</div>
|
|
20
20
|
</div>
|
|
21
21
|
<nav class="flex gap-1 text-sm">
|
|
22
|
-
<a href="/forum" class="px-3 py-2
|
|
23
|
-
<a href="/forum?filter=popular" class="px-3 py-2
|
|
24
|
-
<a href="/forum?filter=recent" class="px-3 py-2
|
|
25
|
-
<a href="/forum?filter=solved" class="px-3 py-2
|
|
22
|
+
<a href="/forum" class="px-3 py-2 {{ isActiveRoute(route, 'forum') ? 'bg-gray-100 rounded-md font-medium' : 'hover:bg-gray-100 rounded-md' }}">All Discussions</a>
|
|
23
|
+
<a href="/forum?filter=popular" class="px-3 py-2 {{ isActiveRoute(route, 'forum-popular') ? 'bg-gray-100 rounded-md font-medium' : 'hover:bg-gray-100 rounded-md' }}">Popular</a>
|
|
24
|
+
<a href="/forum?filter=recent" class="px-3 py-2 {{ isActiveRoute(route, 'forum-recent') ? 'bg-gray-100 rounded-md font-medium' : 'hover:bg-gray-100 rounded-md' }}">Recent</a>
|
|
25
|
+
<a href="/forum?filter=solved" class="px-3 py-2 {{ isActiveRoute(route, 'forum-solved') ? 'bg-gray-100 rounded-md font-medium' : 'hover:bg-gray-100 rounded-md' }}">Solved</a>
|
|
26
26
|
</nav>
|
|
27
27
|
</header>
|
|
28
28
|
|
|
@@ -11,7 +11,7 @@ const { currentPage = 1, totalPages = 1, pages = [] } = defineProps<ForumPaginat
|
|
|
11
11
|
<nav class="flex gap-1 items-center">
|
|
12
12
|
<a
|
|
13
13
|
href="#"
|
|
14
|
-
class="flex items-center justify-center
|
|
14
|
+
class="w-10 h-10 flex items-center justify-center rounded-md border hover:bg-gray-100 {{ currentPage == 1 ? 'opacity-50 cursor-not-allowed' : '' }}"
|
|
15
15
|
data-action="prev-page"
|
|
16
16
|
>
|
|
17
17
|
<span class="i-hugeicons-arrow-left"></span>
|
|
@@ -20,7 +20,7 @@ const { currentPage = 1, totalPages = 1, pages = [] } = defineProps<ForumPaginat
|
|
|
20
20
|
@foreach(pages as page)
|
|
21
21
|
<a
|
|
22
22
|
href="#"
|
|
23
|
-
class="flex items-center justify-center
|
|
23
|
+
class="w-10 h-10 flex items-center justify-center rounded-md {{ currentPage == page ? 'bg-primary text-white' : 'border hover:bg-gray-100' }}"
|
|
24
24
|
data-action="go-to-page"
|
|
25
25
|
data-page="{{ page }}"
|
|
26
26
|
>
|
|
@@ -30,7 +30,7 @@ const { currentPage = 1, totalPages = 1, pages = [] } = defineProps<ForumPaginat
|
|
|
30
30
|
|
|
31
31
|
<a
|
|
32
32
|
href="#"
|
|
33
|
-
class="flex items-center justify-center
|
|
33
|
+
class="w-10 h-10 flex items-center justify-center rounded-md border hover:bg-gray-100 {{ currentPage == totalPages ? 'opacity-50 cursor-not-allowed' : '' }}"
|
|
34
34
|
data-action="next-page"
|
|
35
35
|
>
|
|
36
36
|
<span class="i-hugeicons-arrow-right"></span>
|
|
@@ -64,7 +64,7 @@ function canSubmit(content) {
|
|
|
64
64
|
Preview
|
|
65
65
|
</button>
|
|
66
66
|
<button
|
|
67
|
-
class="px-4 py-2 text-white
|
|
67
|
+
class="px-4 py-2 bg-primary text-white rounded-md hover:opacity-90 {{ canSubmit(replyContent) ? '' : 'opacity-50 cursor-not-allowed' }}"
|
|
68
68
|
data-action="submit-reply"
|
|
69
69
|
{{ canSubmit(replyContent) ? '' : 'disabled' }}
|
|
70
70
|
>
|
|
@@ -14,13 +14,13 @@ name: '',
|
|
|
14
14
|
icon: ''
|
|
15
15
|
}
|
|
16
16
|
</script>
|
|
17
|
-
<div class="
|
|
18
|
-
<div class="w-9 rounded-lg
|
|
17
|
+
<div class="{{ className }} {{ !isActive ? 'opacity-75 hover:opacity-100' : '' }}">
|
|
18
|
+
<div class="w-9 rounded-lg {{ isActive ? 'bg-blue-600' : 'bg-slate-500' }}">
|
|
19
19
|
<svg aria-hidden="true" class="h-9 w-9" fill="none">
|
|
20
20
|
<!-- feature.icon -->
|
|
21
21
|
</svg>
|
|
22
22
|
</div>
|
|
23
|
-
<h3 class="mt-6 font-medium
|
|
23
|
+
<h3 class="mt-6 text-sm font-medium {{ isActive ? 'text-blue-600' : 'text-slate-600' }}">
|
|
24
24
|
{{ feature.name }}
|
|
25
25
|
</h3>
|
|
26
26
|
<p class="mt-2 font-display text-slate-900 text-xl">
|
|
@@ -12,16 +12,16 @@ const { type = 'text', label = '', className = '', id = '', name = '', placehold
|
|
|
12
12
|
|
|
13
13
|
const formClasses = 'block w-full appearance-none rounded-md border border-gray-200 bg-gray-50 px-3 py-2 text-gray-900 placeholder-gray-400 focus:border-blue-500 focus:bg-white focus:outline-none focus:ring-blue-500 sm:text-sm'
|
|
14
14
|
</script>
|
|
15
|
-
<div class="{{ }}
|
|
15
|
+
<div class="{{ className }}">
|
|
16
16
|
@if(label)
|
|
17
17
|
<label for="{{ id }}" class="block mb-3 font-medium text-gray-700 text-sm">
|
|
18
18
|
{{ label }}
|
|
19
19
|
</label>
|
|
20
20
|
@endif
|
|
21
21
|
@if(type == 'text' || type == 'email' || type == 'password')
|
|
22
|
-
<input id="{{ id }}" type="{{ type }}" name="{{ name }}" placeholder="{{ placeholder }}" class="{{ }}
|
|
22
|
+
<input id="{{ id }}" type="{{ type }}" name="{{ name }}" placeholder="{{ placeholder }}" class="{{ formClasses }}">
|
|
23
23
|
@else
|
|
24
|
-
<select id="{{ id }}" name="{{ name }}" class="
|
|
24
|
+
<select id="{{ id }}" name="{{ name }}" class="{{ formClasses }} pr-8">
|
|
25
25
|
<slot />
|
|
26
26
|
</select>
|
|
27
27
|
@endif
|
|
@@ -18,7 +18,7 @@ subsets: ['latin'],
|
|
|
18
18
|
</script>
|
|
19
19
|
<html
|
|
20
20
|
lang="en"
|
|
21
|
-
class="h-full
|
|
21
|
+
class="h-full scroll-smooth bg-white antialiased {{ inter.variable }} {{ lexend.variable }}"
|
|
22
22
|
>
|
|
23
23
|
<body class="flex flex-col h-full">
|
|
24
24
|
<slot />
|
|
@@ -11,12 +11,12 @@ interface PlanProps {
|
|
|
11
11
|
const { name = '', price = '', description = '', href = '', features = [], featured = false } = defineProps<PlanProps>()
|
|
12
12
|
</script>
|
|
13
13
|
<section
|
|
14
|
-
class="flex
|
|
14
|
+
class="flex flex-col rounded-3xl px-6 sm:px-8 {{ featured ? 'order-first bg-blue-600 py-8 lg:order-none' : 'lg:py-8' }}"
|
|
15
15
|
>
|
|
16
16
|
<h3 class="mt-5 font-display text-lg text-white">
|
|
17
17
|
{{ name }}
|
|
18
18
|
</h3>
|
|
19
|
-
<p class="mt-2 text-base
|
|
19
|
+
<p class="mt-2 text-base {{ featured ? 'text-white' : 'text-slate-400' }}">
|
|
20
20
|
{{ description }}
|
|
21
21
|
</p>
|
|
22
22
|
<p class="order-first font-display font-light text-5xl text-white tracking-tight">
|
|
@@ -24,18 +24,18 @@ const { name = '', price = '', description = '', href = '', features = [], featu
|
|
|
24
24
|
</p>
|
|
25
25
|
<ul
|
|
26
26
|
role="list"
|
|
27
|
-
class="
|
|
27
|
+
class="order-last mt-10 flex flex-col gap-y-3 text-sm {{ featured ? 'text-white' : 'text-slate-200' }}"
|
|
28
28
|
>
|
|
29
29
|
@foreach(features as feature)
|
|
30
30
|
<li class="flex">
|
|
31
|
-
<span class="h-5 w-5
|
|
31
|
+
<span class="i-hugeicons-checkmark-circle-02 h-5 w-5 {{ featured ? 'text-white' : 'text-slate-400' }}"></span>
|
|
32
32
|
<span class="ml-4">{{ feature }}</span>
|
|
33
33
|
</li>
|
|
34
34
|
@endforeach
|
|
35
35
|
</ul>
|
|
36
36
|
<a
|
|
37
37
|
href="{{ href }}"
|
|
38
|
-
class="inline-flex items-center justify-center
|
|
38
|
+
class="mt-8 inline-flex items-center justify-center rounded-full py-2 px-4 text-sm font-semibold focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 {{ featured ? 'bg-white text-slate-900 hover:bg-blue-50 active:bg-blue-200 active:text-slate-600 focus-visible:outline-white' : 'ring-1 ring-slate-700 text-white hover:ring-slate-500 active:ring-slate-700 active:text-slate-400 focus-visible:outline-white' }}"
|
|
39
39
|
aria-label="Get started with the {{ name }} plan for {{ price }}"
|
|
40
40
|
>
|
|
41
41
|
Get started
|
|
@@ -84,12 +84,12 @@ const { plans = [ } = defineProps<PricingProps>()
|
|
|
84
84
|
<div class="grid gap-y-10 grid-cols-1 lg:grid-cols-3 xl:gap-x-8 -mx-4 mt-16 sm:mx-auto lg:-mx-8 xl:mx-0 max-w-2xl lg:max-w-none">
|
|
85
85
|
@foreach(plans as plan)
|
|
86
86
|
<section
|
|
87
|
-
class="flex
|
|
87
|
+
class="flex flex-col rounded-3xl px-6 sm:px-8 {{ plan.featured ? 'order-first bg-blue-600 py-8 lg:order-none' : 'lg:py-8' }}"
|
|
88
88
|
>
|
|
89
89
|
<h3 class="mt-5 font-display text-lg text-white">
|
|
90
90
|
{{ plan.name }}
|
|
91
91
|
</h3>
|
|
92
|
-
<p class="mt-2 text-base
|
|
92
|
+
<p class="mt-2 text-base {{ plan.featured ? 'text-white' : 'text-slate-400' }}">
|
|
93
93
|
{{ plan.description }}
|
|
94
94
|
</p>
|
|
95
95
|
<p class="order-first font-display font-light text-5xl text-white tracking-tight">
|
|
@@ -97,18 +97,18 @@ const { plans = [ } = defineProps<PricingProps>()
|
|
|
97
97
|
</p>
|
|
98
98
|
<ul
|
|
99
99
|
role="list"
|
|
100
|
-
class="
|
|
100
|
+
class="order-last mt-10 flex flex-col gap-y-3 text-sm {{ plan.featured ? 'text-white' : 'text-slate-200' }}"
|
|
101
101
|
>
|
|
102
102
|
@foreach(plan.features as feature)
|
|
103
103
|
<li class="flex">
|
|
104
|
-
<span class="h-5 w-5
|
|
104
|
+
<span class="i-hugeicons-checkmark-circle-02 h-5 w-5 {{ plan.featured ? 'text-white' : 'text-slate-400' }}"></span>
|
|
105
105
|
<span class="ml-4">{{ feature }}</span>
|
|
106
106
|
</li>
|
|
107
107
|
@endforeach
|
|
108
108
|
</ul>
|
|
109
109
|
<a
|
|
110
110
|
href="{{ plan.href }}"
|
|
111
|
-
class="inline-flex items-center justify-center
|
|
111
|
+
class="mt-8 inline-flex items-center justify-center rounded-full py-2 px-4 text-sm font-semibold focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 {{ plan.featured ? 'bg-white text-slate-900 hover:bg-blue-50' : 'ring-1 ring-slate-700 text-white hover:ring-slate-500' }}"
|
|
112
112
|
aria-label="Get started with the {{ plan.name }} plan for {{ plan.price }}"
|
|
113
113
|
>
|
|
114
114
|
Get started
|
|
@@ -44,14 +44,14 @@ const { features = [, selectedIndex = 0 } = defineProps<PrimaryFeaturesProps>()
|
|
|
44
44
|
<div class="flex overflow-x-auto sm:overflow-visible lg:col-span-5 -mx-4 mt-16 pb-4 sm:mx-0 sm:pb-0">
|
|
45
45
|
<div class="flex lg:block relative z-10 gap-x-4 lg:gap-x-0 lg:gap-y-1 px-4 sm:mx-auto sm:px-0 lg:mx-0 whitespace-nowrap lg:whitespace-normal">
|
|
46
46
|
@foreach(features as feature)
|
|
47
|
-
<div class="relative px-4 py-1 lg:
|
|
47
|
+
<div class="group relative rounded-full px-4 py-1 lg:rounded-l-xl lg:rounded-r-none lg:p-6 {{ loop.index == selectedIndex ? 'bg-white lg:bg-white/10 lg:ring-1 lg:ring-inset lg:ring-white/10' : 'hover:bg-white/10 lg:hover:bg-white/5' }}">
|
|
48
48
|
<h3>
|
|
49
|
-
<button class="font-display text-lg
|
|
49
|
+
<button class="font-display text-lg {{ loop.index == selectedIndex ? 'text-blue-600 lg:text-white' : 'text-blue-100 hover:text-white lg:text-white' }}" data-action="select-tab" data-index="{{ loop.index }}">
|
|
50
50
|
<span class="absolute inset-0 rounded-full lg:rounded-l-xl lg:rounded-r-none"></span>
|
|
51
51
|
{{ feature.title }}
|
|
52
52
|
</button>
|
|
53
53
|
</h3>
|
|
54
|
-
<p class="
|
|
54
|
+
<p class="mt-2 hidden text-sm lg:block {{ loop.index == selectedIndex ? 'text-white' : 'text-blue-100 group-hover:text-white' }}">
|
|
55
55
|
{{ feature.description }}
|
|
56
56
|
</p>
|
|
57
57
|
</div>
|
|
@@ -62,7 +62,7 @@ const { features = [, selectedIndex = 0 } = defineProps<PrimaryFeaturesProps>()
|
|
|
62
62
|
<!-- Tab Panels -->
|
|
63
63
|
<div class="lg:col-span-7">
|
|
64
64
|
@foreach(features as feature)
|
|
65
|
-
<div class="
|
|
65
|
+
<div class="{{ loop.index != selectedIndex ? 'hidden' : '' }}">
|
|
66
66
|
<div class="lg:hidden relative sm:px-6">
|
|
67
67
|
<div class="absolute bottom-[-4.25rem] top-[-6.5rem] sm:inset-x-0 bg-white/10 ring-1 ring-inset ring-white/10 sm:rounded-t-xl -inset-x-4"></div>
|
|
68
68
|
<p class="relative mx-auto max-w-2xl text-base text-white sm:text-center">
|
|
@@ -58,15 +58,15 @@ const { backgroundImage = '/images/screenshots/background-features.jpg', screens
|
|
|
58
58
|
<div class="flex overflow-x-auto sm:overflow-visible lg:col-span-5 -mx-4 pb-4 sm:mx-0 sm:pb-0">
|
|
59
59
|
<div class="flex lg:block relative z-10 gap-x-4 lg:gap-x-0 lg:gap-y-1 px-4 sm:mx-auto sm:px-0 lg:mx-0 whitespace-nowrap lg:whitespace-normal">
|
|
60
60
|
@foreach(features as feature)
|
|
61
|
-
<div class="relative px-4 py-1 lg:
|
|
61
|
+
<div class="group relative rounded-full px-4 py-1 lg:rounded-l-xl lg:rounded-r-none lg:p-6 {{ loop.index == selectedIndex ? 'bg-white lg:bg-white/10 lg:ring-1 lg:ring-inset lg:ring-white/10' : 'hover:bg-white/10 lg:hover:bg-white/5' }}">
|
|
62
62
|
<h3>
|
|
63
|
-
<button class="font-display text-lg
|
|
63
|
+
<button class="font-display text-lg {{ loop.index == selectedIndex ? 'text-blue-600 lg:text-white' : 'text-blue-100 hover:text-white lg:text-white' }}" data-action="select-tab" data-index="{{ loop.index }}">
|
|
64
64
|
<span class="absolute inset-0 rounded-full lg:rounded-l-xl lg:rounded-r-none"></span>
|
|
65
65
|
{{ feature.title }}
|
|
66
66
|
</button>
|
|
67
67
|
</h3>
|
|
68
68
|
@if(tabOrientation == 'vertical')
|
|
69
|
-
<p class="
|
|
69
|
+
<p class="mt-2 hidden text-sm lg:block {{ loop.index == selectedIndex ? 'text-white' : 'text-blue-100 group-hover:text-white' }}">
|
|
70
70
|
{{ feature.description }}
|
|
71
71
|
</p>
|
|
72
72
|
@endif
|
|
@@ -77,7 +77,7 @@ const { backgroundImage = '/images/screenshots/background-features.jpg', screens
|
|
|
77
77
|
|
|
78
78
|
<div class="lg:col-span-7">
|
|
79
79
|
@foreach(features as feature)
|
|
80
|
-
<div class="
|
|
80
|
+
<div class="{{ loop.index != selectedIndex ? 'hidden' : '' }}">
|
|
81
81
|
<div class="lg:hidden relative sm:px-6">
|
|
82
82
|
<div class="absolute bottom-[-4.25rem] top-[-6.5rem] sm:inset-x-0 bg-white/10 ring-1 ring-inset ring-white/10 sm:rounded-t-xl -inset-x-4"></div>
|
|
83
83
|
<p class="relative mx-auto max-w-2xl text-base text-white sm:text-center">
|
|
@@ -45,12 +45,12 @@ const { features = [, selectedIndex = 0 } = defineProps<SecondaryFeaturesProps>(
|
|
|
45
45
|
<div class="grid gap-x-8 grid-cols-3 pt-12">
|
|
46
46
|
@foreach(features as feature)
|
|
47
47
|
<div class="relative">
|
|
48
|
-
<div class="w-9 rounded-lg selectedIndex
|
|
48
|
+
<div class="w-9 rounded-lg {{ loop.index == selectedIndex ? 'bg-blue-600' : 'bg-slate-500' }}">
|
|
49
49
|
<svg aria-hidden="true" class="h-9 w-9" fill="none">
|
|
50
50
|
{{ feature.icon }}
|
|
51
51
|
</svg>
|
|
52
52
|
</div>
|
|
53
|
-
<h3 class="mt-6 font-medium
|
|
53
|
+
<h3 class="mt-6 text-sm font-medium {{ loop.index == selectedIndex ? 'text-blue-600' : 'text-slate-600' }}">
|
|
54
54
|
<button data-action="select-feature" data-index="{{ loop.index }}">{{ feature.name }}</button>
|
|
55
55
|
</h3>
|
|
56
56
|
<p class="mt-2 font-display text-slate-900 text-xl">
|
|
@@ -66,7 +66,7 @@ const { features = [, selectedIndex = 0 } = defineProps<SecondaryFeaturesProps>(
|
|
|
66
66
|
<div class="overflow-hidden relative mt-20 px-14 py-16 xl:px-16 bg-slate-200 rounded-4xl">
|
|
67
67
|
<div class="flex -mx-5">
|
|
68
68
|
@foreach(features as feature)
|
|
69
|
-
<div class="px-5 duration-500 ease-in-out
|
|
69
|
+
<div class="px-5 transition duration-500 ease-in-out {{ loop.index != selectedIndex ? 'opacity-60' : '' }}" style="transform: translateX(-{{ selectedIndex * 100 }}%)">
|
|
70
70
|
<div class="overflow-hidden w-[52.75rem] bg-white ring-1 ring-slate-500/10 rounded-xl shadow-lg shadow-slate-900/5">
|
|
71
71
|
<img class="w-full" src="{{ feature.image }}" alt="" sizes="52.75rem">
|
|
72
72
|
</div>
|
package/views/[...all].stx
CHANGED
package/views/auth/signup.stx
CHANGED
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
<script server>
|
|
2
|
+
/**
|
|
3
|
+
* The providers whose buttons are worth rendering.
|
|
4
|
+
*
|
|
5
|
+
* `configuredSocialProviders()` knows each provider's own required config,
|
|
6
|
+
* so a provider missing credentials never reaches a visitor as a button
|
|
7
|
+
* that fails at the redirect. Adding credentials to `config/services.ts`
|
|
8
|
+
* is the whole of what turns one on.
|
|
9
|
+
*
|
|
10
|
+
* Icons come from hugeicons, the collection Stacks defaults to. Swap the
|
|
11
|
+
* map if your app standardises on another one.
|
|
12
|
+
*/
|
|
13
|
+
import { configuredSocialProviders } from '@stacksjs/socials'
|
|
14
|
+
|
|
15
|
+
const PROVIDER_ICONS = {
|
|
16
|
+
apple: 'i-hugeicons-apple',
|
|
17
|
+
facebook: 'i-hugeicons-facebook-01',
|
|
18
|
+
github: 'i-hugeicons-github',
|
|
19
|
+
google: 'i-hugeicons-google',
|
|
20
|
+
twitter: 'i-hugeicons-new-twitter',
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const socialSignIn = configuredSocialProviders().map(provider => ({
|
|
24
|
+
label: provider.label,
|
|
25
|
+
href: `/auth/${provider.name}/redirect`,
|
|
26
|
+
icon: PROVIDER_ICONS[provider.name] ?? 'i-hugeicons-user-circle',
|
|
27
|
+
}))
|
|
28
|
+
</script>
|
|
29
|
+
|
|
1
30
|
<div class="flex flex-col justify-center py-12 sm:px-6 lg:px-8 min-h-full">
|
|
2
31
|
<div class="sm:mx-auto sm:max-w-md sm:w-full">
|
|
3
32
|
<Logo class="mx-auto h-11 w-auto" />
|
|
@@ -46,40 +75,46 @@
|
|
|
46
75
|
</div>
|
|
47
76
|
</form>
|
|
48
77
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
<div class="flex absolute inset-0 items-center" aria-hidden="true">
|
|
52
|
-
<div class="w-full border-gray-200 border-t"></div>
|
|
53
|
-
</div>
|
|
54
|
-
<div class="flex relative justify-center font-medium leading-6 text-sm">
|
|
55
|
-
<span class="px-6 text-gray-900 bg-white">Or continue with</span>
|
|
56
|
-
</div>
|
|
57
|
-
</div>
|
|
78
|
+
{{--
|
|
79
|
+
Social sign-in, driven by which providers are actually configured.
|
|
58
80
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
81
|
+
This block used to be two hardcoded buttons pointing at `href="#"`,
|
|
82
|
+
so a scaffolded app shipped a Google and a GitHub button that both
|
|
83
|
+
did nothing, and offered them whether or not either provider had
|
|
84
|
+
credentials. `configuredSocialProviders()` answers that per
|
|
85
|
+
provider on each provider's own terms (Apple has no clientSecret
|
|
86
|
+
and is validated on its signing key instead), so what renders here
|
|
87
|
+
is exactly the set that will complete a sign-in.
|
|
88
|
+
--}}
|
|
89
|
+
@if (socialSignIn.length)
|
|
90
|
+
<div>
|
|
91
|
+
<div class="relative mt-10">
|
|
92
|
+
<div class="flex absolute inset-0 items-center" aria-hidden="true">
|
|
93
|
+
<div class="w-full border-gray-200 border-t"></div>
|
|
94
|
+
</div>
|
|
95
|
+
<div class="flex relative justify-center font-medium leading-6 text-sm">
|
|
96
|
+
<span class="px-6 text-gray-900 bg-white">Or continue with</span>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
69
99
|
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
100
|
+
<div class="grid gap-4 mt-6 {{ socialSignIn.length > 1 ? 'grid-cols-2' : 'grid-cols-1' }}">
|
|
101
|
+
@foreach (socialSignIn as provider)
|
|
102
|
+
<a
|
|
103
|
+
href="{{ provider.href }}"
|
|
104
|
+
class="flex gap-3 items-center justify-center px-3 py-2 w-full font-semibold text-gray-900 text-sm bg-white hover:bg-gray-50 ring-1 ring-gray-300 ring-inset rounded-md focus-visible:ring-transparent shadow-sm"
|
|
105
|
+
>
|
|
106
|
+
<i class="h-5 w-5 {{ provider.icon }}" aria-hidden="true"></i>
|
|
107
|
+
<span class="font-semibold leading-6 text-sm">{{ provider.label }}</span>
|
|
108
|
+
</a>
|
|
109
|
+
@endforeach
|
|
110
|
+
</div>
|
|
76
111
|
</div>
|
|
77
|
-
|
|
112
|
+
@endif
|
|
78
113
|
</div>
|
|
79
114
|
|
|
80
115
|
<p class="mt-10 text-center text-gray-500 text-sm">
|
|
81
116
|
Already have an account?
|
|
82
|
-
<a href="
|
|
117
|
+
<a href="/login" class="font-semibold leading-6 text-blue-600 hover:text-blue-500">Sign in</a>
|
|
83
118
|
</p>
|
|
84
119
|
</div>
|
|
85
120
|
</div>
|
|
@@ -209,7 +209,7 @@ html[data-appearance="arc"] body {
|
|
|
209
209
|
|
|
210
210
|
html[data-appearance="arc"] [data-stx-content] {
|
|
211
211
|
/* Only three margins. `margin-left` is deliberately left to the base rule.
|
|
212
|
-
|
|
212
|
+
|
|
213
213
|
It is the one margin that transitions (0.3s, for the sidebar collapse),
|
|
214
214
|
and overriding a transitioned longhand with a `calc()` over two custom
|
|
215
215
|
properties wedges the interpolation in WebKit: switching appearance leaves
|
|
@@ -217,7 +217,7 @@ html[data-appearance="arc"] [data-stx-content] {
|
|
|
217
217
|
means the property never changes when the appearance does, so there is
|
|
218
218
|
nothing to wedge — and the collapse animation keeps working identically in
|
|
219
219
|
both styles.
|
|
220
|
-
|
|
220
|
+
|
|
221
221
|
The panel strip down the card's left side is the sidebar's own trailing
|
|
222
222
|
padding, which is where it comes from in Arc too. */
|
|
223
223
|
margin-top: var(--dash-arc-gap);
|