@open-aippt/core 1.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +98 -0
  3. package/bin.js +2 -0
  4. package/dist/build-DxTqmvsO.js +17 -0
  5. package/dist/cli/bin.d.ts +1 -0
  6. package/dist/cli/bin.js +86 -0
  7. package/dist/config-CjzqjrEA.js +4280 -0
  8. package/dist/config-DIC-yVPp.d.ts +23 -0
  9. package/dist/design-cpzS8aud.js +35 -0
  10. package/dist/dev-BYuTeJbA.js +20 -0
  11. package/dist/format-BCeKbTOM.js +1605 -0
  12. package/dist/index.d.ts +134 -0
  13. package/dist/index.js +467 -0
  14. package/dist/locale/index.d.ts +24 -0
  15. package/dist/locale/index.js +3 -0
  16. package/dist/preview-DlQvnJPq.js +18 -0
  17. package/dist/sync-BPZ0m27m.js +139 -0
  18. package/dist/sync-EsYusbbL.js +3 -0
  19. package/dist/types-CHmFPIG_.d.ts +430 -0
  20. package/dist/vite/index.d.ts +14 -0
  21. package/dist/vite/index.js +4 -0
  22. package/env.d.ts +59 -0
  23. package/package.json +103 -0
  24. package/skills/apply-comments/SKILL.md +83 -0
  25. package/skills/create-slide/SKILL.md +91 -0
  26. package/skills/create-theme/SKILL.md +250 -0
  27. package/skills/current-slide/SKILL.md +110 -0
  28. package/skills/slide-authoring/SKILL.md +625 -0
  29. package/src/app/app.tsx +47 -0
  30. package/src/app/components/asset-view.tsx +966 -0
  31. package/src/app/components/history-provider.tsx +120 -0
  32. package/src/app/components/image-placeholder.tsx +243 -0
  33. package/src/app/components/inspector/asset-picker-dialog.tsx +196 -0
  34. package/src/app/components/inspector/comment-widget.tsx +93 -0
  35. package/src/app/components/inspector/image-crop-dialog.tsx +212 -0
  36. package/src/app/components/inspector/inspect-overlay.tsx +387 -0
  37. package/src/app/components/inspector/inspector-panel.tsx +1115 -0
  38. package/src/app/components/inspector/inspector-provider.tsx +1218 -0
  39. package/src/app/components/inspector/save-bar.tsx +48 -0
  40. package/src/app/components/language-toggle.tsx +39 -0
  41. package/src/app/components/notes-drawer.tsx +120 -0
  42. package/src/app/components/overview-grid.tsx +363 -0
  43. package/src/app/components/panel/panel-fields.tsx +60 -0
  44. package/src/app/components/panel/panel-shell.tsx +80 -0
  45. package/src/app/components/panel/save-card.tsx +142 -0
  46. package/src/app/components/pdf-progress-toast.tsx +32 -0
  47. package/src/app/components/player.tsx +466 -0
  48. package/src/app/components/pptx-progress-toast.tsx +32 -0
  49. package/src/app/components/present/blackout-overlay.tsx +18 -0
  50. package/src/app/components/present/control-bar.tsx +315 -0
  51. package/src/app/components/present/help-overlay.tsx +57 -0
  52. package/src/app/components/present/jump-input.tsx +74 -0
  53. package/src/app/components/present/laser-pointer.tsx +39 -0
  54. package/src/app/components/present/progress-bar.tsx +26 -0
  55. package/src/app/components/present/use-idle.ts +46 -0
  56. package/src/app/components/present/use-pointer-near-bottom.ts +34 -0
  57. package/src/app/components/present/use-presenter-channel.ts +66 -0
  58. package/src/app/components/present/use-touch-swipe.ts +66 -0
  59. package/src/app/components/shared-element.tsx +48 -0
  60. package/src/app/components/sidebar/folder-item.tsx +258 -0
  61. package/src/app/components/sidebar/icon-picker.tsx +61 -0
  62. package/src/app/components/sidebar/mobile-pill.tsx +34 -0
  63. package/src/app/components/sidebar/sidebar-footer.tsx +105 -0
  64. package/src/app/components/sidebar/sidebar.tsx +284 -0
  65. package/src/app/components/slide-canvas.tsx +102 -0
  66. package/src/app/components/slide-transition-layer.tsx +844 -0
  67. package/src/app/components/style-panel/design-provider.tsx +148 -0
  68. package/src/app/components/style-panel/style-panel.tsx +349 -0
  69. package/src/app/components/style-panel/use-design.ts +112 -0
  70. package/src/app/components/theme-toggle.tsx +59 -0
  71. package/src/app/components/themes/theme-detail.tsx +305 -0
  72. package/src/app/components/themes/themes-gallery.tsx +149 -0
  73. package/src/app/components/thumbnail-rail.tsx +805 -0
  74. package/src/app/components/ui/badge.tsx +45 -0
  75. package/src/app/components/ui/button.tsx +99 -0
  76. package/src/app/components/ui/card.tsx +92 -0
  77. package/src/app/components/ui/context-menu.tsx +237 -0
  78. package/src/app/components/ui/dialog.tsx +157 -0
  79. package/src/app/components/ui/dropdown-menu.tsx +245 -0
  80. package/src/app/components/ui/input.tsx +25 -0
  81. package/src/app/components/ui/label.tsx +24 -0
  82. package/src/app/components/ui/popover.tsx +75 -0
  83. package/src/app/components/ui/progress.tsx +31 -0
  84. package/src/app/components/ui/scroll-area.tsx +53 -0
  85. package/src/app/components/ui/select.tsx +196 -0
  86. package/src/app/components/ui/separator.tsx +28 -0
  87. package/src/app/components/ui/slider.tsx +61 -0
  88. package/src/app/components/ui/sonner.tsx +48 -0
  89. package/src/app/components/ui/tabs.tsx +79 -0
  90. package/src/app/components/ui/textarea.tsx +22 -0
  91. package/src/app/components/ui/toggle-group.tsx +83 -0
  92. package/src/app/components/ui/toggle.tsx +45 -0
  93. package/src/app/components/ui/tooltip.tsx +58 -0
  94. package/src/app/favicon.ico +0 -0
  95. package/src/app/index.html +13 -0
  96. package/src/app/lib/assets.ts +242 -0
  97. package/src/app/lib/design-presets.ts +94 -0
  98. package/src/app/lib/design.ts +58 -0
  99. package/src/app/lib/export-html.ts +326 -0
  100. package/src/app/lib/export-pdf.ts +298 -0
  101. package/src/app/lib/export-pptx.ts +284 -0
  102. package/src/app/lib/folders.ts +239 -0
  103. package/src/app/lib/inspector/fiber.test.ts +154 -0
  104. package/src/app/lib/inspector/fiber.ts +85 -0
  105. package/src/app/lib/inspector/use-comments.ts +74 -0
  106. package/src/app/lib/inspector/use-editor.ts +73 -0
  107. package/src/app/lib/inspector/use-notes.ts +134 -0
  108. package/src/app/lib/locale-store.ts +67 -0
  109. package/src/app/lib/page-context.tsx +38 -0
  110. package/src/app/lib/print-ready.test.ts +32 -0
  111. package/src/app/lib/print-ready.ts +51 -0
  112. package/src/app/lib/sdk.test.ts +13 -0
  113. package/src/app/lib/sdk.ts +37 -0
  114. package/src/app/lib/slides.ts +26 -0
  115. package/src/app/lib/step-context.tsx +261 -0
  116. package/src/app/lib/themes.ts +22 -0
  117. package/src/app/lib/transition.ts +30 -0
  118. package/src/app/lib/use-agent-socket.ts +18 -0
  119. package/src/app/lib/use-click-page-navigation.ts +60 -0
  120. package/src/app/lib/use-is-mobile.ts +21 -0
  121. package/src/app/lib/use-locale.ts +8 -0
  122. package/src/app/lib/use-prefers-reduced-motion.ts +19 -0
  123. package/src/app/lib/use-slide-module.ts +48 -0
  124. package/src/app/lib/use-wheel-page-navigation.ts +99 -0
  125. package/src/app/lib/utils.test.ts +25 -0
  126. package/src/app/lib/utils.ts +6 -0
  127. package/src/app/main.tsx +14 -0
  128. package/src/app/routes/assets.tsx +9 -0
  129. package/src/app/routes/home-shell.tsx +213 -0
  130. package/src/app/routes/home.tsx +807 -0
  131. package/src/app/routes/presenter.tsx +418 -0
  132. package/src/app/routes/slide.tsx +1108 -0
  133. package/src/app/routes/themes.tsx +34 -0
  134. package/src/app/styles.css +429 -0
  135. package/src/app/virtual.d.ts +51 -0
  136. package/src/locale/en.ts +416 -0
  137. package/src/locale/format.ts +12 -0
  138. package/src/locale/index.ts +6 -0
  139. package/src/locale/ja.ts +422 -0
  140. package/src/locale/types.ts +443 -0
  141. package/src/locale/zh-cn.ts +414 -0
  142. package/src/locale/zh-tw.ts +414 -0
@@ -0,0 +1,45 @@
1
+ import * as React from 'react';
2
+ import { cva, type VariantProps } from 'class-variance-authority';
3
+ import { Slot } from 'radix-ui';
4
+
5
+ import { cn } from '@/lib/utils';
6
+
7
+ const badgeVariants = cva(
8
+ 'group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!',
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: 'bg-primary text-primary-foreground [a]:hover:bg-primary/80',
13
+ secondary: 'bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80',
14
+ destructive:
15
+ 'bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20',
16
+ outline: 'border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground',
17
+ ghost: 'hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50',
18
+ link: 'text-primary underline-offset-4 hover:underline',
19
+ },
20
+ },
21
+ defaultVariants: {
22
+ variant: 'default',
23
+ },
24
+ },
25
+ );
26
+
27
+ function Badge({
28
+ className,
29
+ variant = 'default',
30
+ asChild = false,
31
+ ...props
32
+ }: React.ComponentProps<'span'> & VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
33
+ const Comp = asChild ? Slot.Root : 'span';
34
+
35
+ return (
36
+ <Comp
37
+ data-slot="badge"
38
+ data-variant={variant}
39
+ className={cn(badgeVariants({ variant }), className)}
40
+ {...props}
41
+ />
42
+ );
43
+ }
44
+
45
+ export { Badge, badgeVariants };
@@ -0,0 +1,99 @@
1
+ import * as React from 'react';
2
+ import { cva, type VariantProps } from 'class-variance-authority';
3
+ import { Slot } from 'radix-ui';
4
+
5
+ import { cn } from '@/lib/utils';
6
+
7
+ /*
8
+ * Editorial button. Tight square-ish radius, hairline borders instead of
9
+ * shadcn's default ring/shadow stack. The default variant is the strongest
10
+ * affordance — solid ink with subtle inner highlight on hover so the press
11
+ * feels physical without glow.
12
+ */
13
+ const buttonVariants = cva(
14
+ [
15
+ "group/button relative inline-flex shrink-0 items-center justify-center",
16
+ "rounded-[6px] text-[13px] font-medium whitespace-nowrap select-none",
17
+ "outline-none transition-[background-color,color,border-color,box-shadow,transform] duration-100",
18
+ "focus-visible:ring-2 focus-visible:ring-ring/40 focus-visible:ring-offset-1 focus-visible:ring-offset-background",
19
+ "active:not-aria-[haspopup]:translate-y-px",
20
+ "disabled:pointer-events-none disabled:opacity-45",
21
+ "aria-invalid:border-destructive aria-invalid:ring-destructive/30",
22
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
23
+ ].join(' '),
24
+ {
25
+ variants: {
26
+ variant: {
27
+ default: [
28
+ 'bg-foreground text-background',
29
+ 'shadow-[inset_0_1px_0_oklch(1_0_0/0.12),0_1px_0_oklch(0_0_0/0.12)]',
30
+ 'hover:bg-foreground/90',
31
+ 'aria-expanded:bg-foreground/85',
32
+ ].join(' '),
33
+ brand: [
34
+ 'bg-brand text-brand-foreground',
35
+ 'shadow-[inset_0_1px_0_oklch(1_0_0/0.18),0_1px_0_oklch(0_0_0/0.16)]',
36
+ 'hover:brightness-105 active:brightness-95',
37
+ ].join(' '),
38
+ outline: [
39
+ 'border border-border bg-card text-foreground',
40
+ 'hover:bg-muted/60 hover:border-foreground/20',
41
+ 'aria-expanded:bg-muted aria-expanded:border-foreground/25',
42
+ 'data-[state=on]:bg-foreground data-[state=on]:text-background data-[state=on]:border-foreground',
43
+ ].join(' '),
44
+ secondary:
45
+ 'bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary/90',
46
+ ghost: [
47
+ 'text-foreground/75 hover:text-foreground hover:bg-muted',
48
+ 'aria-expanded:bg-muted aria-expanded:text-foreground',
49
+ ].join(' '),
50
+ destructive: [
51
+ 'bg-destructive text-white',
52
+ 'shadow-[inset_0_1px_0_oklch(1_0_0/0.16),0_1px_0_oklch(0_0_0/0.12)]',
53
+ 'hover:brightness-105 active:brightness-95',
54
+ 'focus-visible:ring-destructive/35',
55
+ ].join(' '),
56
+ link: 'text-foreground underline decoration-foreground/30 decoration-1 underline-offset-[3px] hover:decoration-foreground/70 [&_svg]:hidden',
57
+ },
58
+ size: {
59
+ default: 'h-8 gap-1.5 px-3',
60
+ xs: 'h-6 gap-1 rounded-[5px] px-2 text-[11.5px]',
61
+ sm: 'h-7 gap-1.5 rounded-[5px] px-2.5 text-[12px]',
62
+ lg: 'h-9 gap-1.5 px-3.5 text-[13.5px]',
63
+ icon: 'size-8',
64
+ 'icon-xs': 'size-6 rounded-[5px]',
65
+ 'icon-sm': 'size-7 rounded-[5px]',
66
+ 'icon-lg': 'size-9',
67
+ },
68
+ },
69
+ defaultVariants: {
70
+ variant: 'default',
71
+ size: 'default',
72
+ },
73
+ },
74
+ );
75
+
76
+ function Button({
77
+ className,
78
+ variant = 'default',
79
+ size = 'default',
80
+ asChild = false,
81
+ ...props
82
+ }: React.ComponentProps<'button'> &
83
+ VariantProps<typeof buttonVariants> & {
84
+ asChild?: boolean;
85
+ }) {
86
+ const Comp = asChild ? Slot.Root : 'button';
87
+
88
+ return (
89
+ <Comp
90
+ data-slot="button"
91
+ data-variant={variant}
92
+ data-size={size}
93
+ className={cn(buttonVariants({ variant, size, className }))}
94
+ {...props}
95
+ />
96
+ );
97
+ }
98
+
99
+ export { Button, buttonVariants };
@@ -0,0 +1,92 @@
1
+ import * as React from 'react';
2
+
3
+ import { cn } from '@/lib/utils';
4
+
5
+ function Card({
6
+ className,
7
+ size = 'default',
8
+ ...props
9
+ }: React.ComponentProps<'div'> & { size?: 'default' | 'sm' }) {
10
+ return (
11
+ <div
12
+ data-slot="card"
13
+ data-size={size}
14
+ className={cn(
15
+ 'group/card flex flex-col gap-4 overflow-hidden rounded-[10px] bg-card py-4 text-sm text-card-foreground border border-border has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-[10px] *:[img:last-child]:rounded-b-[10px]',
16
+ className,
17
+ )}
18
+ {...props}
19
+ />
20
+ );
21
+ }
22
+
23
+ function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
24
+ return (
25
+ <div
26
+ data-slot="card-header"
27
+ className={cn(
28
+ 'group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3',
29
+ className,
30
+ )}
31
+ {...props}
32
+ />
33
+ );
34
+ }
35
+
36
+ function CardTitle({ className, ...props }: React.ComponentProps<'div'>) {
37
+ return (
38
+ <div
39
+ data-slot="card-title"
40
+ className={cn(
41
+ 'font-heading text-base leading-snug font-medium group-data-[size=sm]/card:text-sm',
42
+ className,
43
+ )}
44
+ {...props}
45
+ />
46
+ );
47
+ }
48
+
49
+ function CardDescription({ className, ...props }: React.ComponentProps<'div'>) {
50
+ return (
51
+ <div
52
+ data-slot="card-description"
53
+ className={cn('text-sm text-muted-foreground', className)}
54
+ {...props}
55
+ />
56
+ );
57
+ }
58
+
59
+ function CardAction({ className, ...props }: React.ComponentProps<'div'>) {
60
+ return (
61
+ <div
62
+ data-slot="card-action"
63
+ className={cn('col-start-2 row-span-2 row-start-1 self-start justify-self-end', className)}
64
+ {...props}
65
+ />
66
+ );
67
+ }
68
+
69
+ function CardContent({ className, ...props }: React.ComponentProps<'div'>) {
70
+ return (
71
+ <div
72
+ data-slot="card-content"
73
+ className={cn('px-4 group-data-[size=sm]/card:px-3', className)}
74
+ {...props}
75
+ />
76
+ );
77
+ }
78
+
79
+ function CardFooter({ className, ...props }: React.ComponentProps<'div'>) {
80
+ return (
81
+ <div
82
+ data-slot="card-footer"
83
+ className={cn(
84
+ 'flex items-center rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/card:p-3',
85
+ className,
86
+ )}
87
+ {...props}
88
+ />
89
+ );
90
+ }
91
+
92
+ export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent };
@@ -0,0 +1,237 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react';
5
+ import { ContextMenu as ContextMenuPrimitive } from 'radix-ui';
6
+
7
+ import { cn } from '@/lib/utils';
8
+
9
+ function ContextMenu({ ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Root>) {
10
+ return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />;
11
+ }
12
+
13
+ function ContextMenuTrigger({
14
+ ...props
15
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) {
16
+ return <ContextMenuPrimitive.Trigger data-slot="context-menu-trigger" {...props} />;
17
+ }
18
+
19
+ function ContextMenuGroup({ ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Group>) {
20
+ return <ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} />;
21
+ }
22
+
23
+ function ContextMenuPortal({ ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) {
24
+ return <ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />;
25
+ }
26
+
27
+ function ContextMenuSub({ ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) {
28
+ return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />;
29
+ }
30
+
31
+ function ContextMenuRadioGroup({
32
+ ...props
33
+ }: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>) {
34
+ return <ContextMenuPrimitive.RadioGroup data-slot="context-menu-radio-group" {...props} />;
35
+ }
36
+
37
+ function ContextMenuSubTrigger({
38
+ className,
39
+ inset,
40
+ children,
41
+ ...props
42
+ }: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & {
43
+ inset?: boolean;
44
+ }) {
45
+ return (
46
+ <ContextMenuPrimitive.SubTrigger
47
+ data-slot="context-menu-sub-trigger"
48
+ data-inset={inset}
49
+ className={cn(
50
+ 'flex cursor-default items-center gap-2 rounded-[5px] px-2 py-1.5 text-[12.5px] outline-hidden select-none focus:bg-foreground focus:text-background data-[inset]:pl-8 data-[state=open]:bg-muted',
51
+ className,
52
+ )}
53
+ {...props}
54
+ >
55
+ {children}
56
+ <ChevronRightIcon className="ml-auto size-3.5 opacity-60" />
57
+ </ContextMenuPrimitive.SubTrigger>
58
+ );
59
+ }
60
+
61
+ function ContextMenuSubContent({
62
+ className,
63
+ ...props
64
+ }: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) {
65
+ return (
66
+ <ContextMenuPrimitive.SubContent
67
+ data-slot="context-menu-sub-content"
68
+ className={cn(
69
+ 'z-50 min-w-[9rem] overflow-hidden rounded-[8px] border border-border bg-popover p-1 text-popover-foreground shadow-floating',
70
+ 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
71
+ 'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
72
+ className,
73
+ )}
74
+ {...props}
75
+ />
76
+ );
77
+ }
78
+
79
+ function ContextMenuContent({
80
+ className,
81
+ ...props
82
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Content>) {
83
+ return (
84
+ <ContextMenuPrimitive.Portal>
85
+ <ContextMenuPrimitive.Content
86
+ data-slot="context-menu-content"
87
+ className={cn(
88
+ 'z-50 max-h-(--radix-context-menu-content-available-height) min-w-[9rem] origin-(--radix-context-menu-content-transform-origin)',
89
+ 'overflow-x-hidden overflow-y-auto rounded-[8px] border border-border bg-popover p-1 text-popover-foreground shadow-floating',
90
+ 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
91
+ 'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
92
+ className,
93
+ )}
94
+ {...props}
95
+ />
96
+ </ContextMenuPrimitive.Portal>
97
+ );
98
+ }
99
+
100
+ function ContextMenuItem({
101
+ className,
102
+ inset,
103
+ variant = 'default',
104
+ ...props
105
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Item> & {
106
+ inset?: boolean;
107
+ variant?: 'default' | 'destructive';
108
+ }) {
109
+ return (
110
+ <ContextMenuPrimitive.Item
111
+ data-slot="context-menu-item"
112
+ data-inset={inset}
113
+ data-variant={variant}
114
+ className={cn(
115
+ 'relative flex cursor-default items-center gap-2 rounded-[5px] px-2 py-1.5 text-[12.5px] outline-hidden select-none transition-colors',
116
+ 'focus:bg-foreground focus:text-background',
117
+ 'data-[active=true]:bg-muted data-[active=true]:text-foreground',
118
+ 'data-[disabled]:pointer-events-none data-[disabled]:opacity-45 data-[inset]:pl-8',
119
+ 'data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive data-[variant=destructive]:focus:text-white',
120
+ "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5 [&_svg:not([class*='text-'])]:text-current [&_svg]:opacity-80",
121
+ className,
122
+ )}
123
+ {...props}
124
+ />
125
+ );
126
+ }
127
+
128
+ function ContextMenuCheckboxItem({
129
+ className,
130
+ children,
131
+ checked,
132
+ ...props
133
+ }: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>) {
134
+ return (
135
+ <ContextMenuPrimitive.CheckboxItem
136
+ data-slot="context-menu-checkbox-item"
137
+ className={cn(
138
+ 'relative flex cursor-default items-center gap-2 rounded-[5px] py-1.5 pr-2 pl-8 text-[12.5px] outline-hidden select-none focus:bg-foreground focus:text-background data-[disabled]:pointer-events-none data-[disabled]:opacity-45',
139
+ className,
140
+ )}
141
+ checked={checked}
142
+ {...props}
143
+ >
144
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
145
+ <ContextMenuPrimitive.ItemIndicator>
146
+ <CheckIcon className="size-3.5" />
147
+ </ContextMenuPrimitive.ItemIndicator>
148
+ </span>
149
+ {children}
150
+ </ContextMenuPrimitive.CheckboxItem>
151
+ );
152
+ }
153
+
154
+ function ContextMenuRadioItem({
155
+ className,
156
+ children,
157
+ ...props
158
+ }: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>) {
159
+ return (
160
+ <ContextMenuPrimitive.RadioItem
161
+ data-slot="context-menu-radio-item"
162
+ className={cn(
163
+ 'relative flex cursor-default items-center gap-2 rounded-[5px] py-1.5 pr-2 pl-8 text-[12.5px] outline-hidden select-none focus:bg-foreground focus:text-background data-[disabled]:pointer-events-none data-[disabled]:opacity-45',
164
+ className,
165
+ )}
166
+ {...props}
167
+ >
168
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
169
+ <ContextMenuPrimitive.ItemIndicator>
170
+ <CircleIcon className="size-2 fill-current" />
171
+ </ContextMenuPrimitive.ItemIndicator>
172
+ </span>
173
+ {children}
174
+ </ContextMenuPrimitive.RadioItem>
175
+ );
176
+ }
177
+
178
+ function ContextMenuLabel({
179
+ className,
180
+ inset,
181
+ ...props
182
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Label> & {
183
+ inset?: boolean;
184
+ }) {
185
+ return (
186
+ <ContextMenuPrimitive.Label
187
+ data-slot="context-menu-label"
188
+ data-inset={inset}
189
+ className={cn('eyebrow px-2 py-1.5 data-[inset]:pl-8', className)}
190
+ {...props}
191
+ />
192
+ );
193
+ }
194
+
195
+ function ContextMenuSeparator({
196
+ className,
197
+ ...props
198
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) {
199
+ return (
200
+ <ContextMenuPrimitive.Separator
201
+ data-slot="context-menu-separator"
202
+ className={cn('-mx-1 my-1 h-px bg-hairline', className)}
203
+ {...props}
204
+ />
205
+ );
206
+ }
207
+
208
+ function ContextMenuShortcut({ className, ...props }: React.ComponentProps<'span'>) {
209
+ return (
210
+ <span
211
+ data-slot="context-menu-shortcut"
212
+ className={cn(
213
+ 'ml-auto font-mono text-[10.5px] tracking-[0.06em] text-muted-foreground/80',
214
+ className,
215
+ )}
216
+ {...props}
217
+ />
218
+ );
219
+ }
220
+
221
+ export {
222
+ ContextMenu,
223
+ ContextMenuTrigger,
224
+ ContextMenuContent,
225
+ ContextMenuItem,
226
+ ContextMenuCheckboxItem,
227
+ ContextMenuRadioItem,
228
+ ContextMenuLabel,
229
+ ContextMenuSeparator,
230
+ ContextMenuShortcut,
231
+ ContextMenuGroup,
232
+ ContextMenuPortal,
233
+ ContextMenuSub,
234
+ ContextMenuSubContent,
235
+ ContextMenuSubTrigger,
236
+ ContextMenuRadioGroup,
237
+ };
@@ -0,0 +1,157 @@
1
+ import { XIcon } from 'lucide-react';
2
+ import { Dialog as DialogPrimitive } from 'radix-ui';
3
+ import type * as React from 'react';
4
+ import { Button } from '@/components/ui/button';
5
+ import { cn } from '@/lib/utils';
6
+
7
+ function Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {
8
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />;
9
+ }
10
+
11
+ function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
12
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
13
+ }
14
+
15
+ function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
16
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
17
+ }
18
+
19
+ function DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {
20
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
21
+ }
22
+
23
+ function DialogOverlay({
24
+ className,
25
+ ...props
26
+ }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
27
+ return (
28
+ <DialogPrimitive.Overlay
29
+ data-slot="dialog-overlay"
30
+ className={cn(
31
+ 'fixed inset-0 z-50 bg-foreground/35 backdrop-blur-[2px] data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0',
32
+ className,
33
+ )}
34
+ {...props}
35
+ />
36
+ );
37
+ }
38
+
39
+ function DialogContent({
40
+ className,
41
+ children,
42
+ showCloseButton = true,
43
+ container,
44
+ ...props
45
+ }: React.ComponentProps<typeof DialogPrimitive.Content> & {
46
+ showCloseButton?: boolean;
47
+ container?: React.ComponentProps<typeof DialogPrimitive.Portal>['container'];
48
+ }) {
49
+ return (
50
+ <DialogPortal data-slot="dialog-portal" container={container}>
51
+ <DialogOverlay />
52
+ <DialogPrimitive.Content
53
+ data-slot="dialog-content"
54
+ className={cn(
55
+ // Crisp paper card with hairline edge + soft drop. No oversized
56
+ // shadcn-glow ring; sits cleanly on the dimmed canvas.
57
+ 'fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-5',
58
+ 'rounded-[10px] border border-border bg-card p-6 text-card-foreground',
59
+ 'shadow-overlay outline-none',
60
+ 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
61
+ 'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
62
+ 'duration-200 sm:max-w-md',
63
+ className,
64
+ )}
65
+ {...props}
66
+ >
67
+ {children}
68
+ {showCloseButton && (
69
+ <DialogPrimitive.Close
70
+ data-slot="dialog-close"
71
+ aria-label="Close"
72
+ className="absolute top-3.5 right-3.5 inline-flex size-7 items-center justify-center rounded-[5px] text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40"
73
+ >
74
+ <XIcon className="size-3.5" />
75
+ <span className="sr-only">Close</span>
76
+ </DialogPrimitive.Close>
77
+ )}
78
+ </DialogPrimitive.Content>
79
+ </DialogPortal>
80
+ );
81
+ }
82
+
83
+ function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
84
+ return (
85
+ <div
86
+ data-slot="dialog-header"
87
+ className={cn('flex flex-col gap-1.5 text-left', className)}
88
+ {...props}
89
+ />
90
+ );
91
+ }
92
+
93
+ function DialogFooter({
94
+ className,
95
+ showCloseButton = false,
96
+ children,
97
+ ...props
98
+ }: React.ComponentProps<'div'> & {
99
+ showCloseButton?: boolean;
100
+ }) {
101
+ return (
102
+ <div
103
+ data-slot="dialog-footer"
104
+ className={cn(
105
+ 'flex flex-col-reverse gap-2 pt-2 sm:flex-row sm:justify-end sm:gap-1.5',
106
+ className,
107
+ )}
108
+ {...props}
109
+ >
110
+ {children}
111
+ {showCloseButton && (
112
+ <DialogPrimitive.Close asChild>
113
+ <Button variant="outline">Close</Button>
114
+ </DialogPrimitive.Close>
115
+ )}
116
+ </div>
117
+ );
118
+ }
119
+
120
+ function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {
121
+ return (
122
+ <DialogPrimitive.Title
123
+ data-slot="dialog-title"
124
+ className={cn(
125
+ 'font-heading text-[15px] font-semibold leading-tight tracking-tight text-foreground',
126
+ className,
127
+ )}
128
+ {...props}
129
+ />
130
+ );
131
+ }
132
+
133
+ function DialogDescription({
134
+ className,
135
+ ...props
136
+ }: React.ComponentProps<typeof DialogPrimitive.Description>) {
137
+ return (
138
+ <DialogPrimitive.Description
139
+ data-slot="dialog-description"
140
+ className={cn('text-[13px] leading-relaxed text-muted-foreground', className)}
141
+ {...props}
142
+ />
143
+ );
144
+ }
145
+
146
+ export {
147
+ Dialog,
148
+ DialogClose,
149
+ DialogContent,
150
+ DialogDescription,
151
+ DialogFooter,
152
+ DialogHeader,
153
+ DialogOverlay,
154
+ DialogPortal,
155
+ DialogTitle,
156
+ DialogTrigger,
157
+ };