Skip to content

Commit c52f3b7

Browse files
Merge pull request #485 from gridaco/canary
Daily RC - Main Menu & Actions
2 parents 2267c1e + 52d8128 commit c52f3b7

File tree

29 files changed

+5350
-899
lines changed

29 files changed

+5350
-899
lines changed

docs/editor/shortcuts/index.md

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ Last updated: Based on keybindings_sheet array and useHotkeys calls in hotkeys.t
2020
<!-- | Command | ⌘ | — | ❌ No | ⌘ (macOS only) | -->
2121
<!-- | Control | ⌃ | Ctrl | ❌ No | Ctrl (Windows), ⌃ (macOS) | -->
2222
<!-- | Option / Alt | ⌥ | Alt | ❌ No | Alt (Windows), ⌥ (macOS) | -->
23-
<!-- | Shift | ⇧ | | ✅ Yes | ⇧ (both) | -->
24-
<!-- | Enter / Return | ⏎ / ↵ | | ⚠️ Mostly | ↵ or Enter | -->
23+
<!-- | Shift | ⇧ | Shift | ❌ No | Shift (Windows), ⇧ (macOS) | -->
24+
<!-- | Enter / Return | ⏎ / ↵ | Enter | ❌ No | Enter (Windows), ↵ (macOS) | -->
2525
<!-- | Escape | ⎋ | Esc | ⚠️ Mixed | Esc | -->
2626
<!-- | Backspace / Delete | ⌫ / ⌦ | Backspace / Del | ❌ No | Text label | -->
2727

@@ -46,7 +46,7 @@ Last updated: Based on keybindings_sheet array and useHotkeys calls in hotkeys.t
4646
| Eraser tool | `E` | `E` | Eraser tool |
4747
| Paint bucket | `G` | `G` | Flood fill tool (bitmap mode only) |
4848
| Variable width | `⇧ + W` | `⇧ + W` | Variable width tool (vector mode only) |
49-
| Eye dropper | `I` | `I` | Pick color from screen |
49+
| Eye dropper | `I` or `⌃ + C` | `I` | Pick color from screen |
5050

5151
## Selection & Navigation
5252

@@ -88,6 +88,8 @@ Last updated: Based on keybindings_sheet array and useHotkeys calls in hotkeys.t
8888
| Nudge resize (down, 10px) | `Ctrl + ⌥ + ⇧ + ↓` | `Ctrl + Alt + ⇧ + ↓` | Resize selection height by 10px |
8989
| Move to front | `]` | `]` | Move selection to front (or increase brush size if brush tool active) |
9090
| Move to back | `[` | `[` | Move selection to back (or decrease brush size if brush tool active) |
91+
| Move forward | `⌘ + ]` | `Ctrl + ]` | Move selection forward one layer |
92+
| Move backward | `⌘ + [` | `Ctrl + [` | Move selection backward one layer |
9193

9294
## Alignment & Distribution
9395

@@ -113,32 +115,45 @@ Last updated: Based on keybindings_sheet array and useHotkeys calls in hotkeys.t
113115

114116
## Text Formatting
115117

116-
| Action | macOS | Windows/Linux | Description |
117-
| ------------------- | ----------- | -------------- | ------------------------- |
118-
| Toggle bold | `⌘ + B` | `Ctrl + B` | Toggle bold style |
119-
| Toggle italic | `⌘ + I` | `Ctrl + I` | Toggle italic style |
120-
| Toggle underline | `⌘ + U` | `Ctrl + U` | Toggle underline style |
121-
| Toggle line-through | `⌘ + ⇧ + X` | `Ctrl + ⇧ + X` | Toggle line-through style |
122-
| Increase font size | `⌘ + ⇧ + >` | `Ctrl + ⇧ + >` | Increase font size by 1px |
123-
| Decrease font size | `⌘ + ⇧ + <` | `Ctrl + ⇧ + <` | Decrease font size by 1px |
118+
| Action | macOS | Windows/Linux | Description |
119+
| ----------------------- | ----------- | ---------------- | ------------------------- |
120+
| Toggle bold | `⌘ + B` | `Ctrl + B` | Toggle bold style |
121+
| Toggle italic | `⌘ + I` | `Ctrl + I` | Toggle italic style |
122+
| Toggle underline | `⌘ + U` | `Ctrl + U` | Toggle underline style |
123+
| Toggle line-through | `⌘ + ⇧ + X` | `Ctrl + ⇧ + X` | Toggle line-through style |
124+
| Text align left | `⌘ + ⌥ + L` | `Ctrl + Alt + L` | Align text to the left |
125+
| Text align center | `⌘ + ⌥ + T` | `Ctrl + Alt + T` | Center text horizontally |
126+
| Text align right | `⌘ + ⌥ + R` | `Ctrl + Alt + R` | Align text to the right |
127+
| Text align justify | `⌘ + ⌥ + J` | `Ctrl + Alt + J` | Justify text horizontally |
128+
| Increase font size | `⌘ + ⇧ + >` | `Ctrl + ⇧ + >` | Increase font size by 1px |
129+
| Decrease font size | `⌘ + ⇧ + <` | `Ctrl + ⇧ + <` | Decrease font size by 1px |
130+
| Increase font weight | `⌘ + ⌥ + >` | `Ctrl + Alt + >` | Increase font weight |
131+
| Decrease font weight | `⌘ + ⌥ + <` | `Ctrl + Alt + <` | Decrease font weight |
132+
| Increase line height | `⌥ + ⇧ + >` | `Alt + ⇧ + >` | Increase line height |
133+
| Decrease line height | `⌥ + ⇧ + <` | `Alt + ⇧ + <` | Decrease line height |
134+
| Increase letter spacing | `⌥ + >` | `Alt + >` | Increase letter spacing |
135+
| Decrease letter spacing | `⌥ + <` | `Alt + <` | Decrease letter spacing |
124136

125137
## Object Properties
126138

127-
| Action | macOS | Windows/Linux | Description |
128-
| ------------------- | ------------------ | ------------------ | ------------------------------------- |
129-
| Toggle active | `⌘ + ⇧ + H` | `Ctrl + ⇧ + H` | Toggle active state for the selection |
130-
| Toggle locked | `⌘ + ⇧ + L` | `Ctrl + ⇧ + L` | Toggle locked state for the selection |
131-
| Set opacity to 0% | `0` (double press) | `0` (double press) | Set opacity to 0% |
132-
| Set opacity to 10% | `1` | `1` | Set opacity to 10% |
133-
| Set opacity to 20% | `2` | `2` | Set opacity to 20% |
134-
| Set opacity to 30% | `3` | `3` | Set opacity to 30% |
135-
| Set opacity to 40% | `4` | `4` | Set opacity to 40% |
136-
| Set opacity to 50% | `5` | `5` | Set opacity to 50% |
137-
| Set opacity to 60% | `6` | `6` | Set opacity to 60% |
138-
| Set opacity to 70% | `7` | `7` | Set opacity to 70% |
139-
| Set opacity to 80% | `8` | `8` | Set opacity to 80% |
140-
| Set opacity to 90% | `9` | `9` | Set opacity to 90% |
141-
| Set opacity to 100% | `0` (single press) | `0` (single press) | Set opacity to 100% |
139+
| Action | macOS | Windows/Linux | Description |
140+
| -------------------- | ------------------ | ------------------ | ---------------------------------------------- |
141+
| Toggle active | `⌘ + ⇧ + H` | `Ctrl + ⇧ + H` | Toggle active state for the selection |
142+
| Toggle locked | `⌘ + ⇧ + L` | `Ctrl + ⇧ + L` | Toggle locked state for the selection |
143+
| Remove fill | `⌥ + /` | `Alt + /` | Remove fill from selection |
144+
| Remove stroke | `⇧ + /` | `⇧ + /` | Remove stroke from selection (sets width to 0) |
145+
| Swap fill and stroke | `⇧ + X` | `⇧ + X` | Swap fill paints and stroke paints |
146+
| Set opacity to 0% | `0` (double press) | `0` (double press) | Set opacity to 0% |
147+
| Set opacity to 10% | `1` | `1` | Set opacity to 10% |
148+
| Set opacity to 20% | `2` | `2` | Set opacity to 20% |
149+
| Set opacity to 30% | `3` | `3` | Set opacity to 30% |
150+
| Set opacity to 40% | `4` | `4` | Set opacity to 40% |
151+
| Set opacity to 50% | `5` | `5` | Set opacity to 50% |
152+
| Set opacity to 60% | `6` | `6` | Set opacity to 60% |
153+
| Set opacity to 70% | `7` | `7` | Set opacity to 70% |
154+
| Set opacity to 80% | `8` | `8` | Set opacity to 80% |
155+
| Set opacity to 90% | `9` | `9` | Set opacity to 90% |
156+
| Set opacity to 100% | `0` (single press) | `0` (single press) | Set opacity to 100% |
142157

143158
## View & Zoom
144159

@@ -179,3 +194,5 @@ The following shortcuts are defined but not yet implemented:
179194
- `⇧ + V` - Flip vertical
180195
- `⌥ + ⌘ + K` / `Alt + Ctrl + K` - Create component
181196
- `⌥ + ⌘ + B` / `Alt + Ctrl + B` - Eject component
197+
- `Tab` - Text range: Increase indentation
198+
- `⇧ + Tab` - Text range: Decrease indentation

editor/components/ui/field.tsx

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
"use client"
2+
3+
import { useMemo } from "react"
4+
import { cva, type VariantProps } from "class-variance-authority"
5+
6+
import { cn } from "@/components/lib/utils/index"
7+
import { Label } from "@/components/ui/label"
8+
import { Separator } from "@/components/ui/separator"
9+
10+
function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
11+
return (
12+
<fieldset
13+
data-slot="field-set"
14+
className={cn(
15+
"flex flex-col gap-6",
16+
"has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
17+
className
18+
)}
19+
{...props}
20+
/>
21+
)
22+
}
23+
24+
function FieldLegend({
25+
className,
26+
variant = "legend",
27+
...props
28+
}: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) {
29+
return (
30+
<legend
31+
data-slot="field-legend"
32+
data-variant={variant}
33+
className={cn(
34+
"mb-3 font-medium",
35+
"data-[variant=legend]:text-base",
36+
"data-[variant=label]:text-sm",
37+
className
38+
)}
39+
{...props}
40+
/>
41+
)
42+
}
43+
44+
function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
45+
return (
46+
<div
47+
data-slot="field-group"
48+
className={cn(
49+
"group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4",
50+
className
51+
)}
52+
{...props}
53+
/>
54+
)
55+
}
56+
57+
const fieldVariants = cva(
58+
"group/field data-[invalid=true]:text-destructive flex w-full gap-3",
59+
{
60+
variants: {
61+
orientation: {
62+
vertical: ["flex-col [&>*]:w-full [&>.sr-only]:w-auto"],
63+
horizontal: [
64+
"flex-row items-center",
65+
"[&>[data-slot=field-label]]:flex-auto",
66+
"has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px has-[>[data-slot=field-content]]:items-start",
67+
],
68+
responsive: [
69+
"@md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto flex-col [&>*]:w-full [&>.sr-only]:w-auto",
70+
"@md/field-group:[&>[data-slot=field-label]]:flex-auto",
71+
"@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
72+
],
73+
},
74+
},
75+
defaultVariants: {
76+
orientation: "vertical",
77+
},
78+
}
79+
)
80+
81+
function Field({
82+
className,
83+
orientation = "vertical",
84+
...props
85+
}: React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>) {
86+
return (
87+
<div
88+
role="group"
89+
data-slot="field"
90+
data-orientation={orientation}
91+
className={cn(fieldVariants({ orientation }), className)}
92+
{...props}
93+
/>
94+
)
95+
}
96+
97+
function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
98+
return (
99+
<div
100+
data-slot="field-content"
101+
className={cn(
102+
"group/field-content flex flex-1 flex-col gap-1.5 leading-snug",
103+
className
104+
)}
105+
{...props}
106+
/>
107+
)
108+
}
109+
110+
function FieldLabel({
111+
className,
112+
...props
113+
}: React.ComponentProps<typeof Label>) {
114+
return (
115+
<Label
116+
data-slot="field-label"
117+
className={cn(
118+
"group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50",
119+
"has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>[data-slot=field]]:p-4",
120+
"has-data-[state=checked]:bg-primary/5 has-data-[state=checked]:border-primary dark:has-data-[state=checked]:bg-primary/10",
121+
className
122+
)}
123+
{...props}
124+
/>
125+
)
126+
}
127+
128+
function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
129+
return (
130+
<div
131+
data-slot="field-label"
132+
className={cn(
133+
"flex w-fit items-center gap-2 text-sm font-medium leading-snug group-data-[disabled=true]/field:opacity-50",
134+
className
135+
)}
136+
{...props}
137+
/>
138+
)
139+
}
140+
141+
function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
142+
return (
143+
<p
144+
data-slot="field-description"
145+
className={cn(
146+
"text-muted-foreground text-sm font-normal leading-normal group-has-[[data-orientation=horizontal]]/field:text-balance",
147+
"nth-last-2:-mt-1 last:mt-0 [[data-variant=legend]+&]:-mt-1.5",
148+
"[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4",
149+
className
150+
)}
151+
{...props}
152+
/>
153+
)
154+
}
155+
156+
function FieldSeparator({
157+
children,
158+
className,
159+
...props
160+
}: React.ComponentProps<"div"> & {
161+
children?: React.ReactNode
162+
}) {
163+
return (
164+
<div
165+
data-slot="field-separator"
166+
data-content={!!children}
167+
className={cn(
168+
"relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2",
169+
className
170+
)}
171+
{...props}
172+
>
173+
<Separator className="absolute inset-0 top-1/2" />
174+
{children && (
175+
<span
176+
className="bg-background text-muted-foreground relative mx-auto block w-fit px-2"
177+
data-slot="field-separator-content"
178+
>
179+
{children}
180+
</span>
181+
)}
182+
</div>
183+
)
184+
}
185+
186+
function FieldError({
187+
className,
188+
children,
189+
errors,
190+
...props
191+
}: React.ComponentProps<"div"> & {
192+
errors?: Array<{ message?: string } | undefined>
193+
}) {
194+
const content = useMemo(() => {
195+
if (children) {
196+
return children
197+
}
198+
199+
if (!errors) {
200+
return null
201+
}
202+
203+
if (errors?.length === 1 && errors[0]?.message) {
204+
return errors[0].message
205+
}
206+
207+
return (
208+
<ul className="ml-4 flex list-disc flex-col gap-1">
209+
{errors.map(
210+
(error, index) =>
211+
error?.message && <li key={index}>{error.message}</li>
212+
)}
213+
</ul>
214+
)
215+
}, [children, errors])
216+
217+
if (!content) {
218+
return null
219+
}
220+
221+
return (
222+
<div
223+
role="alert"
224+
data-slot="field-error"
225+
className={cn("text-destructive text-sm font-normal", className)}
226+
{...props}
227+
>
228+
{content}
229+
</div>
230+
)
231+
}
232+
233+
export {
234+
Field,
235+
FieldLabel,
236+
FieldDescription,
237+
FieldError,
238+
FieldGroup,
239+
FieldLegend,
240+
FieldSeparator,
241+
FieldSet,
242+
FieldContent,
243+
FieldTitle,
244+
}

editor/components/ui/label.tsx

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,25 @@
22

33
import * as React from "react"
44
import * as LabelPrimitive from "@radix-ui/react-label"
5+
import { cva, type VariantProps } from "class-variance-authority"
56

67
import { cn } from "@/components/lib/utils/index"
78

8-
function Label({
9-
className,
10-
...props
11-
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
12-
return (
13-
<LabelPrimitive.Root
14-
data-slot="label"
15-
className={cn(
16-
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
17-
className
18-
)}
19-
{...props}
20-
/>
21-
)
22-
}
9+
const labelVariants = cva(
10+
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
11+
)
12+
13+
const Label = React.forwardRef<
14+
React.ElementRef<typeof LabelPrimitive.Root>,
15+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
16+
VariantProps<typeof labelVariants>
17+
>(({ className, ...props }, ref) => (
18+
<LabelPrimitive.Root
19+
ref={ref}
20+
className={cn(labelVariants(), className)}
21+
{...props}
22+
/>
23+
))
24+
Label.displayName = LabelPrimitive.Root.displayName
2325

2426
export { Label }

0 commit comments

Comments
 (0)