@stacksjs/defaults 0.70.160 → 0.70.161

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.
@@ -270,7 +270,7 @@ export default new Action({
270
270
  },
271
271
  {
272
272
  signature: 'buddy build:components',
273
- description: 'Builds Vue component library & Web Component library',
273
+ description: 'Builds the STX component library and Web Component library',
274
274
  notes: '',
275
275
  synopsis: 'buddy build:components',
276
276
  aliases: [],
@@ -607,15 +607,6 @@ export default new Action({
607
607
  arguments: [],
608
608
  options: [],
609
609
  },
610
- {
611
- signature: 'buddy example:vue',
612
- description: 'Runs the Vue example',
613
- notes: '',
614
- synopsis: 'buddy example:vue',
615
- aliases: [],
616
- arguments: [],
617
- options: [],
618
- },
619
610
  {
620
611
  signature: 'buddy example:web-components',
621
612
  description: 'Runs the Web Component example',
package/app/Models/Tag.ts CHANGED
@@ -32,7 +32,7 @@ export default defineModel({
32
32
  },
33
33
  },
34
34
  factory: faker => faker.helpers.arrayElement([
35
- 'javascript', 'typescript', 'vue', 'react', 'nodejs',
35
+ 'javascript', 'typescript', 'stx', 'react', 'nodejs',
36
36
  'database', 'performance', 'security', 'api', 'testing',
37
37
  'devops', 'cloud', 'frontend', 'backend', 'mobile',
38
38
  ]),
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.160",
5
+ "version": "0.70.161",
6
6
  "description": "Default Stacks scaffold resources (views, layouts, components, preloader) — shipped so apps consuming the framework from node_modules get the same fallback resources a vendored checkout provides. Source of truth: storage/framework/defaults.",
7
7
  "author": "Chris Breuer",
8
8
  "license": "MIT",
@@ -25,7 +25,7 @@ cp`
25
25
  @endif
26
26
  </header>
27
27
 
28
- <div class="bg-[#0d1117] rounded-b-lg vue-ace-editor" style="height: 300px; font-size: 14px;">
28
+ <div class="bg-[#0d1117] rounded-b-lg stx-code-editor" style="height: 300px; font-size: 14px;">
29
29
  <pre class="p-4 font-mono text-[#c9d1d9] text-sm">{{ content }}</pre>
30
30
  </div>
31
31
  <style>
@@ -33,7 +33,7 @@ select {
33
33
  margin-right: 15px;
34
34
  }
35
35
 
36
- .vue-ace-editor {
36
+ .stx-code-editor {
37
37
  font-size: 14px;
38
38
  flex: 1;
39
39
  font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;
@@ -1,7 +1,7 @@
1
1
  <!--
2
2
  MediaCard — Meema-style visual file preview
3
3
 
4
- Ported from meemalabs/file-manager (MediaCard/{Image,Video,Audio,Document}.vue)
4
+ Ported from the meemalabs/file-manager media card components.
5
5
  to stx + Crosswind. Presentational: it renders the preview + label for one
6
6
  file item and derives the media "kind" from `mime_type` (preferred) or the
7
7
  file extension in `type`. Real thumbnails render when the item carries a
@@ -1,8 +1,8 @@
1
1
  <!--
2
2
  MediaInfoPanel — file preview + metadata
3
3
 
4
- Ported from meemalabs/file-manager (MediaInformation/{FilePreviewSection,
5
- MetaDataSection,SharingSection}.vue) to stx + Crosswind. Presentational: it
4
+ Ported from the meemalabs/file-manager file preview, metadata, and sharing
5
+ sections to STX and Crosswind. Presentational: it
6
6
  renders a large preview and a metadata list for one selected file. The parent
7
7
  owns the slide-over shell (visibility, positioning, close button) and passes
8
8
  the current item in.
@@ -189,10 +189,10 @@ const { libraryName = 'hello-world', libraryOwner = '@stacksjs', libraryReposito
189
189
  <div class="grid gap-x-8 gap-y-10 grid-cols-1 md:grid-cols-3 py-16 max-w-7xl">
190
190
  <div>
191
191
  <h2 class="font-semibold leading-7 text-base text-gray-900 dark:text-neutral-100">
192
- Vue Components Configurations
192
+ STX Component Configuration
193
193
  </h2>
194
194
  <p class="mt-1 leading-6 text-gray-600 text-sm">
195
- Update your Vue Components Configuration
195
+ Update your STX component package
196
196
  </p>
197
197
  </div>
198
198
 
@@ -223,15 +223,15 @@ const { libraryName = 'hello-world', libraryOwner = '@stacksjs', libraryReposito
223
223
  <div class="flex justify-between mb-4">
224
224
  <div>
225
225
  <h2 class="font-semibold leading-7 text-base text-gray-900 dark:text-neutral-100">
226
- Vue Components Tags
226
+ STX Component Tags
227
227
  </h2>
228
228
  <p class="mt-1 leading-6 text-gray-600 text-sm">
229
- Update Vue Components Tags, click on the button to show modal to add new tag
229
+ Update STX component tags or add a new custom element tag
230
230
  </p>
231
231
  </div>
232
232
 
233
233
  <div class="sm:flex-none mt-4 sm:ml-16 sm:mt-0">
234
- <button type="button" class="block px-3 py-2 font-semibold text-center text-sm text-white bg-gray-600 hover:bg-indigo-500 rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-indigo-600 focus-visible:outline-offset-2 shadow-sm" data-action="add-vue-tag">
234
+ <button type="button" class="block px-3 py-2 font-semibold text-center text-sm text-white bg-gray-600 hover:bg-indigo-500 rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-indigo-600 focus-visible:outline-offset-2 shadow-sm" data-action="add-stx-tag">
235
235
  Add Tag
236
236
  </button>
237
237
  </div>
@@ -5,177 +5,28 @@ interface SettingsFormManagerProps {
5
5
 
6
6
  const { name = '' } = defineProps<SettingsFormManagerProps>()
7
7
 
8
- import AIForm from './Forms/AIForm.vue'
9
- import AnalyticsForm from './Forms/AnalyticsForm.vue'
10
- import AppForm from './Forms/AppForm.vue'
11
- import CacheForm from './Forms/CacheForm.vue'
12
- import CliForm from './Forms/CLIForm.vue'
13
- import CloudForm from './Forms/CloudForm.vue'
14
- import DatabaseForm from './Forms/DatabaseForm.vue'
15
- import DNSForm from './Forms/DNSForm.vue'
16
- import EmailForm from './Forms/EmailForm.vue'
17
- import FileSystemForm from './Forms/FileSystemForm.vue'
18
- import HashingForm from './Forms/HashingForm.vue'
19
- import LibraryForm from './Forms/LibraryForm.vue'
20
- import QueueForm from './Forms/QueueForm.vue'
21
- import SearchEngineForm from './Forms/SearchEngineForm.vue'
22
- import SecurityForm from './Forms/SecurityForm.vue'
23
- import ServicesForm from './Forms/ServicesForm.vue'
24
- import StorageForm from './Forms/StorageForm.vue'
25
- import TeamsForm from './Forms/TeamsForm.vue'
26
-
27
- const options = [
28
- {
29
- key: 'ai',
30
- label: 'AI',
31
- },
32
- {
33
- key: 'analytics',
34
- label: 'Analytics',
35
- },
36
- {
37
- key: 'app',
38
- label: 'App',
39
- },
40
- {
41
- key: 'cache',
42
- label: 'Cache',
43
- },
44
- {
45
- key: 'cli',
46
- label: 'CLI',
47
- },
48
- {
49
- key: 'cloud',
50
- label: 'Cloud',
51
- },
52
- {
53
- key: 'database',
54
- label: 'Database',
55
- },
56
- {
57
- key: 'dns',
58
- label: 'DNS',
59
- },
60
- {
61
- key: 'email',
62
- label: 'Email',
63
- },
64
- {
65
- key: 'file-system',
66
- label: 'File System',
67
- },
68
- {
69
- key: 'hashing',
70
- label: 'Hashing',
71
- },
72
- {
73
- key: 'languages',
74
- label: 'Languages',
75
- },
76
- {
77
- key: 'library',
78
- label: 'Library',
79
- },
80
- {
81
- key: 'native',
82
- label: 'Native',
83
- },
84
- {
85
- key: 'mail',
86
- label: 'Mail',
87
- },
88
- {
89
- key: 'queue',
90
- label: 'Queue',
91
- },
92
- {
93
- key: 'search-engine',
94
- label: 'Search Engine',
95
- },
96
- {
97
- key: 'security',
98
- label: 'Security',
99
- },
100
- {
101
- key: 'services',
102
- label: 'Services',
103
- },
104
- {
105
- key: 'storage',
106
- label: 'Storage',
107
- },
108
- {
109
- key: 'team',
110
- label: 'Team',
111
- },
112
- {
113
- key: 'ui',
114
- label: 'UI',
115
- },
116
- ]
117
- function pageTitle() { return options.find(option => option.key === name }?.label || name)
8
+ function title(): string {
9
+ return name
10
+ .split(/[-_]/)
11
+ .map(part => part.charAt(0).toUpperCase() + part.slice(1))
12
+ .join(' ')
13
+ }
118
14
  </script>
119
- <main>
120
- <div class="md:col-span-2 px-4 py-6 sm:px-6 lg:px-8 bg-white ring-1 ring-gray-900/5 sm:rounded-xl shadow-sm">
121
- <h2 class="font-semibold leading-7 text-base text-gray-900">
122
- {{ pageTitle }} Configuration
123
- </h2>
124
- <p class="mt-1 leading-6 text-gray-600 text-sm">
125
- This configuration defines all of your {{ pageTitle }} options.
126
- </p>
127
15
 
128
- <div class="mt-10">
129
- @if(name === 'ai')
130
- <AIForm />
131
- @elseif(name === 'analytics')
132
- <AnalyticsForm />
133
- @elseif(name === 'app')
134
- <AppForm />
135
- @elseif(name === 'cache')
136
- <CacheForm />
137
- @elseif(name === 'cli')
138
- <CliForm />
139
- @elseif(name === 'cloud')
140
- <CloudForm />
141
- @elseif(name === 'database')
142
- <DatabaseForm />
143
- @elseif(name === 'dns')
144
- <DNSForm />
145
- @elseif(name === 'email')
146
- <EmailForm />
147
- @elseif(name === 'file-system')
148
- <FileSystemForm />
149
- @elseif(name === 'hashing')
150
- <HashingForm />
151
- @elseif(name === 'library')
152
- <LibraryForm />
153
- @elseif(name === 'queue')
154
- <QueueForm />
155
- @elseif(name === 'search-engine')
156
- <SearchEngineForm />
157
- @elseif(name === 'security')
158
- <SecurityForm />
159
- @elseif(name === 'services')
160
- <ServicesForm />
161
- @elseif(name === 'storage')
162
- <StorageForm />
163
- @elseif(name === 'team')
164
- <TeamsForm />
165
- @else
166
- <div class="text-center">
167
- <div class="h-12 w-12 text-gray-400 dark:text-neutral-200 duration-150 ease-in-out transition-all i-hugeicons-cog-8-tooth" />
168
- <h3 class="mt-2 font-semibold text-gray-900 text-sm">
169
- Settings <span class="font-bold">{{ pageTitle }}</span> Form not created yet
170
- </h3>
171
- <p class="mt-1 text-gray-500 text-sm">
172
- Please ask the maintenance team to fix.
173
- </p>
174
- </div>
175
- @endif
16
+ <Card variant="default" padding="lg">
17
+ <div class="flex gap-4 items-start">
18
+ <div class="flex flex-none items-center justify-center h-10 w-10 text-blue-600 dark:text-blue-400 bg-blue-500/10 rounded-lg">
19
+ <span class="h-5 w-5 i-hugeicons-settings-02"></span>
20
+ </div>
21
+ <div class="flex-1 min-w-0">
22
+ <h2 class="font-semibold text-neutral-900 dark:text-white">{{ title() }} configuration</h2>
23
+ <p class="mt-1 text-neutral-500 text-sm dark:text-neutral-400">
24
+ Configuration editing now lives in the unified Settings workspace.
25
+ </p>
26
+ <a href="/settings" class="inline-flex gap-2 items-center mt-4 font-medium text-blue-600 text-sm dark:text-blue-400 hover:underline">
27
+ Open Settings
28
+ <span class="h-4 w-4 i-hugeicons-arrow-right-02"></span>
29
+ </a>
176
30
  </div>
177
31
  </div>
178
- </main>
179
- <style scoped>
180
-
181
- </style>
32
+ </Card>
@@ -1,13 +1,13 @@
1
1
  <script server>
2
2
  /**
3
- * RouterLink bridge component for templates that use Vue Router-style
3
+ * RouterLink bridge component for templates that use declarative
4
4
  * `<RouterLink to="…">` syntax. The dashboard is server-rendered MPA, so
5
5
  * there's no actual client router; this just maps the `to` prop to a
6
6
  * standard `href` and forwards the rest. Keeps existing pages from
7
7
  * breaking with `[Error loading component: 'router-link']` while we
8
8
  * migrate them off the syntax.
9
9
  *
10
- * `replace` is accepted for parity with Vue's RouterLink; it has no
10
+ * `replace` is accepted for API compatibility; it has no
11
11
  * effect under MPA navigation but makes drop-in usage error-free.
12
12
  */
13
13
  interface RouterLinkProps {
@@ -1,4 +1,4 @@
1
- ```vue
1
+ ```html
2
2
  <script lang="ts" setup>
3
3
  import { ref, computed } from '@stacksjs/stx'
4
4
  import {
@@ -1,5 +1,5 @@
1
- ```vue
2
- <!-- App.vue -->
1
+ ```html
2
+ <!-- App.stx -->
3
3
  <script lang="ts" setup>
4
4
  import { Dialog, DialogPanel } from '@stacksjs/dialog'
5
5
 
@@ -1,4 +1,4 @@
1
- ```vue
1
+ ```html
2
2
  <script lang="ts" setup>
3
3
  import { ref } from '@stacksjs/stx'
4
4
  import { Dropdown, DropdownButton, DropdownItem, DropdownItems } from '@stacksjs/dropdown'
@@ -1,4 +1,4 @@
1
- ```vue
1
+ ```html
2
2
  <script lang="ts" setup>
3
3
  import { ref } from '@stacksjs/stx'
4
4
  import {
@@ -1,4 +1,4 @@
1
- ```vue
1
+ ```html
2
2
  <script lang="ts" setup>
3
3
  import { ref } from '@stacksjs/stx'
4
4
  import { Notification, notification } from '@stacksjs/notifications'
@@ -1,4 +1,4 @@
1
- ```vue
1
+ ```html
2
2
 
3
3
  <script lang="ts" setup>
4
4
  import { Popover, PopoverButton, PopoverPanel } from '@stacksjs/popover'
@@ -1,4 +1,4 @@
1
- ```vue
1
+ ```html
2
2
  <script lang="ts" setup>
3
3
  import { ref } from '@stacksjs/stx'
4
4
  import { RadioGroup, RadioGroupLabel, RadioGroupOption, RadioGroupDescription } from '@stacksjs/radio-group'
@@ -1,4 +1,4 @@
1
- ```vue
1
+ ```html
2
2
  <script setup>
3
3
  import { ref, computed } from '@stacksjs/stx'
4
4
  import { Stepper } from '@stacksjs/stepper'
@@ -1,4 +1,4 @@
1
- ```vue
1
+ ```html
2
2
  <script lang="ts" setup>
3
3
  import { ref } from '@stacksjs/stx'
4
4
  import { Switch } from '@stacksjs/switch'
@@ -69,7 +69,7 @@ console.log('HelloWorld component mounted & the greeting is', greeting)
69
69
  <div class="ml-12">
70
70
  <div class="mt-2 leading-6 text-gray-600 text-sm dark:text-neutral-400">
71
71
  The easiest way to build component libraries and distribute them on npm. Stacks automatically imports all of your components & makes them available to you in your templates.
72
- And it automatically builds framework agnostic (e.g. Web & Vue Components) component libraries out of those.
72
+ And it automatically builds framework agnostic (e.g. Web and STX components) component libraries out of those.
73
73
  </div>
74
74
  </div>
75
75
  </div>
@@ -1,87 +1,17 @@
1
- # The Components Folder
1
+ # Components
2
2
 
3
- This is where you create your components.
3
+ Create reusable STX components in this directory. Components are discovered by
4
+ the STX build pipeline and can be used directly from pages and other templates.
4
5
 
5
- > **Note**
6
- > If your project does not require any UI components, you can remove this folder. If your library does include components, you may use this readme as the boilerplate of your component library.
7
-
8
- ## 💡 Get Started
9
-
10
- It's easy to get started. The only prerequisite is a basic understanding of HTML, sprinkled with *minimal* JavaScript. In other words, there is virtually no learning curve.
11
-
12
- ```bash
13
- # you may use this GitHub template or the following command
14
- bunx stacks new hello-world
15
- cd hello-world
16
-
17
- buddy install # install deps for all packages
18
- buddy dev:components # starts the component dev server
19
- buddy build:components # builds the component library for production-ready use
20
- ```
21
-
22
- Additionally, the `package.json` contains some useful snippets you likely want to be aware of.
23
-
24
- ## 🤖 Usage
25
-
26
- Because this project is optimized toward the development of easily reusable & composable component libraries, it’s very easy to use (and distribute):
27
-
28
- ```vue
29
- <script setup lang="ts">
30
- import HelloWorld from 'hello-world-stack'
6
+ ```html
7
+ <script server lang="ts">
8
+ const props = defineProps<{ name: string }>()
31
9
  </script>
32
10
 
33
11
  <template>
34
- <HelloWorld name="J Doe" />
12
+ <p>Hello, {{ props.name }}.</p>
35
13
  </template>
36
14
  ```
37
15
 
38
- ### Tips
39
-
40
- This project also includes a simple way to handle your versioning. Through semantic commit names, it will also generate the two changelogs: one as part of the GitHub releases & the one markdown file that's stored within the root of the project.
41
-
42
- ```bash
43
- # how to create a git commit
44
- git add . # select the changes you want to commit
45
- buddy commit # then simply follow the prompts
46
-
47
- # after you successfully committed, you may create a "release"
48
- buddy release # automates git commits, versioning, and CHANGELOG generation
49
- ```
50
-
51
- Read more about these tips in the docs.
52
-
53
- ### Coding Style
54
-
55
- You may want to consider using the "Composition API" with [`<script setup>` SFC syntax](https://github.com/vuejs/rfcs/pull/227), as it is perfectly suited for building component libraries via Stacks. [ESLint](https://github.com/eslint) also comes pre-configured to statically analyze, fix and format your code without the need for Prettier.
56
-
57
- When using this template, feel free to adjust it to your needs. It is a framework to help you quickly & efficiently bootstrap and design component libraries using industry best practices.
58
-
59
- ## 🧪 Testing
60
-
61
- ```bash
62
- bun test
63
- ```
64
-
65
- ## 📈 Changelog
66
-
67
- Please see our [releases](https://github.com/stacksjs/stacks/releases) page for more information on what has changed recently.
68
-
69
- ## 🚜 Contributing
70
-
71
- Please review the [Contributing Guide](https://github.com/stacksjs/contributing) for details.
72
-
73
- ## 🏝 Community
74
-
75
- For help, discussion about best practices, or any other conversation that would benefit from being searchable:
76
-
77
- [Discussions on GitHub](https://github.com/stacksjs/stacks/discussions)
78
-
79
- For casual chit-chat with others using this package:
80
-
81
- [Join the Stacks Discord Server](https://discord.gg/stacksjs)
82
-
83
- ## 📄 License
84
-
85
- The MIT License (MIT). Please see [LICENSE](https://github.com/stacksjs/stacks/tree/main/LICENSE.md) for more information.
86
-
87
- Made with 💙
16
+ Run `buddy dev:components` while developing the component library and
17
+ `buddy build:components` to produce its distributable output.
@@ -4,10 +4,5 @@
4
4
  * Complete dashboard components for building modern admin interfaces.
5
5
  */
6
6
 
7
- // Layout Components
8
- export { default as DashboardLayout } from './DashboardLayout.vue'
9
- export { default as SidebarModern } from './SidebarModern.vue'
10
- export { default as NavbarModern } from './NavbarModern.vue'
11
-
12
7
  // UI Components (re-export from UI folder)
13
8
  export * from './UI'
@@ -35,7 +35,7 @@ export interface ComponentEntry {
35
35
 
36
36
  /**
37
37
  * Scan the dashboard component directory plus any user component
38
- * directories. Returns one entry per `.stx` / `.vue` file with the file
38
+ * directories. Returns one entry per `.stx` file with the file
39
39
  * size (so the UI can show "small", "medium", "large" hints).
40
40
  */
41
41
  export function listComponents(): ComponentEntry[] {
@@ -49,7 +49,7 @@ export function listComponents(): ComponentEntry[] {
49
49
  const abs = resolve(root, dir)
50
50
  if (!existsSync(abs)) continue
51
51
  walkSync(abs, (file) => {
52
- if (!/\.(?:stx|vue|tsx|jsx)$/.test(file)) return
52
+ if (!file.endsWith('.stx')) return
53
53
  try {
54
54
  const stat = statSync(file)
55
55
  out.push({
@@ -525,7 +525,7 @@ export function buildNavSections(
525
525
  { to: '/buddy', icon: 'terminal', text: 'Buddy CLI' },
526
526
  { to: '/environment', icon: 'settings', text: 'Environment' },
527
527
  { to: '/access-tokens', icon: 'lock', text: 'Access Tokens' },
528
- { to: '/settings/billing', icon: 'invoice', text: 'Billing' },
528
+ { to: '/settings', icon: 'settings', text: 'Settings' },
529
529
  { to: '/settings/mail', icon: 'mail', text: 'Mail Settings' },
530
530
  ]])
531
531
  }
@@ -652,7 +652,7 @@ export function buildWebSidebarSections(): WebSidebarSection[] {
652
652
  {
653
653
  id: 'system',
654
654
  label: '',
655
- items: [{ id: 'settings', label: 'Settings', icon: NAV_ICON_CLASSES.settings, iconColor: 'blue', href: '/settings/billing' }],
655
+ items: [{ id: 'settings', label: 'Settings', icon: NAV_ICON_CLASSES.settings, iconColor: 'blue', href: '/settings' }],
656
656
  },
657
657
  ]
658
658
  }
@@ -671,6 +671,6 @@ export function buildSidebarChunks(): { top: string, nav: string, bottom: string
671
671
  return {
672
672
  top: `<a href="/" class="sidebar-link sidebar-link-home"><span class="sidebar-icon">${svg('home')}</span><span>Home</span></a>`,
673
673
  nav: buildSidebarNavHtml(manifest.models, manifest.sections),
674
- bottom: `<a href="/settings/billing" class="sidebar-link"><span class="sidebar-icon">${svg('settings')}</span><span>Settings</span></a>`,
674
+ bottom: `<a href="/settings" class="sidebar-link"><span class="sidebar-icon">${svg('settings')}</span><span>Settings</span></a>`,
675
675
  }
676
676
  }
@@ -0,0 +1,81 @@
1
+ interface TrayMenuItem {
2
+ label?: string
3
+ type?: 'normal' | 'separator' | 'submenu'
4
+ action?: string
5
+ submenu?: TrayMenuItem[]
6
+ }
7
+
8
+ interface CraftTrayBridge {
9
+ setTitle(title: string): Promise<void>
10
+ setTooltip(tooltip: string): Promise<void>
11
+ setMenu(items: TrayMenuItem[]): Promise<void>
12
+ onClickToggleWindow(): () => void
13
+ }
14
+
15
+ interface CraftBridge {
16
+ tray?: CraftTrayBridge
17
+ }
18
+
19
+ type TrayActionHandler = (action: string) => void | Promise<void>
20
+
21
+ function bridge(): CraftBridge | undefined {
22
+ return (globalThis as typeof globalThis & { craft?: CraftBridge }).craft
23
+ }
24
+
25
+ export function buildTrayMenu(projects: string[]): TrayMenuItem[] {
26
+ const projectItems = projects.map((project) => {
27
+ const name = project.split('/').filter(Boolean).pop() || project
28
+ return {
29
+ label: name,
30
+ type: 'submenu' as const,
31
+ submenu: [
32
+ { label: 'Open Terminal', action: `open-terminal::${project}` },
33
+ { label: 'Copy IP Address', action: `copy-ip::${project}` },
34
+ { label: 'Open Dashboard', action: `open-dashboard::${project}` },
35
+ { label: 'Buddy Commands', action: `buddy-commands::${project}` },
36
+ { label: 'Deploy', action: `deploy::${project}` },
37
+ { type: 'separator' as const },
38
+ { label: 'Deploy Logs', action: `deploy-logs::${project}` },
39
+ { label: 'Site Logs', action: `site-logs::${project}` },
40
+ { label: 'Error Logs', action: `error-logs::${project}` },
41
+ { type: 'separator' as const },
42
+ { label: 'Edit .env', action: `edit-env::${project}` },
43
+ { label: 'Edit DNS', action: `edit-dns::${project}` },
44
+ { label: 'Edit Email Addresses', action: `edit-email::${project}` },
45
+ { label: 'Ask Buddy', action: `ask-buddy::${project}` },
46
+ ],
47
+ }
48
+ })
49
+
50
+ return [
51
+ { label: 'Refresh', action: 'refresh' },
52
+ { label: 'Open Terminal', action: 'open-terminal' },
53
+ { label: 'Environment Check', action: 'env-check' },
54
+ { label: 'Settings', action: 'settings' },
55
+ { label: 'Check for Updates', action: 'check-updates' },
56
+ { type: 'separator' },
57
+ { label: 'Projects', type: 'submenu', submenu: projectItems },
58
+ { type: 'separator' },
59
+ { label: 'Quit', action: 'quit' },
60
+ ]
61
+ }
62
+
63
+ export async function installTrayMenu(projects: string[], onAction: TrayActionHandler): Promise<() => void> {
64
+ const craft = bridge()
65
+ if (!craft?.tray)
66
+ return () => {}
67
+
68
+ await craft.tray.setTitle('Stacks')
69
+ await craft.tray.setTooltip('Stacks project shortcuts')
70
+ await craft.tray.setMenu(buildTrayMenu(projects))
71
+ const removeClick = craft.tray.onClickToggleWindow()
72
+ const listener = (event: Event) => {
73
+ const action = (event as CustomEvent<{ action?: string }>).detail?.action
74
+ if (action && action !== 'quit') void onAction(action)
75
+ }
76
+ globalThis.addEventListener('craft:tray:menuAction', listener)
77
+ return () => {
78
+ removeClick()
79
+ globalThis.removeEventListener('craft:tray:menuAction', listener)
80
+ }
81
+ }
@@ -1,14 +1,17 @@
1
- ## Layouts
1
+ # Layouts
2
2
 
3
- Vue components in this dir are used as layouts.
3
+ STX layouts wrap pages with shared navigation, metadata, and structure.
4
4
 
5
- By default, `default.stx` will be used unless an alternative is specified in the route meta.
6
-
7
- With [`unplugin-vue-router`](https://github.com/posva/unplugin-vue-router) and [`vite-plugin-layouts`](https://github.com/stacksjs/vite-plugin-layouts), you can specify the layout in the page's SFCs like this:
5
+ `default.stx` is used when a page does not select another layout. A page can
6
+ choose a named layout with the STX layout directive:
8
7
 
9
8
  ```html
10
- <route lang="yaml">
11
- meta:
12
- layout: home
13
- </route>
9
+ @layout('home')
10
+
11
+ <main>
12
+ <h1>{{ title }}</h1>
13
+ </main>
14
14
  ```
15
+
16
+ Layouts live in this directory and use the same STX template syntax as pages
17
+ and components.
@@ -550,12 +550,6 @@ Sponsor Here</div>
550
550
  <div class="package-desc">WebSocket & real-time features</div>
551
551
  </div>
552
552
 
553
- <div class="package-item">
554
- <div class="package-emoji">🔬</div>
555
- <div class="package-title">X-Ray</div>
556
- <div class="package-desc">Debugging & monitoring tools</div>
557
- </div>
558
-
559
553
  <div class="package-item">
560
554
  <div class="package-emoji">💳</div>
561
555
  <div class="package-title">Payments</div>
@@ -613,7 +607,7 @@ Sponsor Here</div>
613
607
  <div class="folder-item-icon">🏗️</div>
614
608
  <div class="folder-item-content">
615
609
  <div class="folder-item-name">stx</div>
616
- <div class="folder-item-desc">A fast & powerful UI framework. Templating inspired by Laravel Blade & Vue.</div>
610
+ <div class="folder-item-desc">A fast & powerful UI framework. Templating inspired by Laravel Blade and modern component systems.</div>
617
611
  </div>
618
612
  </a>
619
613
  </div>