@autono/open-pages 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +108 -0
- package/bin.js +2 -0
- package/dist/build-TP72kiw7.js +35 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +100 -0
- package/dist/config-C9or4m4J.d.ts +23 -0
- package/dist/config-L0IbktJ0.js +3135 -0
- package/dist/design-C13iz9_4.js +35 -0
- package/dist/dev-I2IyEh5W.js +97 -0
- package/dist/export-B-kBRWB1.js +4 -0
- package/dist/export-ic28osQP.js +131 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.js +3 -0
- package/dist/locale/index.d.ts +24 -0
- package/dist/locale/index.js +881 -0
- package/dist/open-pages-plugin-D_DN_zjh.js +632 -0
- package/dist/preview-BrDuvgAN.js +21 -0
- package/dist/sync-Be_hxSOF.js +139 -0
- package/dist/sync-lKEXyPOh.js +3 -0
- package/dist/types-C5NCdDT-.d.ts +219 -0
- package/dist/update-CV62NhBT.js +37 -0
- package/dist/update-package-Cxt57pz7.js +152 -0
- package/dist/version-B6AiiGvT.js +26 -0
- package/dist/version-CeffgdfZ.js +3 -0
- package/dist/vite/index.d.ts +14 -0
- package/dist/vite/index.js +7 -0
- package/env.d.ts +59 -0
- package/package.json +106 -0
- package/skills/apply-comments/SKILL.md +86 -0
- package/skills/create-page/SKILL.md +92 -0
- package/skills/create-theme/SKILL.md +223 -0
- package/skills/current-page/SKILL.md +106 -0
- package/skills/page-authoring/SKILL.md +148 -0
- package/skills/page-authoring/references/assets-and-fonts.md +85 -0
- package/skills/page-authoring/references/html-pages.md +65 -0
- package/skills/page-authoring/references/interactivity.md +77 -0
- package/skills/page-authoring/references/layout-and-responsive.md +71 -0
- package/skills/page-authoring/references/typography-and-color.md +60 -0
- package/src/app/app.tsx +45 -0
- package/src/app/components/asset-view.tsx +1665 -0
- package/src/app/components/command/command-menu.tsx +227 -0
- package/src/app/components/command/command.tsx +142 -0
- package/src/app/components/command/home-command-menu.tsx +96 -0
- package/src/app/components/icon-tooltip.tsx +34 -0
- package/src/app/components/language-toggle.tsx +46 -0
- package/src/app/components/sidebar/folder-item.tsx +269 -0
- package/src/app/components/sidebar/icon-picker.tsx +61 -0
- package/src/app/components/sidebar/sidebar-footer.tsx +139 -0
- package/src/app/components/sidebar/sidebar.tsx +302 -0
- package/src/app/components/theme-toggle.tsx +66 -0
- package/src/app/components/themes/theme-detail.tsx +226 -0
- package/src/app/components/themes/themes-gallery.tsx +104 -0
- package/src/app/components/ui/badge.tsx +48 -0
- package/src/app/components/ui/button.tsx +93 -0
- package/src/app/components/ui/card.tsx +92 -0
- package/src/app/components/ui/context-menu.tsx +247 -0
- package/src/app/components/ui/dialog.tsx +155 -0
- package/src/app/components/ui/dropdown-menu.tsx +267 -0
- package/src/app/components/ui/input.tsx +25 -0
- package/src/app/components/ui/label.tsx +21 -0
- package/src/app/components/ui/popover.tsx +82 -0
- package/src/app/components/ui/progress.tsx +33 -0
- package/src/app/components/ui/scroll-area.tsx +53 -0
- package/src/app/components/ui/select.tsx +195 -0
- package/src/app/components/ui/separator.tsx +26 -0
- package/src/app/components/ui/slider.tsx +68 -0
- package/src/app/components/ui/sonner.tsx +48 -0
- package/src/app/components/ui/tabs.tsx +79 -0
- package/src/app/components/ui/textarea.tsx +22 -0
- package/src/app/components/ui/toggle-group.tsx +84 -0
- package/src/app/components/ui/toggle.tsx +45 -0
- package/src/app/components/ui/tooltip.tsx +75 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/frame/inspect.ts +140 -0
- package/src/app/frame/main.tsx +91 -0
- package/src/app/frame.html +12 -0
- package/src/app/index.html +13 -0
- package/src/app/lib/asset-filter.test.ts +176 -0
- package/src/app/lib/asset-filter.ts +80 -0
- package/src/app/lib/assets.ts +257 -0
- package/src/app/lib/design.ts +58 -0
- package/src/app/lib/folders.ts +239 -0
- package/src/app/lib/frame.ts +38 -0
- package/src/app/lib/locale-store.ts +67 -0
- package/src/app/lib/page-thumb.tsx +46 -0
- package/src/app/lib/pages.ts +28 -0
- package/src/app/lib/sdk.ts +43 -0
- package/src/app/lib/themes.ts +22 -0
- package/src/app/lib/use-locale.ts +8 -0
- package/src/app/lib/use-page-module.ts +48 -0
- package/src/app/lib/use-page-titles.ts +29 -0
- package/src/app/lib/use-restart-server.ts +78 -0
- package/src/app/lib/utils.test.ts +25 -0
- package/src/app/lib/utils.ts +6 -0
- package/src/app/main.tsx +14 -0
- package/src/app/routes/assets.tsx +9 -0
- package/src/app/routes/home-shell.tsx +252 -0
- package/src/app/routes/home.tsx +846 -0
- package/src/app/routes/page.tsx +329 -0
- package/src/app/routes/themes.tsx +34 -0
- package/src/app/styles.css +405 -0
- package/src/app/virtual.d.ts +53 -0
- package/src/locale/en.ts +218 -0
- package/src/locale/format.ts +12 -0
- package/src/locale/index.ts +6 -0
- package/src/locale/ja.ts +221 -0
- package/src/locale/types.ts +215 -0
- package/src/locale/zh-cn.ts +218 -0
- package/src/locale/zh-tw.ts +218 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { Separator as SeparatorPrimitive } from '@base-ui/react/separator';
|
|
5
|
+
|
|
6
|
+
import { cn } from '@/lib/utils';
|
|
7
|
+
|
|
8
|
+
function Separator({
|
|
9
|
+
className,
|
|
10
|
+
orientation = 'horizontal',
|
|
11
|
+
...props
|
|
12
|
+
}: React.ComponentProps<typeof SeparatorPrimitive>) {
|
|
13
|
+
return (
|
|
14
|
+
<SeparatorPrimitive
|
|
15
|
+
data-slot="separator"
|
|
16
|
+
orientation={orientation}
|
|
17
|
+
className={cn(
|
|
18
|
+
'shrink-0 bg-hairline data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch',
|
|
19
|
+
className,
|
|
20
|
+
)}
|
|
21
|
+
{...props}
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { Separator };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Slider as DocrPrimitive } from "@base-ui/react/slider"
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils"
|
|
5
|
+
|
|
6
|
+
function Slider({
|
|
7
|
+
className,
|
|
8
|
+
defaultValue,
|
|
9
|
+
value,
|
|
10
|
+
min = 0,
|
|
11
|
+
max = 100,
|
|
12
|
+
...props
|
|
13
|
+
}: React.ComponentProps<typeof DocrPrimitive.Root>) {
|
|
14
|
+
const _values = React.useMemo(
|
|
15
|
+
() =>
|
|
16
|
+
Array.isArray(value)
|
|
17
|
+
? value
|
|
18
|
+
: Array.isArray(defaultValue)
|
|
19
|
+
? defaultValue
|
|
20
|
+
: [min, max],
|
|
21
|
+
[value, defaultValue, min, max]
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<DocrPrimitive.Root
|
|
26
|
+
data-slot="slider"
|
|
27
|
+
defaultValue={defaultValue}
|
|
28
|
+
value={value}
|
|
29
|
+
min={min}
|
|
30
|
+
max={max}
|
|
31
|
+
thumbAlignment="edge"
|
|
32
|
+
className={cn(
|
|
33
|
+
"group/slider relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col",
|
|
34
|
+
className
|
|
35
|
+
)}
|
|
36
|
+
{...props}
|
|
37
|
+
>
|
|
38
|
+
<DocrPrimitive.Control
|
|
39
|
+
data-slot="slider-control"
|
|
40
|
+
className="relative flex w-full grow items-center data-[orientation=vertical]:h-full data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col"
|
|
41
|
+
>
|
|
42
|
+
<DocrPrimitive.Track
|
|
43
|
+
data-slot="slider-track"
|
|
44
|
+
className={cn(
|
|
45
|
+
"relative grow overflow-hidden rounded-full bg-muted data-[orientation=horizontal]:h-[3px] data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-[3px]"
|
|
46
|
+
)}
|
|
47
|
+
>
|
|
48
|
+
<DocrPrimitive.Indicator
|
|
49
|
+
data-slot="slider-range"
|
|
50
|
+
className={cn(
|
|
51
|
+
"absolute bg-foreground data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full"
|
|
52
|
+
)}
|
|
53
|
+
/>
|
|
54
|
+
</DocrPrimitive.Track>
|
|
55
|
+
{Array.from({ length: _values.length }, (_, index) => (
|
|
56
|
+
<DocrPrimitive.Thumb
|
|
57
|
+
data-slot="slider-thumb"
|
|
58
|
+
index={index}
|
|
59
|
+
key={index}
|
|
60
|
+
className="block size-3.5 shrink-0 rounded-full border border-foreground bg-card shadow-edge transition-transform hover:scale-110 focus-visible:scale-110 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring/50 disabled:pointer-events-none disabled:opacity-50"
|
|
61
|
+
/>
|
|
62
|
+
))}
|
|
63
|
+
</DocrPrimitive.Control>
|
|
64
|
+
</DocrPrimitive.Root>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export { Slider }
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CircleCheckIcon,
|
|
3
|
+
InfoIcon,
|
|
4
|
+
Loader2Icon,
|
|
5
|
+
OctagonXIcon,
|
|
6
|
+
TriangleAlertIcon,
|
|
7
|
+
} from "lucide-react"
|
|
8
|
+
import { useTheme } from "next-themes"
|
|
9
|
+
import { Toaster as Sonner, type ToasterProps } from "sonner"
|
|
10
|
+
|
|
11
|
+
const Toaster = ({ ...props }: ToasterProps) => {
|
|
12
|
+
const { theme = "system" } = useTheme()
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<Sonner
|
|
16
|
+
theme={theme as ToasterProps["theme"]}
|
|
17
|
+
className="toaster group"
|
|
18
|
+
icons={{
|
|
19
|
+
success: <CircleCheckIcon className="size-4" />,
|
|
20
|
+
info: <InfoIcon className="size-4" />,
|
|
21
|
+
warning: <TriangleAlertIcon className="size-4" />,
|
|
22
|
+
error: <OctagonXIcon className="size-4" />,
|
|
23
|
+
loading: <Loader2Icon className="size-4 animate-spin" />,
|
|
24
|
+
}}
|
|
25
|
+
style={
|
|
26
|
+
{
|
|
27
|
+
"--normal-bg": "var(--popover)",
|
|
28
|
+
"--normal-text": "var(--popover-foreground)",
|
|
29
|
+
"--normal-border": "var(--border)",
|
|
30
|
+
"--border-radius": "8px",
|
|
31
|
+
"--font-family":
|
|
32
|
+
"Geist Variable, -apple-system, BlinkMacSystemFont, sans-serif",
|
|
33
|
+
} as React.CSSProperties
|
|
34
|
+
}
|
|
35
|
+
toastOptions={{
|
|
36
|
+
classNames: {
|
|
37
|
+
toast:
|
|
38
|
+
"!font-sans !text-[12.5px] !shadow-floating !border-border !rounded-[8px]",
|
|
39
|
+
title: "!font-medium !text-[12.5px] !tracking-tight",
|
|
40
|
+
description: "!text-[12px] !text-muted-foreground",
|
|
41
|
+
},
|
|
42
|
+
}}
|
|
43
|
+
{...props}
|
|
44
|
+
/>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export { Toaster }
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
3
|
+
import { Tabs as TabsPrimitive } from '@base-ui/react/tabs';
|
|
4
|
+
|
|
5
|
+
import { cn } from '@/lib/utils';
|
|
6
|
+
|
|
7
|
+
function Tabs({
|
|
8
|
+
className,
|
|
9
|
+
orientation = 'horizontal',
|
|
10
|
+
...props
|
|
11
|
+
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
|
|
12
|
+
return (
|
|
13
|
+
<TabsPrimitive.Root
|
|
14
|
+
data-slot="tabs"
|
|
15
|
+
data-orientation={orientation}
|
|
16
|
+
orientation={orientation}
|
|
17
|
+
className={cn('group/tabs flex gap-2 data-[orientation=horizontal]:flex-col', className)}
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const tabsListVariants = cva(
|
|
24
|
+
'group/tabs-list inline-flex w-fit items-center justify-center rounded-[6px] p-[2px] text-muted-foreground group-data-[orientation=horizontal]/tabs:h-7 group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none',
|
|
25
|
+
{
|
|
26
|
+
variants: {
|
|
27
|
+
variant: {
|
|
28
|
+
default: 'bg-muted/70 ring-1 ring-inset ring-border/60',
|
|
29
|
+
line: 'gap-1 bg-transparent',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
defaultVariants: {
|
|
33
|
+
variant: 'default',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
function TabsList({
|
|
39
|
+
className,
|
|
40
|
+
variant = 'default',
|
|
41
|
+
...props
|
|
42
|
+
}: React.ComponentProps<typeof TabsPrimitive.List> & VariantProps<typeof tabsListVariants>) {
|
|
43
|
+
return (
|
|
44
|
+
<TabsPrimitive.List
|
|
45
|
+
data-slot="tabs-list"
|
|
46
|
+
data-variant={variant}
|
|
47
|
+
className={cn(tabsListVariants({ variant }), className)}
|
|
48
|
+
{...props}
|
|
49
|
+
/>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function TabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Tab>) {
|
|
54
|
+
return (
|
|
55
|
+
<TabsPrimitive.Tab
|
|
56
|
+
data-slot="tabs-trigger"
|
|
57
|
+
className={cn(
|
|
58
|
+
"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-[5px] border border-transparent px-2.5 text-[12px] font-medium whitespace-nowrap text-foreground/55 transition-colors group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start hover:text-foreground focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
|
|
59
|
+
'group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent',
|
|
60
|
+
'data-active:bg-card data-active:text-foreground data-active:shadow-edge dark:data-active:bg-foreground/10',
|
|
61
|
+
'after:absolute after:bg-brand after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:-bottom-[6px] group-data-[orientation=horizontal]/tabs:after:h-[2px] group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100',
|
|
62
|
+
className,
|
|
63
|
+
)}
|
|
64
|
+
{...props}
|
|
65
|
+
/>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function TabsContent({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Panel>) {
|
|
70
|
+
return (
|
|
71
|
+
<TabsPrimitive.Panel
|
|
72
|
+
data-slot="tabs-content"
|
|
73
|
+
className={cn('flex-1 outline-none', className)}
|
|
74
|
+
{...props}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '@/lib/utils';
|
|
4
|
+
|
|
5
|
+
function Textarea({ className, ...props }: React.ComponentProps<'textarea'>) {
|
|
6
|
+
return (
|
|
7
|
+
<textarea
|
|
8
|
+
data-slot="textarea"
|
|
9
|
+
className={cn(
|
|
10
|
+
'flex field-sizing-content min-h-16 w-full rounded-[6px] border border-border bg-background px-2.5 py-2 text-[13px] leading-relaxed outline-none',
|
|
11
|
+
'transition-colors placeholder:text-muted-foreground/70',
|
|
12
|
+
'focus-visible:border-foreground/40 focus-visible:ring-2 focus-visible:ring-ring/30',
|
|
13
|
+
'disabled:cursor-not-allowed disabled:opacity-50',
|
|
14
|
+
'aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/25',
|
|
15
|
+
className,
|
|
16
|
+
)}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { Textarea };
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { type VariantProps } from "class-variance-authority"
|
|
5
|
+
import { ToggleGroup as ToggleGroupPrimitive } from "@base-ui/react/toggle-group"
|
|
6
|
+
import { Toggle as TogglePrimitive } from "@base-ui/react/toggle"
|
|
7
|
+
|
|
8
|
+
import { cn } from "@/lib/utils"
|
|
9
|
+
import { toggleVariants } from "@/components/ui/toggle"
|
|
10
|
+
|
|
11
|
+
const ToggleGroupContext = React.createContext<
|
|
12
|
+
VariantProps<typeof toggleVariants> & {
|
|
13
|
+
spacing?: number
|
|
14
|
+
}
|
|
15
|
+
>({
|
|
16
|
+
size: "default",
|
|
17
|
+
variant: "default",
|
|
18
|
+
spacing: 0,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
function ToggleGroup({
|
|
22
|
+
className,
|
|
23
|
+
variant,
|
|
24
|
+
size,
|
|
25
|
+
spacing = 0,
|
|
26
|
+
children,
|
|
27
|
+
...props
|
|
28
|
+
}: React.ComponentProps<typeof ToggleGroupPrimitive> &
|
|
29
|
+
VariantProps<typeof toggleVariants> & {
|
|
30
|
+
spacing?: number
|
|
31
|
+
}) {
|
|
32
|
+
return (
|
|
33
|
+
<ToggleGroupPrimitive
|
|
34
|
+
data-slot="toggle-group"
|
|
35
|
+
data-variant={variant}
|
|
36
|
+
data-size={size}
|
|
37
|
+
data-spacing={spacing}
|
|
38
|
+
style={{ "--gap": spacing } as React.CSSProperties}
|
|
39
|
+
className={cn(
|
|
40
|
+
"group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-[5px]",
|
|
41
|
+
className
|
|
42
|
+
)}
|
|
43
|
+
{...props}
|
|
44
|
+
>
|
|
45
|
+
<ToggleGroupContext.Provider value={{ variant, size, spacing }}>
|
|
46
|
+
{children}
|
|
47
|
+
</ToggleGroupContext.Provider>
|
|
48
|
+
</ToggleGroupPrimitive>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function ToggleGroupItem({
|
|
53
|
+
className,
|
|
54
|
+
children,
|
|
55
|
+
variant,
|
|
56
|
+
size,
|
|
57
|
+
...props
|
|
58
|
+
}: React.ComponentProps<typeof TogglePrimitive> &
|
|
59
|
+
VariantProps<typeof toggleVariants>) {
|
|
60
|
+
const context = React.useContext(ToggleGroupContext)
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<TogglePrimitive
|
|
64
|
+
data-slot="toggle-group-item"
|
|
65
|
+
data-variant={context.variant || variant}
|
|
66
|
+
data-size={context.size || size}
|
|
67
|
+
data-spacing={context.spacing}
|
|
68
|
+
className={cn(
|
|
69
|
+
toggleVariants({
|
|
70
|
+
variant: context.variant || variant,
|
|
71
|
+
size: context.size || size,
|
|
72
|
+
}),
|
|
73
|
+
"w-auto min-w-0 shrink-0 px-3 focus:z-10 focus-visible:z-10",
|
|
74
|
+
"data-[spacing=0]:rounded-none data-[spacing=0]:shadow-none data-[spacing=0]:first:rounded-l-[5px] data-[spacing=0]:last:rounded-r-[5px] data-[spacing=0]:data-[variant=outline]:border-l-0 data-[spacing=0]:data-[variant=outline]:first:border-l",
|
|
75
|
+
className
|
|
76
|
+
)}
|
|
77
|
+
{...props}
|
|
78
|
+
>
|
|
79
|
+
{children}
|
|
80
|
+
</TogglePrimitive>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export { ToggleGroup, ToggleGroupItem }
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { Toggle as TogglePrimitive } from "@base-ui/react/toggle"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
const toggleVariants = cva(
|
|
8
|
+
"inline-flex items-center justify-center gap-2 rounded-[5px] text-[12px] font-medium whitespace-nowrap outline-none transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/30 disabled:pointer-events-none disabled:opacity-50 data-pressed:bg-foreground data-pressed:text-background [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: "bg-transparent text-foreground/70",
|
|
13
|
+
outline:
|
|
14
|
+
"border border-border bg-card text-foreground/75 hover:border-foreground/20 data-pressed:border-foreground",
|
|
15
|
+
},
|
|
16
|
+
size: {
|
|
17
|
+
default: "h-8 min-w-8 px-2",
|
|
18
|
+
sm: "h-7 min-w-7 px-1.5",
|
|
19
|
+
lg: "h-9 min-w-9 px-2.5",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
defaultVariants: {
|
|
23
|
+
variant: "default",
|
|
24
|
+
size: "default",
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
function Toggle({
|
|
30
|
+
className,
|
|
31
|
+
variant,
|
|
32
|
+
size,
|
|
33
|
+
...props
|
|
34
|
+
}: React.ComponentProps<typeof TogglePrimitive> &
|
|
35
|
+
VariantProps<typeof toggleVariants>) {
|
|
36
|
+
return (
|
|
37
|
+
<TogglePrimitive
|
|
38
|
+
data-slot="toggle"
|
|
39
|
+
className={cn(toggleVariants({ variant, size, className }))}
|
|
40
|
+
{...props}
|
|
41
|
+
/>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { Toggle, toggleVariants }
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip"
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils"
|
|
5
|
+
|
|
6
|
+
function TooltipProvider({
|
|
7
|
+
delay = 0,
|
|
8
|
+
...props
|
|
9
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
|
|
10
|
+
return <TooltipPrimitive.Provider delay={delay} {...props} />
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function Tooltip({
|
|
14
|
+
...props
|
|
15
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
|
|
16
|
+
return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function TooltipTrigger({
|
|
20
|
+
...props
|
|
21
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
|
|
22
|
+
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function TooltipContent({
|
|
26
|
+
className,
|
|
27
|
+
side,
|
|
28
|
+
sideOffset = 0,
|
|
29
|
+
align,
|
|
30
|
+
alignOffset,
|
|
31
|
+
collisionPadding,
|
|
32
|
+
children,
|
|
33
|
+
container,
|
|
34
|
+
...props
|
|
35
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Popup> &
|
|
36
|
+
Pick<
|
|
37
|
+
React.ComponentProps<typeof TooltipPrimitive.Positioner>,
|
|
38
|
+
"side" | "sideOffset" | "align" | "alignOffset" | "collisionPadding"
|
|
39
|
+
> & {
|
|
40
|
+
container?: React.ComponentProps<typeof TooltipPrimitive.Portal>["container"]
|
|
41
|
+
}) {
|
|
42
|
+
return (
|
|
43
|
+
<TooltipPrimitive.Portal container={container}>
|
|
44
|
+
<TooltipPrimitive.Positioner
|
|
45
|
+
side={side}
|
|
46
|
+
sideOffset={sideOffset}
|
|
47
|
+
align={align}
|
|
48
|
+
alignOffset={alignOffset}
|
|
49
|
+
collisionPadding={collisionPadding}
|
|
50
|
+
className="isolate z-50"
|
|
51
|
+
>
|
|
52
|
+
<TooltipPrimitive.Popup
|
|
53
|
+
data-slot="tooltip-content"
|
|
54
|
+
className={cn(
|
|
55
|
+
"w-fit origin-(--transform-origin) rounded-md bg-foreground px-3 py-1.5 text-xs text-balance text-background transition-[transform,scale,opacity] data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0 data-[instant]:duration-0",
|
|
56
|
+
className
|
|
57
|
+
)}
|
|
58
|
+
{...props}
|
|
59
|
+
>
|
|
60
|
+
{children}
|
|
61
|
+
<TooltipPrimitive.Arrow
|
|
62
|
+
className={cn(
|
|
63
|
+
"size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-inherit",
|
|
64
|
+
"data-[side=top]:-bottom-2.5 data-[side=bottom]:top-1",
|
|
65
|
+
"data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2",
|
|
66
|
+
"data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2",
|
|
67
|
+
)}
|
|
68
|
+
/>
|
|
69
|
+
</TooltipPrimitive.Popup>
|
|
70
|
+
</TooltipPrimitive.Positioner>
|
|
71
|
+
</TooltipPrimitive.Portal>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
|
Binary file
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// Click-to-source inside the page frame. Every host element in page source
|
|
2
|
+
// carries `data-op-loc="<line>:<col>"` (loc-tags plugin, dev only); hovering
|
|
3
|
+
// outlines the innermost tagged element and a click reports it to the
|
|
4
|
+
// workspace window instead of reaching the page.
|
|
5
|
+
|
|
6
|
+
import type { FrameMessage } from '../lib/frame';
|
|
7
|
+
|
|
8
|
+
const LOC_ATTR = 'data-op-loc';
|
|
9
|
+
const TEXT_MAX = 120;
|
|
10
|
+
|
|
11
|
+
type Post = (msg: FrameMessage) => void;
|
|
12
|
+
|
|
13
|
+
function box(color: string, fill: string): HTMLDivElement {
|
|
14
|
+
const el = document.createElement('div');
|
|
15
|
+
el.style.cssText = [
|
|
16
|
+
'position:fixed',
|
|
17
|
+
'pointer-events:none',
|
|
18
|
+
'z-index:2147483647',
|
|
19
|
+
'border-radius:2px',
|
|
20
|
+
`border:1.5px solid ${color}`,
|
|
21
|
+
`background:${fill}`,
|
|
22
|
+
'display:none',
|
|
23
|
+
'box-sizing:border-box',
|
|
24
|
+
].join(';');
|
|
25
|
+
return el;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function installInspector(post: Post) {
|
|
29
|
+
const hover = box('rgba(59,130,246,0.85)', 'rgba(59,130,246,0.08)');
|
|
30
|
+
const selected = box('rgb(37,99,235)', 'rgba(37,99,235,0.14)');
|
|
31
|
+
document.documentElement.append(hover, selected);
|
|
32
|
+
|
|
33
|
+
let on = false;
|
|
34
|
+
let hoverEl: Element | null = null;
|
|
35
|
+
let selectedLoc: string | null = null;
|
|
36
|
+
let raf = 0;
|
|
37
|
+
|
|
38
|
+
const place = (el: Element | null, target: HTMLDivElement) => {
|
|
39
|
+
if (!el || !el.isConnected) {
|
|
40
|
+
target.style.display = 'none';
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const r = el.getBoundingClientRect();
|
|
44
|
+
target.style.display = 'block';
|
|
45
|
+
target.style.left = `${r.left - 2}px`;
|
|
46
|
+
target.style.top = `${r.top - 2}px`;
|
|
47
|
+
target.style.width = `${r.width + 4}px`;
|
|
48
|
+
target.style.height = `${r.height + 4}px`;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const findSelected = (): Element | null =>
|
|
52
|
+
selectedLoc ? document.querySelector(`[${LOC_ATTR}="${CSS.escape(selectedLoc)}"]`) : null;
|
|
53
|
+
|
|
54
|
+
const sync = () => {
|
|
55
|
+
raf = 0;
|
|
56
|
+
place(on ? hoverEl : null, hover);
|
|
57
|
+
place(findSelected(), selected);
|
|
58
|
+
};
|
|
59
|
+
const schedule = () => {
|
|
60
|
+
if (!raf) raf = requestAnimationFrame(sync);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const targetOf = (e: Event): Element | null => {
|
|
64
|
+
const t = e.target;
|
|
65
|
+
if (!(t instanceof Element)) return null;
|
|
66
|
+
return t.closest(`[${LOC_ATTR}]`);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const onMove = (e: MouseEvent) => {
|
|
70
|
+
if (!on) return;
|
|
71
|
+
const next = targetOf(e);
|
|
72
|
+
if (next !== hoverEl) {
|
|
73
|
+
hoverEl = next;
|
|
74
|
+
schedule();
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const onLeave = () => {
|
|
78
|
+
hoverEl = null;
|
|
79
|
+
schedule();
|
|
80
|
+
};
|
|
81
|
+
const onClick = (e: MouseEvent) => {
|
|
82
|
+
if (!on) return;
|
|
83
|
+
e.preventDefault();
|
|
84
|
+
e.stopPropagation();
|
|
85
|
+
const el = targetOf(e);
|
|
86
|
+
if (!el) return;
|
|
87
|
+
const loc = el.getAttribute(LOC_ATTR) ?? '';
|
|
88
|
+
selectedLoc = loc;
|
|
89
|
+
schedule();
|
|
90
|
+
post({
|
|
91
|
+
type: 'op:select',
|
|
92
|
+
loc,
|
|
93
|
+
tag: el.tagName.toLowerCase(),
|
|
94
|
+
text: (el.textContent ?? '').replace(/\s+/g, ' ').trim().slice(0, TEXT_MAX),
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
const swallow = (e: Event) => {
|
|
98
|
+
if (on) {
|
|
99
|
+
e.preventDefault();
|
|
100
|
+
e.stopPropagation();
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const onKey = (e: KeyboardEvent) => {
|
|
104
|
+
const tag = (e.target as HTMLElement | null)?.tagName;
|
|
105
|
+
if (
|
|
106
|
+
tag === 'INPUT' ||
|
|
107
|
+
tag === 'TEXTAREA' ||
|
|
108
|
+
(e.target as HTMLElement | null)?.isContentEditable
|
|
109
|
+
)
|
|
110
|
+
return;
|
|
111
|
+
if (e.key === 'i' || e.key === 'Escape') post({ type: 'op:key', key: e.key });
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
document.addEventListener('mousemove', onMove, true);
|
|
115
|
+
document.addEventListener('mouseleave', onLeave, true);
|
|
116
|
+
document.addEventListener('click', onClick, true);
|
|
117
|
+
document.addEventListener('mousedown', swallow, true);
|
|
118
|
+
document.addEventListener('pointerdown', swallow, true);
|
|
119
|
+
document.addEventListener('keydown', onKey, true);
|
|
120
|
+
window.addEventListener('scroll', schedule, true);
|
|
121
|
+
window.addEventListener('resize', schedule);
|
|
122
|
+
new MutationObserver(schedule).observe(document.documentElement, {
|
|
123
|
+
subtree: true,
|
|
124
|
+
childList: true,
|
|
125
|
+
attributes: true,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
setInspecting(next: boolean) {
|
|
130
|
+
on = next;
|
|
131
|
+
document.documentElement.style.cursor = on ? 'crosshair' : '';
|
|
132
|
+
if (!on) hoverEl = null;
|
|
133
|
+
schedule();
|
|
134
|
+
},
|
|
135
|
+
setSelected(loc: string | null) {
|
|
136
|
+
selectedLoc = loc;
|
|
137
|
+
schedule();
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import 'virtual:open-pages/pages.css';
|
|
2
|
+
import { loadPage } from 'virtual:open-pages/pages';
|
|
3
|
+
import { loadThemeDemo } from 'virtual:open-pages/themes';
|
|
4
|
+
import { createElement, StrictMode } from 'react';
|
|
5
|
+
import { createRoot } from 'react-dom/client';
|
|
6
|
+
import type { FrameMessage, WorkspaceMessage } from '../lib/frame';
|
|
7
|
+
import { pageChangeIncludes } from '../lib/pages';
|
|
8
|
+
import type { PageModule } from '../lib/sdk';
|
|
9
|
+
import { installInspector } from './inspect';
|
|
10
|
+
|
|
11
|
+
const params = new URLSearchParams(window.location.search);
|
|
12
|
+
const pageId = params.get('page');
|
|
13
|
+
const themeId = params.get('theme');
|
|
14
|
+
|
|
15
|
+
const embedded = window.parent !== window;
|
|
16
|
+
const post = (msg: FrameMessage) => {
|
|
17
|
+
if (embedded) window.parent.postMessage(msg, '*');
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// biome-ignore lint/style/noNonNullAssertion: #root is guaranteed by frame.html
|
|
21
|
+
const root = createRoot(document.getElementById('root')!);
|
|
22
|
+
|
|
23
|
+
function renderError(message: string) {
|
|
24
|
+
root.render(
|
|
25
|
+
createElement(
|
|
26
|
+
'pre',
|
|
27
|
+
{
|
|
28
|
+
style: {
|
|
29
|
+
margin: 0,
|
|
30
|
+
padding: '16px 20px',
|
|
31
|
+
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',
|
|
32
|
+
fontSize: 12,
|
|
33
|
+
lineHeight: 1.5,
|
|
34
|
+
color: '#b91c1c',
|
|
35
|
+
background: '#fef2f2',
|
|
36
|
+
whiteSpace: 'pre-wrap',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
message,
|
|
40
|
+
),
|
|
41
|
+
);
|
|
42
|
+
post({ type: 'op:error', message });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function load(): Promise<PageModule> {
|
|
46
|
+
if (themeId) return loadThemeDemo(themeId);
|
|
47
|
+
if (pageId) return loadPage(pageId);
|
|
48
|
+
throw new Error('frame.html needs ?page=<id> or ?theme=<id>');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let seq = 0;
|
|
52
|
+
async function mount() {
|
|
53
|
+
const my = ++seq;
|
|
54
|
+
try {
|
|
55
|
+
const mod = await load();
|
|
56
|
+
if (my !== seq) return;
|
|
57
|
+
if (typeof mod.default !== 'function') {
|
|
58
|
+
throw new Error(
|
|
59
|
+
`${themeId ? `themes/${themeId}.demo.tsx` : `pages/${pageId}/index.tsx`} must default-export a component. Got: ${typeof mod.default}`,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
const title = mod.meta?.title ?? pageId ?? themeId ?? '';
|
|
63
|
+
document.title = title;
|
|
64
|
+
root.render(createElement(StrictMode, null, createElement(mod.default)));
|
|
65
|
+
post({ type: 'op:ready', title });
|
|
66
|
+
} catch (e) {
|
|
67
|
+
if (my !== seq) return;
|
|
68
|
+
renderError(e instanceof Error ? (e.stack ?? e.message) : String(e));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
void mount();
|
|
73
|
+
|
|
74
|
+
if (import.meta.hot && pageId) {
|
|
75
|
+
import.meta.hot.on('open-pages:page-changed', (data: unknown) => {
|
|
76
|
+
if (!pageChangeIncludes(data, pageId)) return;
|
|
77
|
+
// The virtual module refreshes its cache-bust token on this same event.
|
|
78
|
+
queueMicrotask(() => void mount());
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (embedded && import.meta.env.DEV) {
|
|
83
|
+
const inspector = installInspector(post);
|
|
84
|
+
window.addEventListener('message', (event: MessageEvent<WorkspaceMessage>) => {
|
|
85
|
+
if (event.source !== window.parent) return;
|
|
86
|
+
const msg = event.data;
|
|
87
|
+
if (!msg || typeof msg !== 'object') return;
|
|
88
|
+
if (msg.type === 'op:inspect') inspector.setInspecting(msg.on);
|
|
89
|
+
else if (msg.type === 'op:select') inspector.setSelected(msg.loc);
|
|
90
|
+
});
|
|
91
|
+
}
|