@owlmeans/create-app 0.1.18-rc.3 → 0.1.18-rc.30
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/README.md +39 -2
- package/build/args.d.ts +8 -0
- package/build/args.d.ts.map +1 -1
- package/build/args.js +60 -6
- package/build/args.js.map +1 -1
- package/build/index.d.ts +4 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +2 -0
- package/build/index.js.map +1 -1
- package/build/naming.d.ts +11 -0
- package/build/naming.d.ts.map +1 -0
- package/build/naming.js +18 -0
- package/build/naming.js.map +1 -0
- package/build/run.d.ts.map +1 -1
- package/build/run.js +19 -13
- package/build/run.js.map +1 -1
- package/build/scaffold.d.ts +21 -0
- package/build/scaffold.d.ts.map +1 -0
- package/build/scaffold.js +18 -0
- package/build/scaffold.js.map +1 -0
- package/build/template.d.ts +19 -3
- package/build/template.d.ts.map +1 -1
- package/build/template.js +52 -12
- package/build/template.js.map +1 -1
- package/package.json +4 -3
- package/template/AGENTS.md +28 -1
- package/template/README.bare.md +57 -0
- package/template/README.md +5 -61
- package/template/_agents/scripts/link-skills.sh +310 -15
- package/template/_agents/skills/agent-memory/SKILL.md +12 -11
- package/template/_agents/skills/getting-started/SKILL.md +26 -86
- package/template/_agents/skills/memory-promotion/SKILL.md +10 -4
- package/template/_agents/skills/memory-recompact/SKILL.md +15 -14
- package/template/_agents/skills/reuse-code/SKILL.md +36 -8
- package/template/_agents/skills/self-education/SKILL.md +19 -5
- package/template/_agents/skills/skill-authoring/SKILL.md +53 -11
- package/template/_bare.json +23 -0
- package/template/_gitignore +3 -0
- package/template/bunfig.toml +2 -0
- package/template/package.json +3 -2
- package/template/sources/api/package.json +3 -3
- package/template/sources/api/src/app/session/add.ts +7 -6
- package/template/sources/api/src/app/session/list.ts +12 -9
- package/template/sources/api/src/app/session/remove.ts +7 -6
- package/template/sources/api/src/context.bare.ts +11 -0
- package/template/sources/api/src/entrypoints.bare.ts +4 -0
- package/template/sources/api/src/entrypoints.ts +13 -0
- package/template/sources/api/src/index.ts +2 -2
- package/template/sources/api/src/types.bare.ts +5 -0
- package/template/sources/common/package.json +6 -6
- package/template/sources/common/src/consts.bare.ts +8 -0
- package/template/sources/common/src/consts.ts +0 -13
- package/template/sources/common/src/entrypoints.bare.ts +14 -0
- package/template/sources/common/src/entrypoints.ts +60 -0
- package/template/sources/common/src/index.bare.ts +3 -0
- package/template/sources/common/src/index.ts +1 -1
- package/template/sources/common/src/schemas.ts +7 -7
- package/template/sources/web/index.html +3 -1
- package/template/sources/web/package.json +13 -11
- package/template/sources/web/src/components/ui/dropdown-menu.tsx +257 -0
- package/template/sources/web/src/components/ui/navigation-menu.tsx +169 -0
- package/template/sources/web/src/context.bare.ts +13 -0
- package/template/sources/web/src/context.ts +14 -2
- package/template/sources/web/src/entrypoints.bare.ts +13 -0
- package/template/sources/web/src/entrypoints.ts +16 -0
- package/template/sources/web/src/index.css +7 -0
- package/template/sources/web/src/index.tsx +2 -2
- package/template/sources/web/src/layout/main.tsx +16 -19
- package/template/sources/web/src/nav.bare.ts +21 -0
- package/template/sources/web/src/nav.ts +28 -0
- package/template/sources/web/src/screens/about.tsx +19 -0
- package/template/sources/web/src/screens/home.bare.tsx +17 -0
- package/template/sources/web/src/screens/session.tsx +28 -14
- package/template/sources/web/src/vite-env.d.ts +1 -0
- package/template/sources/api/src/modules.ts +0 -11
- package/template/sources/common/src/modules.ts +0 -26
- package/template/sources/web/src/components/nav/main.tsx +0 -15
- package/template/sources/web/src/modules.ts +0 -20
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { main } from '@owlmeans/server-app'
|
|
2
2
|
import config from './config.js'
|
|
3
3
|
import { makeContext } from './context.js'
|
|
4
|
-
import {
|
|
4
|
+
import { appBindings } from './entrypoints.js'
|
|
5
5
|
import type { Config, Context } from './types.js'
|
|
6
6
|
|
|
7
7
|
const context = makeContext<Config, Context>(config)
|
|
8
8
|
|
|
9
|
-
main<{}, Config, Context>(context,
|
|
9
|
+
main<{}, Config, Context>(context, appBindings)
|
|
@@ -19,15 +19,15 @@
|
|
|
19
19
|
"typecheck": "tsc -b"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@owlmeans/config": "^0.1.
|
|
23
|
-
"@owlmeans/entrypoint": "^0.1.
|
|
24
|
-
"@owlmeans/error": "^0.1.
|
|
25
|
-
"@owlmeans/resource": "^0.1.
|
|
26
|
-
"@owlmeans/route": "^0.1.
|
|
22
|
+
"@owlmeans/config": "^0.1.18-rc.23",
|
|
23
|
+
"@owlmeans/entrypoint": "^0.1.18-rc.22",
|
|
24
|
+
"@owlmeans/error": "^0.1.18-rc.19",
|
|
25
|
+
"@owlmeans/resource": "^0.1.18-rc.20",
|
|
26
|
+
"@owlmeans/route": "^0.1.18-rc.20",
|
|
27
27
|
"ajv": "^8.17.1"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@owlmeans/dep-config": "^0.1.
|
|
30
|
+
"@owlmeans/dep-config": "^0.1.18-rc.6",
|
|
31
31
|
"@types/node": "^24.10.1",
|
|
32
32
|
"typescript": "^7.0.2"
|
|
33
33
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Service aliases shared between web and api. */
|
|
2
|
+
export const APP = '__APP_SLUG__'
|
|
3
|
+
export const APP_WEB = '__APP_SLUG__-web'
|
|
4
|
+
export const APP_API = '__APP_SLUG__-api'
|
|
5
|
+
|
|
6
|
+
/** Local development ports. */
|
|
7
|
+
export const WEB_PORT = 3001
|
|
8
|
+
export const API_PORT = 3000
|
|
@@ -6,16 +6,3 @@ export const APP_API = '__APP_SLUG__-api'
|
|
|
6
6
|
/** Local development ports. */
|
|
7
7
|
export const WEB_PORT = 3001
|
|
8
8
|
export const API_PORT = 3000
|
|
9
|
-
|
|
10
|
-
/** Backend (API) entrypoint identifiers. */
|
|
11
|
-
export const session = {
|
|
12
|
-
base: '__APP_SLUG__:api:session',
|
|
13
|
-
list: '__APP_SLUG__:api:session:list',
|
|
14
|
-
add: '__APP_SLUG__:api:session:add',
|
|
15
|
-
remove: '__APP_SLUG__:api:session:remove',
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/** Frontend (web) entrypoint identifiers. */
|
|
19
|
-
export const web = {
|
|
20
|
-
session: '__APP_SLUG__:web:session',
|
|
21
|
-
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { openProtocol } from '@owlmeans/entrypoint'
|
|
2
|
+
import { BASE, HOME } from '@owlmeans/context'
|
|
3
|
+
import { frontend, route } from '@owlmeans/route'
|
|
4
|
+
|
|
5
|
+
const webBase = openProtocol(route(BASE, '/', frontend()))
|
|
6
|
+
|
|
7
|
+
/** The shared immutable protocol tree. Add API declarations below `api` as the shell grows. */
|
|
8
|
+
export const appProtocols = {
|
|
9
|
+
api: {},
|
|
10
|
+
web: {
|
|
11
|
+
base: webBase,
|
|
12
|
+
home: openProtocol(route(HOME, '/', frontend({ default: true, parent: webBase }))),
|
|
13
|
+
},
|
|
14
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { contract, openProtocol, protocol, typed } from '@owlmeans/entrypoint'
|
|
2
|
+
import { route, RouteMethod, frontend } from '@owlmeans/route'
|
|
3
|
+
import { AddItemSchema, ItemParamsSchema, SessionParamsSchema } from './schemas.js'
|
|
4
|
+
import type { SessionItem } from './types.js'
|
|
5
|
+
|
|
6
|
+
const aliases = {
|
|
7
|
+
session: {
|
|
8
|
+
base: '__APP_SLUG__:api:session',
|
|
9
|
+
list: '__APP_SLUG__:api:session:list',
|
|
10
|
+
add: '__APP_SLUG__:api:session:add',
|
|
11
|
+
remove: '__APP_SLUG__:api:session:remove',
|
|
12
|
+
},
|
|
13
|
+
web: {
|
|
14
|
+
base: '__APP_SLUG__:web:base',
|
|
15
|
+
home: '__APP_SLUG__:web:home',
|
|
16
|
+
session: '__APP_SLUG__:web:session',
|
|
17
|
+
about: '__APP_SLUG__:web:about',
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const sessionBase = protocol(route(aliases.session.base, '/session'), contract())
|
|
22
|
+
const webBase = openProtocol(route(aliases.web.base, '/', frontend()))
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Shared entrypoint declarations. The api materializes these with handlers; the web
|
|
26
|
+
* materializes them with screen components and calls them. Routes resolve under the
|
|
27
|
+
* api service `base` (`/api`), so e.g. `session.list` → `GET /api/session/:sid/items`.
|
|
28
|
+
*/
|
|
29
|
+
export const session = {
|
|
30
|
+
base: sessionBase,
|
|
31
|
+
list: protocol(
|
|
32
|
+
route(aliases.session.list, '/:sid/items', { parent: sessionBase, method: RouteMethod.GET }),
|
|
33
|
+
contract.request({ params: SessionParamsSchema }, typed<SessionItem[]>()),
|
|
34
|
+
),
|
|
35
|
+
add: protocol(
|
|
36
|
+
route(aliases.session.add, '/:sid/items', { parent: sessionBase, method: RouteMethod.POST }),
|
|
37
|
+
contract.request({
|
|
38
|
+
params: SessionParamsSchema,
|
|
39
|
+
body: AddItemSchema,
|
|
40
|
+
}, typed<SessionItem>()),
|
|
41
|
+
),
|
|
42
|
+
remove: protocol(
|
|
43
|
+
route(aliases.session.remove, '/:sid/items/:id', { parent: sessionBase, method: RouteMethod.DELETE }),
|
|
44
|
+
contract.request({ params: ItemParamsSchema }, typed<{ removed: boolean }>()),
|
|
45
|
+
),
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Frontend routes are protocol declarations too; their renderer is bound by the web project. */
|
|
49
|
+
export const web = {
|
|
50
|
+
base: webBase,
|
|
51
|
+
home: openProtocol(route(aliases.web.home, '/', frontend({ default: true, parent: webBase }))),
|
|
52
|
+
session: openProtocol(route(aliases.web.session, '/session', frontend({ parent: webBase }))),
|
|
53
|
+
about: openProtocol(route(aliases.web.about, '/about', frontend({ parent: webBase }))),
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The shared immutable protocol tree. Runtime packages bind their own local handlers and screens. */
|
|
57
|
+
export const appProtocols = Object.freeze({
|
|
58
|
+
api: Object.freeze({ session }),
|
|
59
|
+
web: Object.freeze(web),
|
|
60
|
+
})
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { schema } from '@owlmeans/entrypoint'
|
|
2
2
|
import type { AddItemPayload, ItemParams, SessionParams } from './types.js'
|
|
3
3
|
|
|
4
|
-
export const AddItemSchema
|
|
4
|
+
export const AddItemSchema = schema<AddItemPayload>({
|
|
5
5
|
type: 'object',
|
|
6
6
|
properties: {
|
|
7
7
|
text: { type: 'string', minLength: 1, maxLength: 280 },
|
|
8
8
|
},
|
|
9
9
|
required: ['text'],
|
|
10
10
|
additionalProperties: false,
|
|
11
|
-
}
|
|
11
|
+
})
|
|
12
12
|
|
|
13
|
-
export const SessionParamsSchema
|
|
13
|
+
export const SessionParamsSchema = schema<SessionParams>({
|
|
14
14
|
type: 'object',
|
|
15
15
|
properties: {
|
|
16
16
|
sid: { type: 'string', minLength: 1 },
|
|
17
17
|
},
|
|
18
18
|
required: ['sid'],
|
|
19
19
|
additionalProperties: false,
|
|
20
|
-
}
|
|
20
|
+
})
|
|
21
21
|
|
|
22
|
-
export const ItemParamsSchema
|
|
22
|
+
export const ItemParamsSchema = schema<ItemParams>({
|
|
23
23
|
type: 'object',
|
|
24
24
|
properties: {
|
|
25
25
|
sid: { type: 'string', minLength: 1 },
|
|
@@ -27,4 +27,4 @@ export const ItemParamsSchema: JSONSchemaType<ItemParams> = {
|
|
|
27
27
|
},
|
|
28
28
|
required: ['sid', 'id'],
|
|
29
29
|
additionalProperties: false,
|
|
30
|
-
}
|
|
30
|
+
})
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
<!doctype html>
|
|
2
|
-
<html lang="
|
|
2
|
+
<html lang="__APP_LANG__">
|
|
3
3
|
|
|
4
4
|
<head>
|
|
5
5
|
<meta charset="UTF-8" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<meta name="application-name" content="__APP_NAME__" />
|
|
8
|
+
<meta name="description" content="__APP_DESCRIPTION__" />
|
|
7
9
|
<title>__APP_NAME__</title>
|
|
8
10
|
</head>
|
|
9
11
|
|
|
@@ -11,19 +11,21 @@
|
|
|
11
11
|
"preview": "vite preview"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@owlmeans/client": "^0.1.
|
|
15
|
-
"@owlmeans/client-config": "^0.1.
|
|
16
|
-
"@owlmeans/client-context": "^0.1.
|
|
17
|
-
"@owlmeans/client-entrypoint": "^0.1.
|
|
18
|
-
"@owlmeans/client-i18n": "^0.1.
|
|
19
|
-
"@owlmeans/entrypoint": "^0.1.
|
|
20
|
-
"@owlmeans/route": "^0.1.
|
|
21
|
-
"@owlmeans/
|
|
22
|
-
"@owlmeans/web-
|
|
23
|
-
"
|
|
14
|
+
"@owlmeans/client": "^0.1.18-rc.27",
|
|
15
|
+
"@owlmeans/client-config": "^0.1.18-rc.23",
|
|
16
|
+
"@owlmeans/client-context": "^0.1.18-rc.24",
|
|
17
|
+
"@owlmeans/client-entrypoint": "^0.1.18-rc.24",
|
|
18
|
+
"@owlmeans/client-i18n": "^0.1.18-rc.26",
|
|
19
|
+
"@owlmeans/entrypoint": "^0.1.18-rc.22",
|
|
20
|
+
"@owlmeans/route": "^0.1.18-rc.20",
|
|
21
|
+
"@owlmeans/state": "^0.1.18-rc.21",
|
|
22
|
+
"@owlmeans/web-client": "^0.1.18-rc.36",
|
|
23
|
+
"@owlmeans/web-panel": "^0.1.18-rc.44",
|
|
24
24
|
"@radix-ui/react-label": "^2.1.0",
|
|
25
|
+
"@radix-ui/react-navigation-menu": "^1.2.14",
|
|
25
26
|
"@radix-ui/react-progress": "^1.1.0",
|
|
26
27
|
"@radix-ui/react-slot": "^1.1.0",
|
|
28
|
+
"__APP_SLUG__-common": "workspace:^",
|
|
27
29
|
"ajv": "^8.17.1",
|
|
28
30
|
"class-variance-authority": "^0.7.0",
|
|
29
31
|
"clsx": "^2.1.0",
|
|
@@ -37,7 +39,7 @@
|
|
|
37
39
|
"tailwindcss": "^4.1.13"
|
|
38
40
|
},
|
|
39
41
|
"devDependencies": {
|
|
40
|
-
"@owlmeans/dep-config": "^0.1.
|
|
42
|
+
"@owlmeans/dep-config": "^0.1.18-rc.6",
|
|
41
43
|
"@tailwindcss/vite": "^4.1.13",
|
|
42
44
|
"@types/react": "^19.2.17",
|
|
43
45
|
"@types/react-dom": "^19.2.3",
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
|
|
5
|
+
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
|
|
6
|
+
|
|
7
|
+
import { cn } from "@/lib/utils"
|
|
8
|
+
|
|
9
|
+
function DropdownMenu({
|
|
10
|
+
...props
|
|
11
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
|
|
12
|
+
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function DropdownMenuPortal({
|
|
16
|
+
...props
|
|
17
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
|
|
18
|
+
return (
|
|
19
|
+
<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function DropdownMenuTrigger({
|
|
24
|
+
...props
|
|
25
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
|
|
26
|
+
return (
|
|
27
|
+
<DropdownMenuPrimitive.Trigger
|
|
28
|
+
data-slot="dropdown-menu-trigger"
|
|
29
|
+
{...props}
|
|
30
|
+
/>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function DropdownMenuContent({
|
|
35
|
+
className,
|
|
36
|
+
sideOffset = 4,
|
|
37
|
+
...props
|
|
38
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
|
|
39
|
+
return (
|
|
40
|
+
<DropdownMenuPrimitive.Portal>
|
|
41
|
+
<DropdownMenuPrimitive.Content
|
|
42
|
+
data-slot="dropdown-menu-content"
|
|
43
|
+
sideOffset={sideOffset}
|
|
44
|
+
className={cn(
|
|
45
|
+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
|
|
46
|
+
className
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
/>
|
|
50
|
+
</DropdownMenuPrimitive.Portal>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function DropdownMenuGroup({
|
|
55
|
+
...props
|
|
56
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
|
|
57
|
+
return (
|
|
58
|
+
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function DropdownMenuItem({
|
|
63
|
+
className,
|
|
64
|
+
inset,
|
|
65
|
+
variant = "default",
|
|
66
|
+
...props
|
|
67
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
|
|
68
|
+
inset?: boolean
|
|
69
|
+
variant?: "default" | "destructive"
|
|
70
|
+
}) {
|
|
71
|
+
return (
|
|
72
|
+
<DropdownMenuPrimitive.Item
|
|
73
|
+
data-slot="dropdown-menu-item"
|
|
74
|
+
data-inset={inset}
|
|
75
|
+
data-variant={variant}
|
|
76
|
+
className={cn(
|
|
77
|
+
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
78
|
+
className
|
|
79
|
+
)}
|
|
80
|
+
{...props}
|
|
81
|
+
/>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function DropdownMenuCheckboxItem({
|
|
86
|
+
className,
|
|
87
|
+
children,
|
|
88
|
+
checked,
|
|
89
|
+
...props
|
|
90
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
|
|
91
|
+
return (
|
|
92
|
+
<DropdownMenuPrimitive.CheckboxItem
|
|
93
|
+
data-slot="dropdown-menu-checkbox-item"
|
|
94
|
+
className={cn(
|
|
95
|
+
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
96
|
+
className
|
|
97
|
+
)}
|
|
98
|
+
checked={checked}
|
|
99
|
+
{...props}
|
|
100
|
+
>
|
|
101
|
+
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
|
102
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
103
|
+
<CheckIcon className="size-4" />
|
|
104
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
105
|
+
</span>
|
|
106
|
+
{children}
|
|
107
|
+
</DropdownMenuPrimitive.CheckboxItem>
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function DropdownMenuRadioGroup({
|
|
112
|
+
...props
|
|
113
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
|
|
114
|
+
return (
|
|
115
|
+
<DropdownMenuPrimitive.RadioGroup
|
|
116
|
+
data-slot="dropdown-menu-radio-group"
|
|
117
|
+
{...props}
|
|
118
|
+
/>
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function DropdownMenuRadioItem({
|
|
123
|
+
className,
|
|
124
|
+
children,
|
|
125
|
+
...props
|
|
126
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
|
|
127
|
+
return (
|
|
128
|
+
<DropdownMenuPrimitive.RadioItem
|
|
129
|
+
data-slot="dropdown-menu-radio-item"
|
|
130
|
+
className={cn(
|
|
131
|
+
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
132
|
+
className
|
|
133
|
+
)}
|
|
134
|
+
{...props}
|
|
135
|
+
>
|
|
136
|
+
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
|
137
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
138
|
+
<CircleIcon className="size-2 fill-current" />
|
|
139
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
140
|
+
</span>
|
|
141
|
+
{children}
|
|
142
|
+
</DropdownMenuPrimitive.RadioItem>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function DropdownMenuLabel({
|
|
147
|
+
className,
|
|
148
|
+
inset,
|
|
149
|
+
...props
|
|
150
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
|
|
151
|
+
inset?: boolean
|
|
152
|
+
}) {
|
|
153
|
+
return (
|
|
154
|
+
<DropdownMenuPrimitive.Label
|
|
155
|
+
data-slot="dropdown-menu-label"
|
|
156
|
+
data-inset={inset}
|
|
157
|
+
className={cn(
|
|
158
|
+
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
|
|
159
|
+
className
|
|
160
|
+
)}
|
|
161
|
+
{...props}
|
|
162
|
+
/>
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function DropdownMenuSeparator({
|
|
167
|
+
className,
|
|
168
|
+
...props
|
|
169
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
|
|
170
|
+
return (
|
|
171
|
+
<DropdownMenuPrimitive.Separator
|
|
172
|
+
data-slot="dropdown-menu-separator"
|
|
173
|
+
className={cn("bg-border -mx-1 my-1 h-px", className)}
|
|
174
|
+
{...props}
|
|
175
|
+
/>
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function DropdownMenuShortcut({
|
|
180
|
+
className,
|
|
181
|
+
...props
|
|
182
|
+
}: React.ComponentProps<"span">) {
|
|
183
|
+
return (
|
|
184
|
+
<span
|
|
185
|
+
data-slot="dropdown-menu-shortcut"
|
|
186
|
+
className={cn(
|
|
187
|
+
"text-muted-foreground ml-auto text-xs tracking-widest",
|
|
188
|
+
className
|
|
189
|
+
)}
|
|
190
|
+
{...props}
|
|
191
|
+
/>
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function DropdownMenuSub({
|
|
196
|
+
...props
|
|
197
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
|
|
198
|
+
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function DropdownMenuSubTrigger({
|
|
202
|
+
className,
|
|
203
|
+
inset,
|
|
204
|
+
children,
|
|
205
|
+
...props
|
|
206
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
|
|
207
|
+
inset?: boolean
|
|
208
|
+
}) {
|
|
209
|
+
return (
|
|
210
|
+
<DropdownMenuPrimitive.SubTrigger
|
|
211
|
+
data-slot="dropdown-menu-sub-trigger"
|
|
212
|
+
data-inset={inset}
|
|
213
|
+
className={cn(
|
|
214
|
+
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8",
|
|
215
|
+
className
|
|
216
|
+
)}
|
|
217
|
+
{...props}
|
|
218
|
+
>
|
|
219
|
+
{children}
|
|
220
|
+
<ChevronRightIcon className="ml-auto size-4" />
|
|
221
|
+
</DropdownMenuPrimitive.SubTrigger>
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function DropdownMenuSubContent({
|
|
226
|
+
className,
|
|
227
|
+
...props
|
|
228
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
|
|
229
|
+
return (
|
|
230
|
+
<DropdownMenuPrimitive.SubContent
|
|
231
|
+
data-slot="dropdown-menu-sub-content"
|
|
232
|
+
className={cn(
|
|
233
|
+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
|
|
234
|
+
className
|
|
235
|
+
)}
|
|
236
|
+
{...props}
|
|
237
|
+
/>
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export {
|
|
242
|
+
DropdownMenu,
|
|
243
|
+
DropdownMenuPortal,
|
|
244
|
+
DropdownMenuTrigger,
|
|
245
|
+
DropdownMenuContent,
|
|
246
|
+
DropdownMenuGroup,
|
|
247
|
+
DropdownMenuLabel,
|
|
248
|
+
DropdownMenuItem,
|
|
249
|
+
DropdownMenuCheckboxItem,
|
|
250
|
+
DropdownMenuRadioGroup,
|
|
251
|
+
DropdownMenuRadioItem,
|
|
252
|
+
DropdownMenuSeparator,
|
|
253
|
+
DropdownMenuShortcut,
|
|
254
|
+
DropdownMenuSub,
|
|
255
|
+
DropdownMenuSubTrigger,
|
|
256
|
+
DropdownMenuSubContent,
|
|
257
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// shadcn navigation-menu — sourced from shadcn/ui (new-york) 2026-08-24
|
|
2
|
+
import * as React from "react"
|
|
3
|
+
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
|
|
4
|
+
import { cva } from "class-variance-authority"
|
|
5
|
+
import { ChevronDownIcon } from "lucide-react"
|
|
6
|
+
|
|
7
|
+
import { cn } from "@/lib/utils"
|
|
8
|
+
|
|
9
|
+
function NavigationMenu({
|
|
10
|
+
className,
|
|
11
|
+
children,
|
|
12
|
+
viewport = true,
|
|
13
|
+
...props
|
|
14
|
+
}: React.ComponentProps<typeof NavigationMenuPrimitive.Root> & {
|
|
15
|
+
viewport?: boolean
|
|
16
|
+
}) {
|
|
17
|
+
return (
|
|
18
|
+
<NavigationMenuPrimitive.Root
|
|
19
|
+
data-slot="navigation-menu"
|
|
20
|
+
data-viewport={viewport}
|
|
21
|
+
className={cn(
|
|
22
|
+
"group/navigation-menu relative flex max-w-max flex-1 items-center justify-center",
|
|
23
|
+
className
|
|
24
|
+
)}
|
|
25
|
+
{...props}
|
|
26
|
+
>
|
|
27
|
+
{children}
|
|
28
|
+
{viewport && <NavigationMenuViewport />}
|
|
29
|
+
</NavigationMenuPrimitive.Root>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function NavigationMenuList({
|
|
34
|
+
className,
|
|
35
|
+
...props
|
|
36
|
+
}: React.ComponentProps<typeof NavigationMenuPrimitive.List>) {
|
|
37
|
+
return (
|
|
38
|
+
<NavigationMenuPrimitive.List
|
|
39
|
+
data-slot="navigation-menu-list"
|
|
40
|
+
className={cn(
|
|
41
|
+
"group flex flex-1 list-none items-center justify-center gap-1",
|
|
42
|
+
className
|
|
43
|
+
)}
|
|
44
|
+
{...props}
|
|
45
|
+
/>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function NavigationMenuItem({
|
|
50
|
+
className,
|
|
51
|
+
...props
|
|
52
|
+
}: React.ComponentProps<typeof NavigationMenuPrimitive.Item>) {
|
|
53
|
+
return (
|
|
54
|
+
<NavigationMenuPrimitive.Item
|
|
55
|
+
data-slot="navigation-menu-item"
|
|
56
|
+
className={cn("relative", className)}
|
|
57
|
+
{...props}
|
|
58
|
+
/>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const navigationMenuTriggerStyle = cva(
|
|
63
|
+
"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 focus-visible:ring-ring/50 outline-none transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
function NavigationMenuTrigger({
|
|
67
|
+
className,
|
|
68
|
+
children,
|
|
69
|
+
...props
|
|
70
|
+
}: React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>) {
|
|
71
|
+
return (
|
|
72
|
+
<NavigationMenuPrimitive.Trigger
|
|
73
|
+
data-slot="navigation-menu-trigger"
|
|
74
|
+
className={cn(navigationMenuTriggerStyle(), "group", className)}
|
|
75
|
+
{...props}
|
|
76
|
+
>
|
|
77
|
+
{children}{" "}
|
|
78
|
+
<ChevronDownIcon
|
|
79
|
+
className="relative top-[1px] ml-1 size-3 transition duration-300 group-data-[state=open]:rotate-180"
|
|
80
|
+
aria-hidden="true"
|
|
81
|
+
/>
|
|
82
|
+
</NavigationMenuPrimitive.Trigger>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function NavigationMenuContent({
|
|
87
|
+
className,
|
|
88
|
+
...props
|
|
89
|
+
}: React.ComponentProps<typeof NavigationMenuPrimitive.Content>) {
|
|
90
|
+
return (
|
|
91
|
+
<NavigationMenuPrimitive.Content
|
|
92
|
+
data-slot="navigation-menu-content"
|
|
93
|
+
className={cn(
|
|
94
|
+
"data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 top-0 left-0 w-full p-2 pr-2.5 md:absolute md:w-auto",
|
|
95
|
+
"group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none",
|
|
96
|
+
className
|
|
97
|
+
)}
|
|
98
|
+
{...props}
|
|
99
|
+
/>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function NavigationMenuViewport({
|
|
104
|
+
className,
|
|
105
|
+
...props
|
|
106
|
+
}: React.ComponentProps<typeof NavigationMenuPrimitive.Viewport>) {
|
|
107
|
+
return (
|
|
108
|
+
<div
|
|
109
|
+
className={cn(
|
|
110
|
+
"absolute top-full left-0 isolate z-50 flex justify-center"
|
|
111
|
+
)}
|
|
112
|
+
>
|
|
113
|
+
<NavigationMenuPrimitive.Viewport
|
|
114
|
+
data-slot="navigation-menu-viewport"
|
|
115
|
+
className={cn(
|
|
116
|
+
"origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow md:w-[var(--radix-navigation-menu-viewport-width)]",
|
|
117
|
+
className
|
|
118
|
+
)}
|
|
119
|
+
{...props}
|
|
120
|
+
/>
|
|
121
|
+
</div>
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function NavigationMenuLink({
|
|
126
|
+
className,
|
|
127
|
+
...props
|
|
128
|
+
}: React.ComponentProps<typeof NavigationMenuPrimitive.Link>) {
|
|
129
|
+
return (
|
|
130
|
+
<NavigationMenuPrimitive.Link
|
|
131
|
+
data-slot="navigation-menu-link"
|
|
132
|
+
className={cn(
|
|
133
|
+
"data-[active=true]:focus:bg-accent data-[active=true]:hover:bg-accent data-[active=true]:bg-accent/50 data-[active=true]:text-accent-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus-visible:ring-ring/50 [&_svg:not([class*='text-'])]:text-muted-foreground flex flex-col gap-1 rounded-sm p-2 text-sm transition-all outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&_svg:not([class*='size-'])]:size-4",
|
|
134
|
+
className
|
|
135
|
+
)}
|
|
136
|
+
{...props}
|
|
137
|
+
/>
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function NavigationMenuIndicator({
|
|
142
|
+
className,
|
|
143
|
+
...props
|
|
144
|
+
}: React.ComponentProps<typeof NavigationMenuPrimitive.Indicator>) {
|
|
145
|
+
return (
|
|
146
|
+
<NavigationMenuPrimitive.Indicator
|
|
147
|
+
data-slot="navigation-menu-indicator"
|
|
148
|
+
className={cn(
|
|
149
|
+
"data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden",
|
|
150
|
+
className
|
|
151
|
+
)}
|
|
152
|
+
{...props}
|
|
153
|
+
>
|
|
154
|
+
<div className="bg-border relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md" />
|
|
155
|
+
</NavigationMenuPrimitive.Indicator>
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export {
|
|
160
|
+
NavigationMenu,
|
|
161
|
+
NavigationMenuList,
|
|
162
|
+
NavigationMenuItem,
|
|
163
|
+
NavigationMenuContent,
|
|
164
|
+
NavigationMenuTrigger,
|
|
165
|
+
NavigationMenuLink,
|
|
166
|
+
NavigationMenuIndicator,
|
|
167
|
+
NavigationMenuViewport,
|
|
168
|
+
navigationMenuTriggerStyle,
|
|
169
|
+
}
|