jbilcke-hf HF staff commited on
Commit
aeeae20
1 Parent(s): 3635e59

neutral theme, comfy auth, dynamic workflows

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. documentation/design/README.md +70 -0
  2. package-lock.json +31 -16
  3. package.json +6 -5
  4. src/app/api/resolve/providers/comfy-replicate/index.ts +52 -15
  5. src/app/api/resolve/providers/comfyui/getComfyWorkflow.ts +0 -33
  6. src/app/api/resolve/providers/comfyui/index.ts +44 -6
  7. src/app/api/resolve/providers/comfyui/temporary_demo.json +0 -80
  8. src/app/embed/embed.tsx +1 -1
  9. src/app/main.tsx +23 -4
  10. src/components/dialogs/iframe-warning/index.tsx +2 -2
  11. src/components/dialogs/loader/index.tsx +1 -1
  12. src/components/forms/FormSection.tsx +1 -1
  13. src/components/mobile/MobileMenu.tsx +25 -0
  14. src/components/monitor/StaticPlayer/index.tsx +2 -2
  15. src/components/settings/index.tsx +3 -3
  16. src/components/settings/provider.tsx +26 -2
  17. src/components/tags/colors.ts +1 -1
  18. src/components/toolbars/bottom-bar/metrics/index.tsx +2 -2
  19. src/components/toolbars/editors-menu/EditorsSideMenuItem.tsx +2 -2
  20. src/components/toolbars/system-menu/index.tsx +13 -10
  21. src/components/toolbars/top-menu/ToggleView/index.tsx +1 -1
  22. src/components/toolbars/top-menu/index.tsx +2 -2
  23. src/components/ui/alert.tsx +3 -2
  24. src/components/ui/avatar.tsx +1 -1
  25. src/components/ui/badge.tsx +5 -5
  26. src/components/ui/button.tsx +7 -7
  27. src/components/ui/card.tsx +2 -2
  28. src/components/ui/checkbox.tsx +1 -1
  29. src/components/ui/command.tsx +7 -7
  30. src/components/ui/dialog.tsx +4 -4
  31. src/components/ui/dropdown-menu.tsx +10 -7
  32. src/components/ui/popover.tsx +1 -1
  33. src/components/ui/progress.tsx +2 -2
  34. src/components/ui/scroll-area.tsx +1 -1
  35. src/components/ui/select.tsx +7 -4
  36. src/components/ui/separator.tsx +1 -1
  37. src/components/ui/sheet.tsx +143 -0
  38. src/components/ui/sonner.tsx +4 -4
  39. src/components/ui/table.tsx +7 -4
  40. src/components/ui/tabs.tsx +3 -3
  41. src/components/ui/textarea.tsx +1 -1
  42. src/components/ui/toast.tsx +5 -5
  43. src/components/ui/tooltip.tsx +1 -1
  44. src/components/ui/vertical-slider.tsx +3 -3
  45. src/components/wizards/project/index.tsx +3 -3
  46. src/experiments/moodboard/MoodboardView.tsx +1 -1
  47. src/experiments/moodboard/Node.tsx +1 -1
  48. src/lib/core/constants.ts +1 -1
  49. src/services/debug.ts +9 -2
  50. src/services/editors/script-editor/useScriptEditor.ts +3 -2
documentation/design/README.md ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Design
2
+
3
+ (those are just quick notes for now, we can elaborate later)
4
+
5
+ ## Designing Clapper for the Web
6
+
7
+ ### Targeted devices
8
+
9
+ Currently Clapper works best on a laptop,
10
+ since the UI has been developed around the presence of a relatively wide screen,
11
+ a mouse and a touch pad (for horizontal scrolling in the timeline).
12
+
13
+ However I think we should try to better support the following environments:
14
+
15
+ - Tiny laptop (12", 11")
16
+ - Tablet (without a mouse)
17
+ - Desktop computer with multiple screens
18
+
19
+ (mobile has its own chapter, see below in this document)
20
+
21
+ ### Use Web APIs in priority
22
+
23
+ We should try to use standard Web APIs (ratified by the W3C) as much as possible, with polyfills in case some browsers don't implement them yet.
24
+
25
+ We can use experimental standards (eg. WebGPU),
26
+ but since they are unstable and not supported by all browsers, they should not be mandatory to the experience.
27
+
28
+ ## Designing Clapper for desktop
29
+
30
+ The experience on Desktop should be similar to the one in a browser, but we can do some changes:
31
+
32
+ ### Customizing the look of the app window
33
+
34
+ Electron offers us to hide or customize the app's window, so we should do it.
35
+
36
+ We don't have to follow the design principles of the underlying operating system (some apps don't care eg. video games, Spotify, Slack, FLStudio, Discord..) but it may be necessary for some operations (file pickers, installer, window management etc).
37
+
38
+ ### Use the native file system
39
+
40
+ The big benefit of running Clapper as a desktop application is that we can access the file system, meaning we can work with files of arbitrary file length (note: for this kind of file system manipulation, we will have to use extra code eg. NodeJS)
41
+
42
+ This can also be used for performance optimization such as using temporary files, or pre-computing things and store them in a cache for the next time Clapper is opened.
43
+
44
+ ### Download additional data for local use
45
+
46
+ By running Clapper on the user's device, we can also make it download data of arbitrary size.
47
+
48
+ This can help with various use cases, such as running AI models locally.
49
+
50
+ For instance the desktop app LMStudio can download models from Hugging Face.
51
+
52
+ ### Call external or embedded native libraries
53
+
54
+ We have complete freedom to ship Clapper with embedded native tools eg. a database or a native library (eg. a C++ library to run a LLM locally).
55
+
56
+ We could even use Python scripts with Clapper.
57
+
58
+ Please note however that we will have to make sure anything we embed works on various operating systems (Windows/macOS/Linux) so this requires dedicated skills and maintenance.
59
+
60
+ ### Use System Notifications
61
+
62
+ When a job is pending, finished, a software update is ready etc.
63
+
64
+ ## Clapper Design For Mobile
65
+
66
+ Currently Clapper can *run* on mobile, but it is not designed for it.
67
+
68
+ So things like the top menu etc will be pretty much unuseable, unless we adapt them.
69
+
70
+
package-lock.json CHANGED
@@ -11,7 +11,7 @@
11
  "dependencies": {
12
  "@aitube/broadway": "0.2.3",
13
  "@aitube/clap": "0.2.3",
14
- "@aitube/clapper-services": "0.2.3-1",
15
  "@aitube/client": "0.2.3",
16
  "@aitube/engine": "0.2.3",
17
  "@aitube/timeline": "0.2.3",
@@ -33,7 +33,7 @@
33
  "@radix-ui/react-avatar": "^1.0.4",
34
  "@radix-ui/react-checkbox": "^1.0.4",
35
  "@radix-ui/react-collapsible": "^1.0.3",
36
- "@radix-ui/react-dialog": "^1.0.5",
37
  "@radix-ui/react-dropdown-menu": "^2.0.6",
38
  "@radix-ui/react-icons": "^1.3.0",
39
  "@radix-ui/react-label": "^2.0.2",
@@ -66,7 +66,7 @@
66
  "class-variance-authority": "^0.7.0",
67
  "clsx": "^2.1.1",
68
  "cmdk": "^0.2.1",
69
- "comfydeploy": "^0.0.19-beta.13",
70
  "date-fns": "^3.6.0",
71
  "dotenv": "^16.4.5",
72
  "fflate": "^0.8.2",
@@ -89,6 +89,7 @@
89
  "react-dnd-html5-backend": "^16.0.1",
90
  "react-dom": "^18.3.1",
91
  "react-drag-drop-files": "^2.3.10",
 
92
  "react-hook-consent": "^3.5.3",
93
  "react-hotkeys-hook": "^4.5.0",
94
  "react-icons": "^5.2.1",
@@ -99,7 +100,7 @@
99
  "replicate": "^0.32.0",
100
  "sharp": "0.33.4",
101
  "sonner": "^1.5.0",
102
- "tailwind-merge": "^2.4.0",
103
  "tailwindcss-animate": "^1.0.7",
104
  "three": "^0.164.1",
105
  "ts-node": "^10.9.2",
@@ -109,7 +110,7 @@
109
  "web-audio-beat-detector": "^8.2.12",
110
  "yaml": "^2.4.5",
111
  "zustand": "4.5.2",
112
- "zx": "^8.1.3"
113
  },
114
  "devDependencies": {
115
  "@electron-forge/cli": "^7.4.0",
@@ -191,9 +192,9 @@
191
  }
192
  },
193
  "node_modules/@aitube/clapper-services": {
194
- "version": "0.2.3-1",
195
- "resolved": "https://registry.npmjs.org/@aitube/clapper-services/-/clapper-services-0.2.3-1.tgz",
196
- "integrity": "sha512-R6n9Wlte7E7bRIKh3Jf8xSZkvly2BgQ4p12OJY1rBNzgvDaUfHyFcCdCwKcBXzcDlWzGW5fjQIA7+OPeoXpC0Q==",
197
  "peerDependencies": {
198
  "@aitube/clap": "0.2.3",
199
  "@aitube/timeline": "0.2.3",
@@ -9394,11 +9395,14 @@
9394
  }
9395
  },
9396
  "node_modules/comfydeploy": {
9397
- "version": "0.0.19-beta.13",
9398
- "resolved": "https://registry.npmjs.org/comfydeploy/-/comfydeploy-0.0.19-beta.13.tgz",
9399
- "integrity": "sha512-m3sn8XCYJ9FjEhB6CoA8IYQ67CvizQuZ9vodNroiZB5BA2TakkGTK5TUj+PxmpOopdUZVhlSOVqjcB1+KQYJBQ==",
9400
- "peerDependencies": {
9401
- "zod": ">= 3"
 
 
 
9402
  }
9403
  },
9404
  "node_modules/comma-separated-tokens": {
@@ -17745,6 +17749,17 @@
17745
  "react-dom": "^18.0.0"
17746
  }
17747
  },
 
 
 
 
 
 
 
 
 
 
 
17748
  "node_modules/react-hook-consent": {
17749
  "version": "3.5.3",
17750
  "resolved": "https://registry.npmjs.org/react-hook-consent/-/react-hook-consent-3.5.3.tgz",
@@ -19595,9 +19610,9 @@
19595
  "dev": true
19596
  },
19597
  "node_modules/tailwind-merge": {
19598
- "version": "2.4.0",
19599
- "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.4.0.tgz",
19600
- "integrity": "sha512-49AwoOQNKdqKPd9CViyH5wJoSKsCDjUlzL8DxuGp3P1FsGY36NJDAa18jLZcaHAUUuTj+JB8IAo8zWgBNvBF7A==",
19601
  "funding": {
19602
  "type": "github",
19603
  "url": "https://github.com/sponsors/dcastil"
 
11
  "dependencies": {
12
  "@aitube/broadway": "0.2.3",
13
  "@aitube/clap": "0.2.3",
14
+ "@aitube/clapper-services": "0.2.3-2",
15
  "@aitube/client": "0.2.3",
16
  "@aitube/engine": "0.2.3",
17
  "@aitube/timeline": "0.2.3",
 
33
  "@radix-ui/react-avatar": "^1.0.4",
34
  "@radix-ui/react-checkbox": "^1.0.4",
35
  "@radix-ui/react-collapsible": "^1.0.3",
36
+ "@radix-ui/react-dialog": "^1.1.1",
37
  "@radix-ui/react-dropdown-menu": "^2.0.6",
38
  "@radix-ui/react-icons": "^1.3.0",
39
  "@radix-ui/react-label": "^2.0.2",
 
66
  "class-variance-authority": "^0.7.0",
67
  "clsx": "^2.1.1",
68
  "cmdk": "^0.2.1",
69
+ "comfydeploy": "^0.0.21",
70
  "date-fns": "^3.6.0",
71
  "dotenv": "^16.4.5",
72
  "fflate": "^0.8.2",
 
89
  "react-dnd-html5-backend": "^16.0.1",
90
  "react-dom": "^18.3.1",
91
  "react-drag-drop-files": "^2.3.10",
92
+ "react-error-boundary": "^4.0.13",
93
  "react-hook-consent": "^3.5.3",
94
  "react-hotkeys-hook": "^4.5.0",
95
  "react-icons": "^5.2.1",
 
100
  "replicate": "^0.32.0",
101
  "sharp": "0.33.4",
102
  "sonner": "^1.5.0",
103
+ "tailwind-merge": "^2.5.2",
104
  "tailwindcss-animate": "^1.0.7",
105
  "three": "^0.164.1",
106
  "ts-node": "^10.9.2",
 
110
  "web-audio-beat-detector": "^8.2.12",
111
  "yaml": "^2.4.5",
112
  "zustand": "4.5.2",
113
+ "zx": "^8.1.4"
114
  },
115
  "devDependencies": {
116
  "@electron-forge/cli": "^7.4.0",
 
192
  }
193
  },
194
  "node_modules/@aitube/clapper-services": {
195
+ "version": "0.2.3-2",
196
+ "resolved": "https://registry.npmjs.org/@aitube/clapper-services/-/clapper-services-0.2.3-2.tgz",
197
+ "integrity": "sha512-Qd6Riridk4FVcTjlscxw5wsbUgojwi1wkTIjlgPluhT5J5kLyEJQL/hmT2gBDBRsB4kyieVNZiGflgXnauDENw==",
198
  "peerDependencies": {
199
  "@aitube/clap": "0.2.3",
200
  "@aitube/timeline": "0.2.3",
 
9395
  }
9396
  },
9397
  "node_modules/comfydeploy": {
9398
+ "version": "0.0.21",
9399
+ "resolved": "https://registry.npmjs.org/comfydeploy/-/comfydeploy-0.0.21.tgz",
9400
+ "integrity": "sha512-v5i6igitVWN6jmDlg35g8XyJuGsToD1sbhEZIz4nM3F6gSXZf2YjJzL/wzabsHlOtHhKsO/vdQAXuvO7/w8deQ==",
9401
+ "dependencies": {
9402
+ "zod": "^3.22.4"
9403
+ },
9404
+ "engines": {
9405
+ "node": ">=16"
9406
  }
9407
  },
9408
  "node_modules/comma-separated-tokens": {
 
17749
  "react-dom": "^18.0.0"
17750
  }
17751
  },
17752
+ "node_modules/react-error-boundary": {
17753
+ "version": "4.0.13",
17754
+ "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.13.tgz",
17755
+ "integrity": "sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==",
17756
+ "dependencies": {
17757
+ "@babel/runtime": "^7.12.5"
17758
+ },
17759
+ "peerDependencies": {
17760
+ "react": ">=16.13.1"
17761
+ }
17762
+ },
17763
  "node_modules/react-hook-consent": {
17764
  "version": "3.5.3",
17765
  "resolved": "https://registry.npmjs.org/react-hook-consent/-/react-hook-consent-3.5.3.tgz",
 
19610
  "dev": true
19611
  },
19612
  "node_modules/tailwind-merge": {
19613
+ "version": "2.5.2",
19614
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.5.2.tgz",
19615
+ "integrity": "sha512-kjEBm+pvD+6eAwzJL2Bi+02/9LFLal1Gs61+QB7HvTfQQ0aXwC5LGT8PEt1gS0CWKktKe6ysPTAy3cBC5MeiIg==",
19616
  "funding": {
19617
  "type": "github",
19618
  "url": "https://github.com/sponsors/dcastil"
package.json CHANGED
@@ -38,7 +38,7 @@
38
  "dependencies": {
39
  "@aitube/broadway": "0.2.3",
40
  "@aitube/clap": "0.2.3",
41
- "@aitube/clapper-services": "0.2.3-1",
42
  "@aitube/client": "0.2.3",
43
  "@aitube/engine": "0.2.3",
44
  "@aitube/timeline": "0.2.3",
@@ -60,7 +60,7 @@
60
  "@radix-ui/react-avatar": "^1.0.4",
61
  "@radix-ui/react-checkbox": "^1.0.4",
62
  "@radix-ui/react-collapsible": "^1.0.3",
63
- "@radix-ui/react-dialog": "^1.0.5",
64
  "@radix-ui/react-dropdown-menu": "^2.0.6",
65
  "@radix-ui/react-icons": "^1.3.0",
66
  "@radix-ui/react-label": "^2.0.2",
@@ -93,7 +93,7 @@
93
  "class-variance-authority": "^0.7.0",
94
  "clsx": "^2.1.1",
95
  "cmdk": "^0.2.1",
96
- "comfydeploy": "^0.0.19-beta.13",
97
  "date-fns": "^3.6.0",
98
  "dotenv": "^16.4.5",
99
  "fflate": "^0.8.2",
@@ -116,6 +116,7 @@
116
  "react-dnd-html5-backend": "^16.0.1",
117
  "react-dom": "^18.3.1",
118
  "react-drag-drop-files": "^2.3.10",
 
119
  "react-hook-consent": "^3.5.3",
120
  "react-hotkeys-hook": "^4.5.0",
121
  "react-icons": "^5.2.1",
@@ -126,7 +127,7 @@
126
  "replicate": "^0.32.0",
127
  "sharp": "0.33.4",
128
  "sonner": "^1.5.0",
129
- "tailwind-merge": "^2.4.0",
130
  "tailwindcss-animate": "^1.0.7",
131
  "three": "^0.164.1",
132
  "ts-node": "^10.9.2",
@@ -136,7 +137,7 @@
136
  "web-audio-beat-detector": "^8.2.12",
137
  "yaml": "^2.4.5",
138
  "zustand": "4.5.2",
139
- "zx": "^8.1.3"
140
  },
141
  "devDependencies": {
142
  "@electron-forge/cli": "^7.4.0",
 
38
  "dependencies": {
39
  "@aitube/broadway": "0.2.3",
40
  "@aitube/clap": "0.2.3",
41
+ "@aitube/clapper-services": "0.2.3-2",
42
  "@aitube/client": "0.2.3",
43
  "@aitube/engine": "0.2.3",
44
  "@aitube/timeline": "0.2.3",
 
60
  "@radix-ui/react-avatar": "^1.0.4",
61
  "@radix-ui/react-checkbox": "^1.0.4",
62
  "@radix-ui/react-collapsible": "^1.0.3",
63
+ "@radix-ui/react-dialog": "^1.1.1",
64
  "@radix-ui/react-dropdown-menu": "^2.0.6",
65
  "@radix-ui/react-icons": "^1.3.0",
66
  "@radix-ui/react-label": "^2.0.2",
 
93
  "class-variance-authority": "^0.7.0",
94
  "clsx": "^2.1.1",
95
  "cmdk": "^0.2.1",
96
+ "comfydeploy": "^0.0.21",
97
  "date-fns": "^3.6.0",
98
  "dotenv": "^16.4.5",
99
  "fflate": "^0.8.2",
 
116
  "react-dnd-html5-backend": "^16.0.1",
117
  "react-dom": "^18.3.1",
118
  "react-drag-drop-files": "^2.3.10",
119
+ "react-error-boundary": "^4.0.13",
120
  "react-hook-consent": "^3.5.3",
121
  "react-hotkeys-hook": "^4.5.0",
122
  "react-icons": "^5.2.1",
 
127
  "replicate": "^0.32.0",
128
  "sharp": "0.33.4",
129
  "sonner": "^1.5.0",
130
+ "tailwind-merge": "^2.5.2",
131
  "tailwindcss-animate": "^1.0.7",
132
  "three": "^0.164.1",
133
  "ts-node": "^10.9.2",
 
137
  "web-audio-beat-detector": "^8.2.12",
138
  "yaml": "^2.4.5",
139
  "zustand": "4.5.2",
140
+ "zx": "^8.1.4"
141
  },
142
  "devDependencies": {
143
  "@electron-forge/cli": "^7.4.0",
src/app/api/resolve/providers/comfy-replicate/index.ts CHANGED
@@ -1,7 +1,11 @@
1
- import { ClapSegmentStatus, getClapAssetSourceType } from '@aitube/clap'
 
 
 
 
2
 
3
  import { ResolveRequest } from '@aitube/clapper-services'
4
- import { getComfyWorkflow } from '../comfyui/getComfyWorkflow'
5
  import { runWorkflow } from './runWorkflow'
6
  import { TimelineSegment } from '@aitube/timeline'
7
 
@@ -11,22 +15,55 @@ export async function resolveSegment(
11
  if (!request.settings.replicateApiKey) {
12
  throw new Error(`Missing API key for "Replicate.com"`)
13
  }
14
- const workflow = getComfyWorkflow(request)
15
 
16
  const segment: TimelineSegment = request.segment
17
 
18
- try {
19
- segment.assetUrl = await runWorkflow({
20
- apiKey: request.settings.replicateApiKey,
21
- workflow,
22
- })
23
- segment.assetSourceType = getClapAssetSourceType(segment.assetUrl)
24
- } catch (err) {
25
- console.error(`failed to call Replicate: `, err)
26
- segment.assetUrl = ''
27
- segment.assetSourceType = getClapAssetSourceType(segment.assetUrl)
28
- segment.status = ClapSegmentStatus.TO_GENERATE
29
- // request.segment.status = ClapSegmentStatus.ERROR
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  }
31
 
32
  return segment
 
1
+ import {
2
+ ClapSegmentCategory,
3
+ ClapSegmentStatus,
4
+ getClapAssetSourceType,
5
+ } from '@aitube/clap'
6
 
7
  import { ResolveRequest } from '@aitube/clapper-services'
8
+ import { getComfyWorkflow } from '../../../../../services/editors/workflow-editor/workflows/comfyui/getComfyWorkflow'
9
  import { runWorkflow } from './runWorkflow'
10
  import { TimelineSegment } from '@aitube/timeline'
11
 
 
15
  if (!request.settings.replicateApiKey) {
16
  throw new Error(`Missing API key for "Replicate.com"`)
17
  }
 
18
 
19
  const segment: TimelineSegment = request.segment
20
 
21
+ if (request.segment.category === ClapSegmentCategory.STORYBOARD) {
22
+ const inputFields =
23
+ request.settings.imageGenerationWorkflow.inputFields || []
24
+
25
+ // since this is a random "wild" workflow, it is possible
26
+ // that the field name is a bit different
27
+ // we try to look into the workflow input fields
28
+ // to find the best match
29
+ const promptFields = [
30
+ inputFields.find((f) => f.id === 'prompt'), // exactMatch,
31
+ inputFields.find((f) => f.id.includes('prompt')), // similarName,
32
+ inputFields.find((f) => f.type === 'string'), // similarType
33
+ ].filter((x) => typeof x !== 'undefined')
34
+
35
+ const promptField = promptFields[0]
36
+ if (!promptField) {
37
+ throw new Error(
38
+ `this workflow doesn't seem to have a parameter called "prompt"`
39
+ )
40
+ }
41
+
42
+ console.log(`TODO: inject parameters into the final request object`)
43
+ const workflow = `{}` // <- the final workflow sent to Replicate
44
+
45
+ try {
46
+ segment.assetUrl = await runWorkflow({
47
+ apiKey: request.settings.replicateApiKey,
48
+ workflow,
49
+ })
50
+ segment.assetSourceType = getClapAssetSourceType(segment.assetUrl)
51
+ } catch (err) {
52
+ console.error(`failed to call Replicate: `, err)
53
+ segment.assetUrl = ''
54
+ segment.assetSourceType = getClapAssetSourceType(segment.assetUrl)
55
+ segment.status = ClapSegmentStatus.TO_GENERATE
56
+ // request.segment.status = ClapSegmentStatus.ERROR
57
+ }
58
+ } else if (request.segment.category === ClapSegmentCategory.VIDEO) {
59
+ // TODO do the same for video
60
+ throw new Error(
61
+ `Clapper doesn't support ${request.segment.category} generation for provider "Comfy.icu". Please open a pull request with (working code) to solve this!`
62
+ )
63
+ } else {
64
+ throw new Error(
65
+ `Clapper doesn't support ${request.segment.category} generation for provider "Comfy.icu". Please open a pull request with (working code) to solve this!`
66
+ )
67
  }
68
 
69
  return segment
src/app/api/resolve/providers/comfyui/getComfyWorkflow.ts DELETED
@@ -1,33 +0,0 @@
1
- import { ClapSegmentCategory } from '@aitube/clap'
2
- import { getVideoPrompt } from '@aitube/engine'
3
-
4
- import { ComfyNode, ResolveRequest } from '@aitube/clapper-services'
5
-
6
- // TODO move this to @aitube/engine or @aitube/engine-comfy
7
- export function getComfyWorkflow(request: ResolveRequest) {
8
- let comfyWorkflow = '{}'
9
-
10
- if (request.segment.category === ClapSegmentCategory.STORYBOARD) {
11
- comfyWorkflow = request.settings.comfyWorkflowForImage
12
- } else if (request.segment.category === ClapSegmentCategory.VIDEO) {
13
- comfyWorkflow = request.settings.comfyWorkflowForVideo
14
- }
15
-
16
- // parse the node array from the ComfyUI workflow
17
- const nodes = Object.values(JSON.parse(comfyWorkflow)) as ComfyNode[]
18
-
19
- const visualPrompt = getVideoPrompt(request.segments, request.entities)
20
-
21
- const output: Record<string, ComfyNode> = {}
22
-
23
- nodes.forEach((node, i) => {
24
- if (typeof node.inputs.text === 'string') {
25
- if (node._meta.title.includes('Prompt')) {
26
- node.inputs.text = visualPrompt
27
- }
28
- }
29
- output[`${i}`] = node
30
- })
31
-
32
- return JSON.stringify(output)
33
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/app/api/resolve/providers/comfyui/index.ts CHANGED
@@ -1,5 +1,6 @@
1
  import { ResolveRequest } from '@aitube/clapper-services'
2
  import {
 
3
  ClapSegmentCategory,
4
  ClapSegmentStatus,
5
  generateSeed,
@@ -7,6 +8,7 @@ import {
7
  } from '@aitube/clap'
8
  import { TimelineSegment } from '@aitube/timeline'
9
  import {
 
10
  CallWrapper,
11
  ComfyApi,
12
  PromptBuilder,
@@ -15,6 +17,7 @@ import {
15
  } from '@saintno/comfyui-sdk'
16
 
17
  import { getWorkflowInputValues } from '../getWorkflowInputValues'
 
18
 
19
  export async function resolveSegment(
20
  request: ResolveRequest
@@ -25,10 +28,25 @@ export async function resolveSegment(
25
 
26
  const segment: TimelineSegment = { ...request.segment }
27
 
 
 
 
 
 
 
28
  // for API doc please see:
29
  // https://github.com/tctien342/comfyui-sdk/blob/main/examples/example-t2i.ts
30
  const api = new ComfyApi(
31
- request.settings.comfyUiApiUrl || 'http://localhost:8188'
 
 
 
 
 
 
 
 
 
32
  ).init()
33
 
34
  if (request.segment.category === ClapSegmentCategory.STORYBOARD) {
@@ -123,18 +141,38 @@ export async function resolveSegment(
123
  .onStart(() => console.log('Task is started'))
124
  .onPreview((blob) => console.log(blob))
125
  .onFinished((data) => {
126
- console.log(
127
- data.images?.images.map((img: any) => api.getPathImage(img))
128
- )
129
  })
130
  .onProgress((info) =>
131
  console.log('Processing node', info.node, `${info.value}/${info.max}`)
132
  )
133
  .onFailed((err) => console.log('Task is failed', err))
134
 
135
- const result = await pipeline.run()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
 
137
- console.log(`result:`, result)
138
  } else {
139
  throw new Error(
140
  `Clapper doesn't support ${request.segment.category} generation for provider "ComfyUI". Please open a pull request with (working code) to solve this!`
 
1
  import { ResolveRequest } from '@aitube/clapper-services'
2
  import {
3
+ ClapAssetSource,
4
  ClapSegmentCategory,
5
  ClapSegmentStatus,
6
  generateSeed,
 
8
  } from '@aitube/clap'
9
  import { TimelineSegment } from '@aitube/timeline'
10
  import {
11
+ BasicCredentials,
12
  CallWrapper,
13
  ComfyApi,
14
  PromptBuilder,
 
17
  } from '@saintno/comfyui-sdk'
18
 
19
  import { getWorkflowInputValues } from '../getWorkflowInputValues'
20
+ import { decodeOutput } from '@/lib/utils/decodeOutput'
21
 
22
  export async function resolveSegment(
23
  request: ResolveRequest
 
28
 
29
  const segment: TimelineSegment = { ...request.segment }
30
 
31
+ const credentials: BasicCredentials = {
32
+ type: 'basic',
33
+ username: request.settings.comfyUiHttpAuthLogin,
34
+ password: request.settings.comfyUiHttpAuthPassword,
35
+ }
36
+
37
  // for API doc please see:
38
  // https://github.com/tctien342/comfyui-sdk/blob/main/examples/example-t2i.ts
39
  const api = new ComfyApi(
40
+ request.settings.comfyUiApiUrl || 'http://localhost:8188',
41
+ request.settings.comfyUiClientId,
42
+
43
+ // HTTP Auth is optional
44
+ // also in the future, we might support other things (bearer tokens?)
45
+ request.settings.comfyUiHttpAuthLogin
46
+ ? {
47
+ credentials,
48
+ }
49
+ : undefined
50
  ).init()
51
 
52
  if (request.segment.category === ClapSegmentCategory.STORYBOARD) {
 
141
  .onStart(() => console.log('Task is started'))
142
  .onPreview((blob) => console.log(blob))
143
  .onFinished((data) => {
144
+ console.log('Pipeline finished')
 
 
145
  })
146
  .onProgress((info) =>
147
  console.log('Processing node', info.node, `${info.value}/${info.max}`)
148
  )
149
  .onFailed((err) => console.log('Task is failed', err))
150
 
151
+ const rawOutput = await pipeline.run()
152
+
153
+ if (!rawOutput) {
154
+ throw new Error(`failed to run the pipeline (no output)`)
155
+ }
156
+
157
+ const imagePaths = rawOutput.images?.images.map((img: any) =>
158
+ api.getPathImage(img)
159
+ )
160
+
161
+ console.log(`imagePaths:`, imagePaths)
162
+
163
+ const imagePath = imagePaths.at(0)
164
+ if (!imagePath) {
165
+ throw new Error(`failed to run the pipeline (no image)`)
166
+ }
167
+
168
+ // TODO: check what the imagePath looks like exactly
169
+ const assetUrl = await decodeOutput(imagePath)
170
+
171
+ console.log(`assetUrl:`, imagePaths)
172
+ segment.assetUrl = assetUrl
173
+ segment.assetSourceType = ClapAssetSource.DATA
174
 
175
+ // TODO:
176
  } else {
177
  throw new Error(
178
  `Clapper doesn't support ${request.segment.category} generation for provider "ComfyUI". Please open a pull request with (working code) to solve this!`
src/app/api/resolve/providers/comfyui/temporary_demo.json DELETED
@@ -1,80 +0,0 @@
1
- {
2
- "3": {
3
- "inputs": {
4
- "seed": 303533494192794,
5
- "steps": 20,
6
- "cfg": 8,
7
- "sampler_name": "euler",
8
- "scheduler": "normal",
9
- "denoise": 1,
10
- "model": ["4", 0],
11
- "positive": ["6", 0],
12
- "negative": ["7", 0],
13
- "latent_image": ["5", 0]
14
- },
15
- "class_type": "KSampler",
16
- "_meta": {
17
- "title": "KSampler"
18
- }
19
- },
20
- "4": {
21
- "inputs": {
22
- "ckpt_name": "SD15/counterfeitV30_v30.safetensors"
23
- },
24
- "class_type": "CheckpointLoaderSimple",
25
- "_meta": {
26
- "title": "Load Checkpoint"
27
- }
28
- },
29
- "5": {
30
- "inputs": {
31
- "width": 512,
32
- "height": 512,
33
- "batch_size": 1
34
- },
35
- "class_type": "EmptyLatentImage",
36
- "_meta": {
37
- "title": "Empty Latent Image"
38
- }
39
- },
40
- "6": {
41
- "inputs": {
42
- "text": "beautiful scenery nature glass bottle landscape, , purple galaxy bottle,",
43
- "clip": ["4", 1]
44
- },
45
- "class_type": "CLIPTextEncode",
46
- "_meta": {
47
- "title": "CLIP Text Encode (Prompt)"
48
- }
49
- },
50
- "7": {
51
- "inputs": {
52
- "text": "text, watermark",
53
- "clip": ["4", 1]
54
- },
55
- "class_type": "CLIPTextEncode",
56
- "_meta": {
57
- "title": "CLIP Text Encode (Prompt)"
58
- }
59
- },
60
- "8": {
61
- "inputs": {
62
- "samples": ["3", 0],
63
- "vae": ["4", 2]
64
- },
65
- "class_type": "VAEDecode",
66
- "_meta": {
67
- "title": "VAE Decode"
68
- }
69
- },
70
- "9": {
71
- "inputs": {
72
- "filename_prefix": "ComfyUI",
73
- "images": ["8", 0]
74
- },
75
- "class_type": "SaveImage",
76
- "_meta": {
77
- "title": "Save Image"
78
- }
79
- }
80
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/app/embed/embed.tsx CHANGED
@@ -23,7 +23,7 @@ export function Embed() {
23
  <div
24
  ref={ref}
25
  className={cn(
26
- `dark fixed flex h-screen w-screen select-none flex-col items-center justify-center overflow-hidden font-light text-stone-900/90 dark:text-stone-100/90`
27
  )}
28
  style={{ backgroundImage: theme.wallpaperBgImage }}
29
  >
 
23
  <div
24
  ref={ref}
25
  className={cn(
26
+ `dark fixed flex h-screen w-screen select-none flex-col items-center justify-center overflow-hidden font-light text-neutral-900/90 dark:text-neutral-100/90`
27
  )}
28
  style={{ backgroundImage: theme.wallpaperBgImage }}
29
  >
src/app/main.tsx CHANGED
@@ -6,6 +6,7 @@ import { useSearchParams } from 'next/navigation'
6
  import { DndProvider, useDrop } from 'react-dnd'
7
  import { HTML5Backend, NativeTypes } from 'react-dnd-html5-backend'
8
  import { UIWindowLayout } from '@aitube/clapper-services'
 
9
 
10
  import { Toaster } from '@/components/ui/sonner'
11
  import { cn } from '@/lib/utils'
@@ -14,12 +15,10 @@ import { Monitor } from '@/components/monitor'
14
 
15
  import { SettingsDialog } from '@/components/settings'
16
  import { LoadingDialog } from '@/components/dialogs/loader/LoadingDialog'
17
- import { useUI, useIO } from '@/services'
18
  import { TopBar } from '@/components/toolbars/top-bar'
19
  import { Timeline } from '@/components/core/timeline'
20
  import { ChatView } from '@/components/assistant/ChatView'
21
  import { Editors } from '@/components/editors/Editors'
22
- import { useTheme } from '@/services/ui/useTheme'
23
  import { BottomToolbar } from '@/components/toolbars/bottom-bar'
24
  import { FruityDesktop, FruityWindow } from '@/components/windows'
25
  import { ScriptEditor } from '@/components/editors/ScriptEditor'
@@ -27,7 +26,10 @@ import { SegmentEditor } from '@/components/editors/SegmentEditor'
27
  import { EntityEditor } from '@/components/editors/EntityEditor'
28
  import { WorkflowEditor } from '@/components/editors/WorkflowEditor'
29
  import { FilterEditor } from '@/components/editors/FilterEditor'
 
 
30
  import { useRenderLoop } from '@/services/renderer'
 
31
 
32
  type DroppableThing = { files: File[] }
33
 
@@ -47,6 +49,10 @@ function MainContent() {
47
  // perform its routine even when the monitor component is hidden
48
  useRenderLoop()
49
 
 
 
 
 
50
  const [{ isOver, canDrop }, connectFileDrop] = useDrop({
51
  accept: [NativeTypes.FILE],
52
  drop: (item: DroppableThing): void => {
@@ -229,7 +235,7 @@ function MainContent() {
229
  : 'pointer-events-none hidden',
230
  `fixed top-9 h-[calc(100vh-36px)] w-screen flex-row overflow-hidden`,
231
  `items-center justify-center`,
232
- `bg-stone-950`
233
  )}
234
  >
235
  <div
@@ -310,11 +316,24 @@ function MainContent() {
310
  )
311
  }
312
 
 
 
 
 
 
 
 
 
 
 
 
313
  export function Main() {
314
  return (
315
  <TooltipProvider>
316
  <DndProvider backend={HTML5Backend}>
317
- <MainContent />
 
 
318
  </DndProvider>
319
  </TooltipProvider>
320
  )
 
6
  import { DndProvider, useDrop } from 'react-dnd'
7
  import { HTML5Backend, NativeTypes } from 'react-dnd-html5-backend'
8
  import { UIWindowLayout } from '@aitube/clapper-services'
9
+ import { ErrorBoundary, FallbackProps } from 'react-error-boundary'
10
 
11
  import { Toaster } from '@/components/ui/sonner'
12
  import { cn } from '@/lib/utils'
 
15
 
16
  import { SettingsDialog } from '@/components/settings'
17
  import { LoadingDialog } from '@/components/dialogs/loader/LoadingDialog'
 
18
  import { TopBar } from '@/components/toolbars/top-bar'
19
  import { Timeline } from '@/components/core/timeline'
20
  import { ChatView } from '@/components/assistant/ChatView'
21
  import { Editors } from '@/components/editors/Editors'
 
22
  import { BottomToolbar } from '@/components/toolbars/bottom-bar'
23
  import { FruityDesktop, FruityWindow } from '@/components/windows'
24
  import { ScriptEditor } from '@/components/editors/ScriptEditor'
 
26
  import { EntityEditor } from '@/components/editors/EntityEditor'
27
  import { WorkflowEditor } from '@/components/editors/WorkflowEditor'
28
  import { FilterEditor } from '@/components/editors/FilterEditor'
29
+
30
+ import { useUI, useIO, useTheme } from '@/services'
31
  import { useRenderLoop } from '@/services/renderer'
32
+ import { useDynamicWorkflows } from '@/services/editors/workflow-editor/useDynamicWorkflows'
33
 
34
  type DroppableThing = { files: File[] }
35
 
 
49
  // perform its routine even when the monitor component is hidden
50
  useRenderLoop()
51
 
52
+ // this has to be done at the root of the app, that way it can
53
+ // sync workflows even when the workflow component is hidden
54
+ useDynamicWorkflows()
55
+
56
  const [{ isOver, canDrop }, connectFileDrop] = useDrop({
57
  accept: [NativeTypes.FILE],
58
  drop: (item: DroppableThing): void => {
 
235
  : 'pointer-events-none hidden',
236
  `fixed top-9 h-[calc(100vh-36px)] w-screen flex-row overflow-hidden`,
237
  `items-center justify-center`,
238
+ `bg-neutral-950`
239
  )}
240
  >
241
  <div
 
316
  )
317
  }
318
 
319
+ function fallbackRender({ error, resetErrorBoundary }: FallbackProps) {
320
+ // Call resetErrorBoundary() to reset the error boundary and retry the render.
321
+
322
+ return (
323
+ <div role="alert">
324
+ <p>Something went wrong:</p>
325
+ <pre style={{ color: 'red' }}>{error.message}</pre>
326
+ </div>
327
+ )
328
+ }
329
+
330
  export function Main() {
331
  return (
332
  <TooltipProvider>
333
  <DndProvider backend={HTML5Backend}>
334
+ <ErrorBoundary fallbackRender={fallbackRender}>
335
+ <MainContent />
336
+ </ErrorBoundary>
337
  </DndProvider>
338
  </TooltipProvider>
339
  )
src/components/dialogs/iframe-warning/index.tsx CHANGED
@@ -18,7 +18,7 @@ export function IframeWarning() {
18
  return (
19
  <div
20
  className={cn(
21
- `fixed bottom-0 left-0 right-0 top-0 z-[999] m-0 flex h-screen w-screen items-center justify-center overflow-hidden bg-stone-950 p-0 text-center`,
22
  showWarning
23
  ? 'pointer-events-auto opacity-100'
24
  : 'pointer-events-none opacity-0'
@@ -26,7 +26,7 @@ export function IframeWarning() {
26
  style={{ backgroundImage: theme.wallpaperBgImage }}
27
  >
28
  <p
29
- className={cn('font-sans text-stone-100')}
30
  style={{ textShadow: '#000 1px 0 3px' }}
31
  >
32
  <span className="text-[2vw] font-thin">
 
18
  return (
19
  <div
20
  className={cn(
21
+ `fixed bottom-0 left-0 right-0 top-0 z-[999] m-0 flex h-screen w-screen items-center justify-center overflow-hidden bg-neutral-950 p-0 text-center`,
22
  showWarning
23
  ? 'pointer-events-auto opacity-100'
24
  : 'pointer-events-none opacity-0'
 
26
  style={{ backgroundImage: theme.wallpaperBgImage }}
27
  >
28
  <p
29
+ className={cn('font-sans text-neutral-100')}
30
  style={{ textShadow: '#000 1px 0 3px' }}
31
  >
32
  <span className="text-[2vw] font-thin">
src/components/dialogs/loader/index.tsx CHANGED
@@ -27,7 +27,7 @@ export function DeprecatedLoader({
27
  )}
28
  >
29
  <p
30
- className={cn('font-sans text-[3vw] font-thin text-stone-100')}
31
  style={{ textShadow: '#000 1px 0 3px' }}
32
  >
33
  <span className="">Loading..</span>
 
27
  )}
28
  >
29
  <p
30
+ className={cn('font-sans text-[3vw] font-thin text-neutral-100')}
31
  style={{ textShadow: '#000 1px 0 3px' }}
32
  >
33
  <span className="">Loading..</span>
src/components/forms/FormSection.tsx CHANGED
@@ -15,7 +15,7 @@ export function FormSection({
15
  <div
16
  className={cn(
17
  '@container', // so that inputs can use container queries
18
- `flex h-full w-full flex-col space-y-2 overflow-y-scroll scrollbar scrollbar-track-stone-300 scrollbar-thumb-stone-700 scrollbar-corner-stone-500`,
19
  className
20
  )}
21
  >
 
15
  <div
16
  className={cn(
17
  '@container', // so that inputs can use container queries
18
+ `flex h-full w-full flex-col space-y-2 overflow-y-scroll scrollbar scrollbar-track-neutral-300 scrollbar-thumb-neutral-700 scrollbar-corner-neutral-500`,
19
  className
20
  )}
21
  >
src/components/mobile/MobileMenu.tsx ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ Sheet,
3
+ SheetContent,
4
+ SheetDescription,
5
+ SheetHeader,
6
+ SheetTitle,
7
+ SheetTrigger,
8
+ } from '@/components/ui/sheet'
9
+
10
+ export function MobileMenu() {
11
+ return (
12
+ <Sheet>
13
+ <SheetTrigger>Open</SheetTrigger>
14
+ <SheetContent>
15
+ <SheetHeader>
16
+ <SheetTitle>Clapper</SheetTitle>
17
+ <SheetDescription>
18
+ TODO JULIAN: we need a way to display the hierarchical menu on
19
+ mobile
20
+ </SheetDescription>
21
+ </SheetHeader>
22
+ </SheetContent>
23
+ </Sheet>
24
+ )
25
+ }
src/components/monitor/StaticPlayer/index.tsx CHANGED
@@ -54,7 +54,7 @@ export function StaticPlayer(
54
  }, [setStaticVideoRef])
55
 
56
  const placeholder = (
57
- <div className="text-center text-base text-stone-50/90 dark:text-stone-50/90">
58
  {error ? <span>{error}</span> : <span>No video yet</span>}
59
  </div>
60
  )
@@ -85,7 +85,7 @@ export function StaticPlayer(
85
  // autoPlay
86
  // muted
87
  loop
88
- className="h-full rounded-lg border border-stone-950 object-cover"
89
  style={{}}
90
  />
91
  ) : (
 
54
  }, [setStaticVideoRef])
55
 
56
  const placeholder = (
57
+ <div className="text-center text-base text-neutral-50/90 dark:text-neutral-50/90">
58
  {error ? <span>{error}</span> : <span>No video yet</span>}
59
  </div>
60
  )
 
85
  // autoPlay
86
  // muted
87
  loop
88
+ className="h-full rounded-lg border border-neutral-950 object-cover"
89
  style={{}}
90
  />
91
  ) : (
src/components/settings/index.tsx CHANGED
@@ -67,7 +67,7 @@ export function SettingsDialog() {
67
  <Button
68
  key={key}
69
  variant="ghost"
70
- className="flex w-full flex-col items-end text-right text-lg font-thin capitalize text-stone-300"
71
  onClick={() => setShowSettings(key as SettingsCategory)}
72
  >
73
  {panelLabels[key]}
@@ -76,14 +76,14 @@ export function SettingsDialog() {
76
  </div>
77
  </ScrollArea>
78
 
79
- <div className="flex h-full max-w-[calc(100%-150px)] flex-grow select-none flex-col justify-between border-l border-stone-800 pl-8">
80
  <ScrollArea className="flex h-full flex-row">
81
  {panels[showSettings]}
82
  </ScrollArea>
83
  <DialogFooter>
84
  <Button
85
  variant="outline"
86
- className="text-sm font-light dark:border-stone-700 dark:bg-stone-800 dark:text-stone-400"
87
  onClick={() => {
88
  setShowSettings(SettingsCategory.NONE)
89
  }}
 
67
  <Button
68
  key={key}
69
  variant="ghost"
70
+ className="flex w-full flex-col items-end text-right text-lg font-thin capitalize text-neutral-300"
71
  onClick={() => setShowSettings(key as SettingsCategory)}
72
  >
73
  {panelLabels[key]}
 
76
  </div>
77
  </ScrollArea>
78
 
79
+ <div className="flex h-full max-w-[calc(100%-150px)] flex-grow select-none flex-col justify-between border-l border-neutral-800 pl-8">
80
  <ScrollArea className="flex h-full flex-row">
81
  {panels[showSettings]}
82
  </ScrollArea>
83
  <DialogFooter>
84
  <Button
85
  variant="outline"
86
+ className="text-sm font-light dark:border-neutral-700 dark:bg-neutral-800 dark:text-neutral-400"
87
  onClick={() => {
88
  setShowSettings(SettingsCategory.NONE)
89
  }}
src/components/settings/provider.tsx CHANGED
@@ -17,7 +17,7 @@ function GetItHere({ href, children }: { href: string; children: ReactNode }) {
17
  <span>
18
  {children} (
19
  <a
20
- className="text-stone-500 underline hover:text-stone-400"
21
  href={href}
22
  target="_blank"
23
  >
@@ -42,6 +42,14 @@ export function SettingsSectionProvider() {
42
  const comfyUiClientId = useSettings((s) => s.comfyUiClientId)
43
  const setComfyUiClientId = useSettings((s) => s.setComfyUiClientId)
44
 
 
 
 
 
 
 
 
 
45
  const comfyIcuApiKey = useSettings((s) => s.comfyIcuApiKey)
46
  const setComfyIcuApiKey = useSettings((s) => s.setComfyIcuApiKey)
47
 
@@ -89,7 +97,7 @@ export function SettingsSectionProvider() {
89
  return (
90
  <div className="flex flex-col justify-between space-y-6">
91
  <FormSection label="Compute providers">
92
- <p className="max-w-80 text-sm italic text-stone-500">
93
  Note: preferences and credentials are stored inside your browser local
94
  storage.
95
  <br />
@@ -135,6 +143,22 @@ export function SettingsSectionProvider() {
135
  type="text"
136
  />
137
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  <FormInput
139
  label="Comfy.icu API key"
140
  value={comfyIcuApiKey}
 
17
  <span>
18
  {children} (
19
  <a
20
+ className="text-neutral-500 underline hover:text-neutral-400"
21
  href={href}
22
  target="_blank"
23
  >
 
42
  const comfyUiClientId = useSettings((s) => s.comfyUiClientId)
43
  const setComfyUiClientId = useSettings((s) => s.setComfyUiClientId)
44
 
45
+ const comfyUiHttpAuthLogin = useSettings((s) => s.comfyUiHttpAuthLogin)
46
+ const setComfyUiHttpAuthLogin = useSettings((s) => s.setComfyUiHttpAuthLogin)
47
+
48
+ const comfyUiHttpAuthPassword = useSettings((s) => s.comfyUiHttpAuthPassword)
49
+ const setComfyUiHttpAuthPassword = useSettings(
50
+ (s) => s.setComfyUiHttpAuthPassword
51
+ )
52
+
53
  const comfyIcuApiKey = useSettings((s) => s.comfyIcuApiKey)
54
  const setComfyIcuApiKey = useSettings((s) => s.setComfyIcuApiKey)
55
 
 
97
  return (
98
  <div className="flex flex-col justify-between space-y-6">
99
  <FormSection label="Compute providers">
100
+ <p className="max-w-80 text-sm italic text-neutral-500">
101
  Note: preferences and credentials are stored inside your browser local
102
  storage.
103
  <br />
 
143
  type="text"
144
  />
145
 
146
+ <FormInput
147
+ label="ComfyUI HTTP Auth login (optional)"
148
+ value={comfyUiHttpAuthLogin}
149
+ defaultValue={defaultSettings.comfyUiHttpAuthLogin}
150
+ onChange={setComfyUiHttpAuthLogin}
151
+ type="text"
152
+ />
153
+
154
+ <FormInput
155
+ label="ComfyUI HTTP Auth password (optional)"
156
+ value={comfyUiHttpAuthPassword}
157
+ defaultValue={defaultSettings.comfyUiHttpAuthPassword}
158
+ onChange={setComfyUiHttpAuthPassword}
159
+ type={apiKeyType}
160
+ />
161
+
162
  <FormInput
163
  label="Comfy.icu API key"
164
  value={comfyIcuApiKey}
src/components/tags/colors.ts CHANGED
@@ -14,7 +14,7 @@ export const tagColorToTailwindClass: Record<TagColor, string> = {
14
  [TagColor.GRAY]: `bg-gray-800 border-gray-600 text-gray-300`,
15
  [TagColor.ZINC]: `bg-zinc-800 border-zinc-600 text-zinc-300`,
16
  [TagColor.NEUTRAL]: `bg-neutral-800 border-neutral-600 text-neutral-300`,
17
- [TagColor.STONE]: `bg-stone-800 border-stone-600 text-stone-300`,
18
  [TagColor.RED]: `bg-red-800 border-red-600 text-red-300`,
19
  [TagColor.ORANGE]: `bg-orange-800 border-orange-600 text-orange-300`,
20
  [TagColor.AMBER]: `bg-amber-800 border-amber-600 text-amber-300`,
 
14
  [TagColor.GRAY]: `bg-gray-800 border-gray-600 text-gray-300`,
15
  [TagColor.ZINC]: `bg-zinc-800 border-zinc-600 text-zinc-300`,
16
  [TagColor.NEUTRAL]: `bg-neutral-800 border-neutral-600 text-neutral-300`,
17
+ [TagColor.STONE]: `bg-neutral-800 border-neutral-600 text-neutral-300`,
18
  [TagColor.RED]: `bg-red-800 border-red-600 text-red-300`,
19
  [TagColor.ORANGE]: `bg-orange-800 border-orange-600 text-orange-300`,
20
  [TagColor.AMBER]: `bg-amber-800 border-amber-600 text-amber-300`,
src/components/toolbars/bottom-bar/metrics/index.tsx CHANGED
@@ -13,8 +13,8 @@ export function Metrics() {
13
 
14
  return (
15
  <div className="flex flex-row space-x-1">
16
- <span className="text-stone-500">memory usage:</span>
17
- <span className="text-stone-400">
18
  {!bytes ? 'waiting..' : humanReadableString || ''}
19
  </span>
20
  </div>
 
13
 
14
  return (
15
  <div className="flex flex-row space-x-1">
16
+ <span className="text-neutral-500">memory usage:</span>
17
+ <span className="text-neutral-400">
18
  {!bytes ? 'waiting..' : humanReadableString || ''}
19
  </span>
20
  </div>
src/components/toolbars/editors-menu/EditorsSideMenuItem.tsx CHANGED
@@ -63,8 +63,8 @@ export function EditorsSideMenuItem({
63
  unmanaged || isActive ? '' : `cursor-pointer`,
64
  `border-l-[2px]`,
65
  isActive
66
- ? 'fill-stone-50/80 text-stone-50 opacity-100 hover:fill-stone-50 hover:text-stone-50'
67
- : 'fill-stone-400/80 text-gray-400 opacity-80 hover:fill-stone-200 hover:text-stone-200 hover:opacity-100',
68
  `group`
69
  )}
70
  style={{
 
63
  unmanaged || isActive ? '' : `cursor-pointer`,
64
  `border-l-[2px]`,
65
  isActive
66
+ ? 'fill-neutral-50/80 text-neutral-50 opacity-100 hover:fill-neutral-50 hover:text-neutral-50'
67
+ : 'fill-neutral-400/80 text-gray-400 opacity-80 hover:fill-neutral-200 hover:text-neutral-200 hover:opacity-100',
68
  `group`
69
  )}
70
  style={{
src/components/toolbars/system-menu/index.tsx CHANGED
@@ -23,7 +23,7 @@ const Menubar = React.forwardRef<
23
  <MenubarPrimitive.Root
24
  ref={ref}
25
  className={cn(
26
- 'border-bottom flex h-9 items-center space-x-1 rounded-md border-stone-800 bg-white p-1 dark:border-stone-800 dark:bg-stone-800',
27
  className
28
  )}
29
  {...props}
@@ -38,7 +38,7 @@ const MenubarTrigger = React.forwardRef<
38
  <MenubarPrimitive.Trigger
39
  ref={ref}
40
  className={cn(
41
- 'flex cursor-default select-none items-center rounded-sm px-3 py-1 text-sm font-light outline-none data-[state=open]:bg-stone-100 data-[state=open]:text-stone-900 focus:bg-stone-100 focus:text-stone-900 dark:data-[state=open]:bg-stone-700 dark:data-[state=open]:text-stone-50 dark:focus:bg-stone-700 dark:focus:text-stone-50',
42
  className
43
  )}
44
  {...props}
@@ -55,7 +55,7 @@ const MenubarSubTrigger = React.forwardRef<
55
  <MenubarPrimitive.SubTrigger
56
  ref={ref}
57
  className={cn(
58
- 'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[state=open]:bg-stone-100 data-[state=open]:text-stone-900 focus:bg-stone-100 focus:text-stone-900 dark:data-[state=open]:bg-stone-800 dark:data-[state=open]:text-stone-100 dark:focus:bg-stone-700 dark:focus:text-stone-50',
59
  inset && 'pl-8',
60
  className
61
  )}
@@ -74,7 +74,7 @@ const MenubarSubContent = React.forwardRef<
74
  <MenubarPrimitive.SubContent
75
  ref={ref}
76
  className={cn(
77
- 'z-50 min-w-[8rem] overflow-hidden rounded-md border-transparent bg-white p-1 text-stone-950 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 dark:border-stone-700 dark:bg-stone-800 dark:text-stone-100',
78
  className
79
  )}
80
  {...props}
@@ -97,7 +97,7 @@ const MenubarContent = React.forwardRef<
97
  alignOffset={alignOffset}
98
  sideOffset={sideOffset}
99
  className={cn(
100
- 'z-50 min-w-[12rem] overflow-hidden rounded-md border border-stone-200 bg-white p-1 text-stone-950 shadow-md data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 dark:border-stone-700 dark:bg-stone-800 dark:text-stone-100',
101
  className
102
  )}
103
  {...props}
@@ -116,7 +116,7 @@ const MenubarItem = React.forwardRef<
116
  <MenubarPrimitive.Item
117
  ref={ref}
118
  className={cn(
119
- 'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-stone-100 focus:text-stone-900 dark:focus:bg-stone-700 dark:focus:text-stone-50',
120
  inset && 'pl-8',
121
  className
122
  )}
@@ -132,7 +132,7 @@ const MenubarCheckboxItem = React.forwardRef<
132
  <MenubarPrimitive.CheckboxItem
133
  ref={ref}
134
  className={cn(
135
- 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-stone-100 focus:text-stone-900 dark:focus:bg-stone-700 dark:focus:text-stone-100',
136
  className
137
  )}
138
  checked={checked}
@@ -155,7 +155,7 @@ const MenubarRadioItem = React.forwardRef<
155
  <MenubarPrimitive.RadioItem
156
  ref={ref}
157
  className={cn(
158
- 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-stone-100 focus:text-stone-900 dark:focus:bg-stone-700 dark:focus:text-stone-100',
159
  className
160
  )}
161
  {...props}
@@ -194,7 +194,10 @@ const MenubarSeparator = React.forwardRef<
194
  >(({ className, ...props }, ref) => (
195
  <MenubarPrimitive.Separator
196
  ref={ref}
197
- className={cn('-mx-1 my-1 h-px bg-stone-100 dark:bg-stone-700', className)}
 
 
 
198
  {...props}
199
  />
200
  ))
@@ -207,7 +210,7 @@ const MenubarShortcut = ({
207
  return (
208
  <span
209
  className={cn(
210
- 'ml-auto text-xs tracking-widest text-stone-500 dark:text-stone-400',
211
  className
212
  )}
213
  {...props}
 
23
  <MenubarPrimitive.Root
24
  ref={ref}
25
  className={cn(
26
+ 'border-bottom flex h-9 items-center space-x-1 rounded-md border-neutral-800 bg-white p-1 dark:border-neutral-800 dark:bg-neutral-800',
27
  className
28
  )}
29
  {...props}
 
38
  <MenubarPrimitive.Trigger
39
  ref={ref}
40
  className={cn(
41
+ 'flex cursor-default select-none items-center rounded-sm px-3 py-1 text-sm font-light outline-none data-[state=open]:bg-neutral-100 data-[state=open]:text-neutral-900 focus:bg-neutral-100 focus:text-neutral-900 dark:data-[state=open]:bg-neutral-700 dark:data-[state=open]:text-neutral-50 dark:focus:bg-neutral-700 dark:focus:text-neutral-50',
42
  className
43
  )}
44
  {...props}
 
55
  <MenubarPrimitive.SubTrigger
56
  ref={ref}
57
  className={cn(
58
+ 'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[state=open]:bg-neutral-100 data-[state=open]:text-neutral-900 focus:bg-neutral-100 focus:text-neutral-900 dark:data-[state=open]:bg-neutral-800 dark:data-[state=open]:text-neutral-100 dark:focus:bg-neutral-700 dark:focus:text-neutral-50',
59
  inset && 'pl-8',
60
  className
61
  )}
 
74
  <MenubarPrimitive.SubContent
75
  ref={ref}
76
  className={cn(
77
+ 'z-50 min-w-[8rem] overflow-hidden rounded-md border-transparent bg-white p-1 text-neutral-950 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 dark:border-neutral-700 dark:bg-neutral-800 dark:text-neutral-100',
78
  className
79
  )}
80
  {...props}
 
97
  alignOffset={alignOffset}
98
  sideOffset={sideOffset}
99
  className={cn(
100
+ 'z-50 min-w-[12rem] overflow-hidden rounded-md border border-neutral-200 bg-white p-1 text-neutral-950 shadow-md data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 dark:border-neutral-700 dark:bg-neutral-800 dark:text-neutral-100',
101
  className
102
  )}
103
  {...props}
 
116
  <MenubarPrimitive.Item
117
  ref={ref}
118
  className={cn(
119
+ 'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-neutral-100 focus:text-neutral-900 dark:focus:bg-neutral-700 dark:focus:text-neutral-50',
120
  inset && 'pl-8',
121
  className
122
  )}
 
132
  <MenubarPrimitive.CheckboxItem
133
  ref={ref}
134
  className={cn(
135
+ 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-neutral-100 focus:text-neutral-900 dark:focus:bg-neutral-700 dark:focus:text-neutral-100',
136
  className
137
  )}
138
  checked={checked}
 
155
  <MenubarPrimitive.RadioItem
156
  ref={ref}
157
  className={cn(
158
+ 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-neutral-100 focus:text-neutral-900 dark:focus:bg-neutral-700 dark:focus:text-neutral-100',
159
  className
160
  )}
161
  {...props}
 
194
  >(({ className, ...props }, ref) => (
195
  <MenubarPrimitive.Separator
196
  ref={ref}
197
+ className={cn(
198
+ '-mx-1 my-1 h-px bg-neutral-100 dark:bg-neutral-700',
199
+ className
200
+ )}
201
  {...props}
202
  />
203
  ))
 
210
  return (
211
  <span
212
  className={cn(
213
+ 'ml-auto text-xs tracking-widest text-neutral-500 dark:text-neutral-400',
214
  className
215
  )}
216
  {...props}
src/components/toolbars/top-menu/ToggleView/index.tsx CHANGED
@@ -31,7 +31,7 @@ export function ToggleView({
31
  >
32
  <div
33
  className={cn(
34
- `flex transition-all duration-200 ease-in-out group-hover:scale-110 group-hover:border-stone-100`,
35
  isVisible
36
  ? `border-neutral-100 bg-neutral-400`
37
  : `border-neutral-400`,
 
31
  >
32
  <div
33
  className={cn(
34
+ `flex transition-all duration-200 ease-in-out group-hover:scale-110 group-hover:border-neutral-100`,
35
  isVisible
36
  ? `border-neutral-100 bg-neutral-400`
37
  : `border-neutral-400`,
src/components/toolbars/top-menu/index.tsx CHANGED
@@ -143,7 +143,7 @@ export function TopMenu() {
143
 
144
  <Tooltip>
145
  <TooltipTrigger className="">
146
- <div className="ml-2 scale-100 cursor-pointer text-stone-300/70 transition-all duration-200 ease-in-out hover:scale-110 hover:text-stone-100/90">
147
  <a href="https://discord.gg/AEruz9B92B" target='"_blank'>
148
  <TbBrandDiscord className="h-4 w-4" />
149
  </a>
@@ -156,7 +156,7 @@ export function TopMenu() {
156
 
157
  <Tooltip>
158
  <TooltipTrigger className="">
159
- <div className="scale-100 cursor-pointer text-stone-300/70 transition-all duration-200 ease-in-out hover:scale-110 hover:text-stone-100/90">
160
  <a href="https://github.com/jbilcke-hf/clapper" target='"_blank'>
161
  <FaGithubAlt className="h-4 w-4" />
162
  </a>
 
143
 
144
  <Tooltip>
145
  <TooltipTrigger className="">
146
+ <div className="ml-2 scale-100 cursor-pointer text-neutral-300/70 transition-all duration-200 ease-in-out hover:scale-110 hover:text-neutral-100/90">
147
  <a href="https://discord.gg/AEruz9B92B" target='"_blank'>
148
  <TbBrandDiscord className="h-4 w-4" />
149
  </a>
 
156
 
157
  <Tooltip>
158
  <TooltipTrigger className="">
159
+ <div className="scale-100 cursor-pointer text-neutral-300/70 transition-all duration-200 ease-in-out hover:scale-110 hover:text-neutral-100/90">
160
  <a href="https://github.com/jbilcke-hf/clapper" target='"_blank'>
161
  <FaGithubAlt className="h-4 w-4" />
162
  </a>
src/components/ui/alert.tsx CHANGED
@@ -4,11 +4,12 @@ import { cva, type VariantProps } from 'class-variance-authority'
4
  import { cn } from '@/lib/utils/cn'
5
 
6
  const alertVariants = cva(
7
- 'relative w-full rounded-lg border border-stone-200 p-4 [&:has(svg)]:pl-11 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-stone-950 dark:border-stone-800 dark:[&>svg]:text-stone-50',
8
  {
9
  variants: {
10
  variant: {
11
- default: 'bg-white text-stone-950 dark:bg-stone-950 dark:text-stone-50',
 
12
  destructive:
13
  'border-red-500/50 text-red-500 dark:border-red-500 [&>svg]:text-red-500 dark:border-red-900/50 dark:text-red-900 dark:dark:border-red-900 dark:[&>svg]:text-red-900',
14
  },
 
4
  import { cn } from '@/lib/utils/cn'
5
 
6
  const alertVariants = cva(
7
+ 'relative w-full rounded-lg border border-neutral-200 p-4 [&:has(svg)]:pl-11 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-neutral-950 dark:border-neutral-800 dark:[&>svg]:text-neutral-50',
8
  {
9
  variants: {
10
  variant: {
11
+ default:
12
+ 'bg-white text-neutral-950 dark:bg-neutral-950 dark:text-neutral-50',
13
  destructive:
14
  'border-red-500/50 text-red-500 dark:border-red-500 [&>svg]:text-red-500 dark:border-red-900/50 dark:text-red-900 dark:dark:border-red-900 dark:[&>svg]:text-red-900',
15
  },
src/components/ui/avatar.tsx CHANGED
@@ -39,7 +39,7 @@ const AvatarFallback = React.forwardRef<
39
  <AvatarPrimitive.Fallback
40
  ref={ref}
41
  className={cn(
42
- 'flex h-full w-full items-center justify-center rounded-full bg-stone-100 dark:bg-stone-800',
43
  className
44
  )}
45
  {...props}
 
39
  <AvatarPrimitive.Fallback
40
  ref={ref}
41
  className={cn(
42
+ 'flex h-full w-full items-center justify-center rounded-full bg-neutral-100 dark:bg-neutral-800',
43
  className
44
  )}
45
  {...props}
src/components/ui/badge.tsx CHANGED
@@ -4,17 +4,17 @@ import { cva, type VariantProps } from 'class-variance-authority'
4
  import { cn } from '@/lib/utils/cn'
5
 
6
  const badgeVariants = cva(
7
- 'inline-flex items-center rounded-full border border-stone-200 px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-stone-400 focus:ring-offset-2 dark:border-stone-800 dark:focus:ring-stone-800',
8
  {
9
  variants: {
10
  variant: {
11
  default:
12
- 'border-transparent bg-stone-900 text-stone-50 hover:bg-stone-900/80 dark:bg-stone-50 dark:text-stone-900 dark:hover:bg-stone-50/80',
13
  secondary:
14
- 'border-transparent bg-stone-100 text-stone-900 hover:bg-stone-100/80 dark:bg-stone-800 dark:text-stone-50 dark:hover:bg-stone-800/80',
15
  destructive:
16
- 'border-transparent bg-red-500 text-stone-50 hover:bg-red-500/80 dark:bg-red-900 dark:text-red-50 dark:hover:bg-red-900/80',
17
- outline: 'text-stone-950 dark:text-stone-50',
18
  },
19
  },
20
  defaultVariants: {
 
4
  import { cn } from '@/lib/utils/cn'
5
 
6
  const badgeVariants = cva(
7
+ 'inline-flex items-center rounded-full border border-neutral-200 px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-neutral-400 focus:ring-offset-2 dark:border-neutral-800 dark:focus:ring-neutral-800',
8
  {
9
  variants: {
10
  variant: {
11
  default:
12
+ 'border-transparent bg-neutral-900 text-neutral-50 hover:bg-neutral-900/80 dark:bg-neutral-50 dark:text-neutral-900 dark:hover:bg-neutral-50/80',
13
  secondary:
14
+ 'border-transparent bg-neutral-100 text-neutral-900 hover:bg-neutral-100/80 dark:bg-neutral-800 dark:text-neutral-50 dark:hover:bg-neutral-800/80',
15
  destructive:
16
+ 'border-transparent bg-red-500 text-neutral-50 hover:bg-red-500/80 dark:bg-red-900 dark:text-red-50 dark:hover:bg-red-900/80',
17
+ outline: 'text-neutral-950 dark:text-neutral-50',
18
  },
19
  },
20
  defaultVariants: {
src/components/ui/button.tsx CHANGED
@@ -5,21 +5,21 @@ import { cva, type VariantProps } from 'class-variance-authority'
5
  import { cn } from '@/lib/utils/cn'
6
 
7
  const buttonVariants = cva(
8
- 'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-stone-400 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-stone-950 dark:focus-visible:ring-stone-800',
9
  {
10
  variants: {
11
  variant: {
12
  default:
13
- 'bg-stone-900 text-stone-50 hover:bg-stone-900/90 dark:bg-stone-50 dark:text-stone-900 dark:hover:bg-stone-50/90',
14
  destructive:
15
- 'bg-red-500 text-stone-50 hover:bg-red-500/90 dark:bg-red-900 dark:text-red-50 dark:hover:bg-red-900/90',
16
  outline:
17
- 'border border-stone-200 bg-white hover:bg-stone-100 hover:text-stone-900 dark:border-stone-800 dark:bg-stone-950 dark:hover:bg-stone-800 dark:hover:text-stone-50',
18
  secondary:
19
- 'bg-stone-100 text-stone-900 hover:bg-stone-100/80 dark:bg-stone-800 dark:text-stone-50 dark:hover:bg-stone-800/80',
20
  ghost:
21
- 'hover:bg-stone-100 hover:text-stone-900 dark:hover:bg-stone-800 dark:hover:text-stone-50',
22
- link: 'text-stone-900 underline-offset-4 hover:underline dark:text-stone-50',
23
  },
24
  size: {
25
  default: 'h-10 px-4 py-2',
 
5
  import { cn } from '@/lib/utils/cn'
6
 
7
  const buttonVariants = cva(
8
+ 'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-400 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-neutral-950 dark:focus-visible:ring-neutral-800',
9
  {
10
  variants: {
11
  variant: {
12
  default:
13
+ 'bg-neutral-900 text-neutral-50 hover:bg-neutral-900/90 dark:bg-neutral-50 dark:text-neutral-900 dark:hover:bg-neutral-50/90',
14
  destructive:
15
+ 'bg-red-500 text-neutral-50 hover:bg-red-500/90 dark:bg-red-900 dark:text-red-50 dark:hover:bg-red-900/90',
16
  outline:
17
+ 'border border-neutral-200 bg-white hover:bg-neutral-100 hover:text-neutral-900 dark:border-neutral-800 dark:bg-neutral-950 dark:hover:bg-neutral-800 dark:hover:text-neutral-50',
18
  secondary:
19
+ 'bg-neutral-100 text-neutral-900 hover:bg-neutral-100/80 dark:bg-neutral-800 dark:text-neutral-50 dark:hover:bg-neutral-800/80',
20
  ghost:
21
+ 'hover:bg-neutral-100 hover:text-neutral-900 dark:hover:bg-neutral-800 dark:hover:text-neutral-50',
22
+ link: 'text-neutral-900 underline-offset-4 hover:underline dark:text-neutral-50',
23
  },
24
  size: {
25
  default: 'h-10 px-4 py-2',
src/components/ui/card.tsx CHANGED
@@ -9,7 +9,7 @@ const Card = React.forwardRef<
9
  <div
10
  ref={ref}
11
  className={cn(
12
- 'rounded-lg border border-stone-200 bg-white text-stone-950 shadow-sm dark:border-stone-800 dark:bg-stone-950 dark:text-stone-50',
13
  className
14
  )}
15
  {...props}
@@ -50,7 +50,7 @@ const CardDescription = React.forwardRef<
50
  >(({ className, ...props }, ref) => (
51
  <p
52
  ref={ref}
53
- className={cn('text-sm text-stone-500 dark:text-stone-400', className)}
54
  {...props}
55
  />
56
  ))
 
9
  <div
10
  ref={ref}
11
  className={cn(
12
+ 'rounded-lg border border-neutral-200 bg-white text-neutral-950 shadow-sm dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50',
13
  className
14
  )}
15
  {...props}
 
50
  >(({ className, ...props }, ref) => (
51
  <p
52
  ref={ref}
53
+ className={cn('text-sm text-neutral-500 dark:text-neutral-400', className)}
54
  {...props}
55
  />
56
  ))
src/components/ui/checkbox.tsx CHANGED
@@ -13,7 +13,7 @@ const Checkbox = React.forwardRef<
13
  <CheckboxPrimitive.Root
14
  ref={ref}
15
  className={cn(
16
- 'peer h-4 w-4 shrink-0 rounded-sm border border-stone-200 border-stone-900 ring-offset-white data-[state=checked]:bg-stone-900 data-[state=checked]:text-stone-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-stone-400 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-stone-50 dark:border-stone-800 dark:ring-offset-stone-950 dark:data-[state=checked]:bg-stone-50 dark:data-[state=checked]:text-stone-900 dark:focus-visible:ring-stone-800',
17
  className
18
  )}
19
  {...props}
 
13
  <CheckboxPrimitive.Root
14
  ref={ref}
15
  className={cn(
16
+ 'peer h-4 w-4 shrink-0 rounded-sm border border-neutral-200 border-neutral-900 ring-offset-white data-[state=checked]:bg-neutral-900 data-[state=checked]:text-neutral-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-400 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-neutral-50 dark:border-neutral-800 dark:ring-offset-neutral-950 dark:data-[state=checked]:bg-neutral-50 dark:data-[state=checked]:text-neutral-900 dark:focus-visible:ring-neutral-800',
17
  className
18
  )}
19
  {...props}
src/components/ui/command.tsx CHANGED
@@ -15,7 +15,7 @@ const Command = React.forwardRef<
15
  <CommandPrimitive
16
  ref={ref}
17
  className={cn(
18
- 'flex h-full w-full flex-col overflow-hidden rounded-md bg-white text-stone-950 dark:bg-stone-950 dark:text-stone-50',
19
  className
20
  )}
21
  {...props}
@@ -29,7 +29,7 @@ const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
29
  return (
30
  <Dialog {...props}>
31
  <DialogContent className="overflow-hidden p-0 shadow-lg">
32
- <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-stone-500 dark:[&_[cmdk-group-heading]]:text-stone-400 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
33
  {children}
34
  </Command>
35
  </DialogContent>
@@ -46,7 +46,7 @@ const CommandInput = React.forwardRef<
46
  <CommandPrimitive.Input
47
  ref={ref}
48
  className={cn(
49
- 'flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-stone-500 disabled:cursor-not-allowed disabled:opacity-50 dark:placeholder:text-stone-400',
50
  className
51
  )}
52
  {...props}
@@ -89,7 +89,7 @@ const CommandGroup = React.forwardRef<
89
  <CommandPrimitive.Group
90
  ref={ref}
91
  className={cn(
92
- 'overflow-hidden p-1 text-stone-950 dark:text-stone-50 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-stone-500 dark:[&_[cmdk-group-heading]]:text-stone-400',
93
  className
94
  )}
95
  {...props}
@@ -104,7 +104,7 @@ const CommandSeparator = React.forwardRef<
104
  >(({ className, ...props }, ref) => (
105
  <CommandPrimitive.Separator
106
  ref={ref}
107
- className={cn('-mx-1 h-px bg-stone-200 dark:bg-stone-800', className)}
108
  {...props}
109
  />
110
  ))
@@ -117,7 +117,7 @@ const CommandItem = React.forwardRef<
117
  <CommandPrimitive.Item
118
  ref={ref}
119
  className={cn(
120
- 'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-stone-100 aria-selected:text-stone-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:aria-selected:bg-stone-800 dark:aria-selected:text-stone-50',
121
  className
122
  )}
123
  {...props}
@@ -133,7 +133,7 @@ const CommandShortcut = ({
133
  return (
134
  <span
135
  className={cn(
136
- 'ml-auto text-xs tracking-widest text-stone-500 dark:text-stone-400',
137
  className
138
  )}
139
  {...props}
 
15
  <CommandPrimitive
16
  ref={ref}
17
  className={cn(
18
+ 'flex h-full w-full flex-col overflow-hidden rounded-md bg-white text-neutral-950 dark:bg-neutral-950 dark:text-neutral-50',
19
  className
20
  )}
21
  {...props}
 
29
  return (
30
  <Dialog {...props}>
31
  <DialogContent className="overflow-hidden p-0 shadow-lg">
32
+ <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-neutral-500 dark:[&_[cmdk-group-heading]]:text-neutral-400 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
33
  {children}
34
  </Command>
35
  </DialogContent>
 
46
  <CommandPrimitive.Input
47
  ref={ref}
48
  className={cn(
49
+ 'flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-neutral-500 disabled:cursor-not-allowed disabled:opacity-50 dark:placeholder:text-neutral-400',
50
  className
51
  )}
52
  {...props}
 
89
  <CommandPrimitive.Group
90
  ref={ref}
91
  className={cn(
92
+ 'overflow-hidden p-1 text-neutral-950 dark:text-neutral-50 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-neutral-500 dark:[&_[cmdk-group-heading]]:text-neutral-400',
93
  className
94
  )}
95
  {...props}
 
104
  >(({ className, ...props }, ref) => (
105
  <CommandPrimitive.Separator
106
  ref={ref}
107
+ className={cn('-mx-1 h-px bg-neutral-200 dark:bg-neutral-800', className)}
108
  {...props}
109
  />
110
  ))
 
117
  <CommandPrimitive.Item
118
  ref={ref}
119
  className={cn(
120
+ 'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-neutral-100 aria-selected:text-neutral-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:aria-selected:bg-neutral-800 dark:aria-selected:text-neutral-50',
121
  className
122
  )}
123
  {...props}
 
133
  return (
134
  <span
135
  className={cn(
136
+ 'ml-auto text-xs tracking-widest text-neutral-500 dark:text-neutral-400',
137
  className
138
  )}
139
  {...props}
src/components/ui/dialog.tsx CHANGED
@@ -22,7 +22,7 @@ const DialogOverlay = React.forwardRef<
22
  <DialogPrimitive.Overlay
23
  ref={ref}
24
  className={cn(
25
- 'fixed inset-0 z-50 blur-sm backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 dark:bg-stone-900/50',
26
  className
27
  )}
28
  {...props}
@@ -39,13 +39,13 @@ const DialogContent = React.forwardRef<
39
  <DialogPrimitive.Content
40
  ref={ref}
41
  className={cn(
42
- 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-stone-200 bg-white p-6 shadow-sm duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] dark:border-stone-800 dark:bg-stone-900 sm:rounded-lg md:w-full',
43
  className
44
  )}
45
  {...props}
46
  >
47
  {children}
48
- <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity data-[state=open]:bg-stone-100 data-[state=open]:text-stone-500 hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-stone-400 focus:ring-offset-2 disabled:pointer-events-none dark:ring-offset-stone-950 dark:data-[state=open]:bg-stone-800 dark:data-[state=open]:text-stone-400 dark:focus:ring-stone-800">
49
  <X className="h-4 w-4" />
50
  <span className="sr-only">Close</span>
51
  </DialogPrimitive.Close>
@@ -103,7 +103,7 @@ const DialogDescription = React.forwardRef<
103
  >(({ className, ...props }, ref) => (
104
  <DialogPrimitive.Description
105
  ref={ref}
106
- className={cn('text-sm text-stone-500 dark:text-stone-400', className)}
107
  {...props}
108
  />
109
  ))
 
22
  <DialogPrimitive.Overlay
23
  ref={ref}
24
  className={cn(
25
+ 'fixed inset-0 z-50 blur-sm backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 dark:bg-neutral-900/50',
26
  className
27
  )}
28
  {...props}
 
39
  <DialogPrimitive.Content
40
  ref={ref}
41
  className={cn(
42
+ 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-neutral-200 bg-white p-6 shadow-sm duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] dark:border-neutral-800 dark:bg-neutral-900 sm:rounded-lg md:w-full',
43
  className
44
  )}
45
  {...props}
46
  >
47
  {children}
48
+ <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity data-[state=open]:bg-neutral-100 data-[state=open]:text-neutral-500 hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-neutral-400 focus:ring-offset-2 disabled:pointer-events-none dark:ring-offset-neutral-950 dark:data-[state=open]:bg-neutral-800 dark:data-[state=open]:text-neutral-400 dark:focus:ring-neutral-800">
49
  <X className="h-4 w-4" />
50
  <span className="sr-only">Close</span>
51
  </DialogPrimitive.Close>
 
103
  >(({ className, ...props }, ref) => (
104
  <DialogPrimitive.Description
105
  ref={ref}
106
+ className={cn('text-sm text-neutral-500 dark:text-neutral-400', className)}
107
  {...props}
108
  />
109
  ))
src/components/ui/dropdown-menu.tsx CHANGED
@@ -27,7 +27,7 @@ const DropdownMenuSubTrigger = React.forwardRef<
27
  <DropdownMenuPrimitive.SubTrigger
28
  ref={ref}
29
  className={cn(
30
- 'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[state=open]:bg-stone-100 focus:bg-stone-100 dark:data-[state=open]:bg-stone-800 dark:focus:bg-stone-800',
31
  inset && 'pl-8',
32
  className
33
  )}
@@ -47,7 +47,7 @@ const DropdownMenuSubContent = React.forwardRef<
47
  <DropdownMenuPrimitive.SubContent
48
  ref={ref}
49
  className={cn(
50
- 'z-50 min-w-[8rem] overflow-hidden rounded-md border border-stone-200 bg-white p-1 text-stone-950 shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 dark:border-stone-800 dark:bg-stone-950 dark:text-stone-50',
51
  className
52
  )}
53
  {...props}
@@ -65,7 +65,7 @@ const DropdownMenuContent = React.forwardRef<
65
  ref={ref}
66
  sideOffset={sideOffset}
67
  className={cn(
68
- 'z-50 min-w-[8rem] overflow-hidden rounded-md border border-stone-200 bg-white p-1 text-stone-950 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 dark:border-stone-800 dark:bg-stone-950 dark:text-stone-50',
69
  className
70
  )}
71
  {...props}
@@ -83,7 +83,7 @@ const DropdownMenuItem = React.forwardRef<
83
  <DropdownMenuPrimitive.Item
84
  ref={ref}
85
  className={cn(
86
- 'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-stone-100 focus:text-stone-900 dark:focus:bg-stone-800 dark:focus:text-stone-50',
87
  inset && 'pl-8',
88
  className
89
  )}
@@ -99,7 +99,7 @@ const DropdownMenuCheckboxItem = React.forwardRef<
99
  <DropdownMenuPrimitive.CheckboxItem
100
  ref={ref}
101
  className={cn(
102
- 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-stone-100 focus:text-stone-900 dark:focus:bg-stone-800 dark:focus:text-stone-50',
103
  className
104
  )}
105
  checked={checked}
@@ -123,7 +123,7 @@ const DropdownMenuRadioItem = React.forwardRef<
123
  <DropdownMenuPrimitive.RadioItem
124
  ref={ref}
125
  className={cn(
126
- 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-stone-100 focus:text-stone-900 dark:focus:bg-stone-800 dark:focus:text-stone-50',
127
  className
128
  )}
129
  {...props}
@@ -162,7 +162,10 @@ const DropdownMenuSeparator = React.forwardRef<
162
  >(({ className, ...props }, ref) => (
163
  <DropdownMenuPrimitive.Separator
164
  ref={ref}
165
- className={cn('-mx-1 my-1 h-px bg-stone-100 dark:bg-stone-800', className)}
 
 
 
166
  {...props}
167
  />
168
  ))
 
27
  <DropdownMenuPrimitive.SubTrigger
28
  ref={ref}
29
  className={cn(
30
+ 'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[state=open]:bg-neutral-100 focus:bg-neutral-100 dark:data-[state=open]:bg-neutral-800 dark:focus:bg-neutral-800',
31
  inset && 'pl-8',
32
  className
33
  )}
 
47
  <DropdownMenuPrimitive.SubContent
48
  ref={ref}
49
  className={cn(
50
+ 'z-50 min-w-[8rem] overflow-hidden rounded-md border border-neutral-200 bg-white p-1 text-neutral-950 shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50',
51
  className
52
  )}
53
  {...props}
 
65
  ref={ref}
66
  sideOffset={sideOffset}
67
  className={cn(
68
+ 'z-50 min-w-[8rem] overflow-hidden rounded-md border border-neutral-200 bg-white p-1 text-neutral-950 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50',
69
  className
70
  )}
71
  {...props}
 
83
  <DropdownMenuPrimitive.Item
84
  ref={ref}
85
  className={cn(
86
+ 'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-neutral-100 focus:text-neutral-900 dark:focus:bg-neutral-800 dark:focus:text-neutral-50',
87
  inset && 'pl-8',
88
  className
89
  )}
 
99
  <DropdownMenuPrimitive.CheckboxItem
100
  ref={ref}
101
  className={cn(
102
+ 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-neutral-100 focus:text-neutral-900 dark:focus:bg-neutral-800 dark:focus:text-neutral-50',
103
  className
104
  )}
105
  checked={checked}
 
123
  <DropdownMenuPrimitive.RadioItem
124
  ref={ref}
125
  className={cn(
126
+ 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-neutral-100 focus:text-neutral-900 dark:focus:bg-neutral-800 dark:focus:text-neutral-50',
127
  className
128
  )}
129
  {...props}
 
162
  >(({ className, ...props }, ref) => (
163
  <DropdownMenuPrimitive.Separator
164
  ref={ref}
165
+ className={cn(
166
+ '-mx-1 my-1 h-px bg-neutral-100 dark:bg-neutral-800',
167
+ className
168
+ )}
169
  {...props}
170
  />
171
  ))
src/components/ui/popover.tsx CHANGED
@@ -19,7 +19,7 @@ const PopoverContent = React.forwardRef<
19
  align={align}
20
  sideOffset={sideOffset}
21
  className={cn(
22
- 'z-50 w-72 rounded-md border border-stone-200 bg-white p-4 text-stone-950 shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 dark:border-stone-800 dark:bg-stone-950 dark:text-stone-50',
23
  className
24
  )}
25
  {...props}
 
19
  align={align}
20
  sideOffset={sideOffset}
21
  className={cn(
22
+ 'z-50 w-72 rounded-md border border-neutral-200 bg-white p-4 text-neutral-950 shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50',
23
  className
24
  )}
25
  {...props}
src/components/ui/progress.tsx CHANGED
@@ -12,13 +12,13 @@ const Progress = React.forwardRef<
12
  <ProgressPrimitive.Root
13
  ref={ref}
14
  className={cn(
15
- 'relative h-4 w-full overflow-hidden rounded-full bg-stone-100 dark:bg-stone-800',
16
  className
17
  )}
18
  {...props}
19
  >
20
  <ProgressPrimitive.Indicator
21
- className="h-full w-full flex-1 bg-stone-900 transition-all dark:bg-stone-50"
22
  style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
23
  />
24
  </ProgressPrimitive.Root>
 
12
  <ProgressPrimitive.Root
13
  ref={ref}
14
  className={cn(
15
+ 'relative h-4 w-full overflow-hidden rounded-full bg-neutral-100 dark:bg-neutral-800',
16
  className
17
  )}
18
  {...props}
19
  >
20
  <ProgressPrimitive.Indicator
21
+ className="h-full w-full flex-1 bg-neutral-900 transition-all dark:bg-neutral-50"
22
  style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
23
  />
24
  </ProgressPrimitive.Root>
src/components/ui/scroll-area.tsx CHANGED
@@ -40,7 +40,7 @@ const ScrollBar = React.forwardRef<
40
  )}
41
  {...props}
42
  >
43
- <ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-stone-200 dark:bg-stone-800" />
44
  </ScrollAreaPrimitive.ScrollAreaScrollbar>
45
  ))
46
  ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
 
40
  )}
41
  {...props}
42
  >
43
+ <ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-neutral-200 dark:bg-neutral-800" />
44
  </ScrollAreaPrimitive.ScrollAreaScrollbar>
45
  ))
46
  ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
src/components/ui/select.tsx CHANGED
@@ -19,7 +19,7 @@ const SelectTrigger = React.forwardRef<
19
  <SelectPrimitive.Trigger
20
  ref={ref}
21
  className={cn(
22
- 'flex h-10 w-full items-center justify-between rounded-md border border-stone-200 bg-transparent px-3 py-2 text-sm ring-offset-white placeholder:text-stone-500 focus:outline-none focus:ring-2 focus:ring-stone-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-stone-800 dark:ring-offset-stone-950 dark:placeholder:text-stone-400 dark:focus:ring-stone-800',
23
  className
24
  )}
25
  {...props}
@@ -40,7 +40,7 @@ const SelectContent = React.forwardRef<
40
  <SelectPrimitive.Content
41
  ref={ref}
42
  className={cn(
43
- 'relative z-50 min-w-[8rem] overflow-hidden rounded-md border border-stone-200 bg-white text-stone-950 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 dark:border-stone-800 dark:bg-stone-950 dark:text-stone-50',
44
  position === 'popper' &&
45
  'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
46
  className
@@ -81,7 +81,7 @@ const SelectItem = React.forwardRef<
81
  <SelectPrimitive.Item
82
  ref={ref}
83
  className={cn(
84
- 'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-stone-100 focus:text-stone-900 dark:focus:bg-stone-800 dark:focus:text-stone-50',
85
  className
86
  )}
87
  {...props}
@@ -103,7 +103,10 @@ const SelectSeparator = React.forwardRef<
103
  >(({ className, ...props }, ref) => (
104
  <SelectPrimitive.Separator
105
  ref={ref}
106
- className={cn('-mx-1 my-1 h-px bg-stone-100 dark:bg-stone-800', className)}
 
 
 
107
  {...props}
108
  />
109
  ))
 
19
  <SelectPrimitive.Trigger
20
  ref={ref}
21
  className={cn(
22
+ 'flex h-10 w-full items-center justify-between rounded-md border border-neutral-200 bg-transparent px-3 py-2 text-sm ring-offset-white placeholder:text-neutral-500 focus:outline-none focus:ring-2 focus:ring-neutral-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-neutral-800 dark:ring-offset-neutral-950 dark:placeholder:text-neutral-400 dark:focus:ring-neutral-800',
23
  className
24
  )}
25
  {...props}
 
40
  <SelectPrimitive.Content
41
  ref={ref}
42
  className={cn(
43
+ 'relative z-50 min-w-[8rem] overflow-hidden rounded-md border border-neutral-200 bg-white text-neutral-950 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50',
44
  position === 'popper' &&
45
  'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
46
  className
 
81
  <SelectPrimitive.Item
82
  ref={ref}
83
  className={cn(
84
+ 'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-neutral-100 focus:text-neutral-900 dark:focus:bg-neutral-800 dark:focus:text-neutral-50',
85
  className
86
  )}
87
  {...props}
 
103
  >(({ className, ...props }, ref) => (
104
  <SelectPrimitive.Separator
105
  ref={ref}
106
+ className={cn(
107
+ '-mx-1 my-1 h-px bg-neutral-100 dark:bg-neutral-800',
108
+ className
109
+ )}
110
  {...props}
111
  />
112
  ))
src/components/ui/separator.tsx CHANGED
@@ -18,7 +18,7 @@ const Separator = React.forwardRef<
18
  decorative={decorative}
19
  orientation={orientation}
20
  className={cn(
21
- 'shrink-0 bg-stone-200 dark:bg-stone-800',
22
  orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
23
  className
24
  )}
 
18
  decorative={decorative}
19
  orientation={orientation}
20
  className={cn(
21
+ 'shrink-0 bg-neutral-200 dark:bg-neutral-800',
22
  orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
23
  className
24
  )}
src/components/ui/sheet.tsx ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import * as SheetPrimitive from '@radix-ui/react-dialog'
5
+ import { cva, type VariantProps } from 'class-variance-authority'
6
+ import { X } from 'lucide-react'
7
+
8
+ import { cn } from '@/lib/utils'
9
+
10
+ const Sheet = SheetPrimitive.Root
11
+
12
+ const SheetTrigger = SheetPrimitive.Trigger
13
+
14
+ const SheetClose = SheetPrimitive.Close
15
+
16
+ const SheetPortal = SheetPrimitive.Portal
17
+
18
+ const SheetOverlay = React.forwardRef<
19
+ React.ElementRef<typeof SheetPrimitive.Overlay>,
20
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
21
+ >(({ className, ...props }, ref) => (
22
+ <SheetPrimitive.Overlay
23
+ className={cn(
24
+ 'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
25
+ className
26
+ )}
27
+ {...props}
28
+ ref={ref}
29
+ />
30
+ ))
31
+ SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
32
+
33
+ const sheetVariants = cva(
34
+ 'fixed z-50 gap-4 bg-white p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500 dark:bg-neutral-950',
35
+ {
36
+ variants: {
37
+ side: {
38
+ top: 'inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top',
39
+ bottom:
40
+ 'inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',
41
+ left: 'inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm',
42
+ right:
43
+ 'inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm',
44
+ },
45
+ },
46
+ defaultVariants: {
47
+ side: 'right',
48
+ },
49
+ }
50
+ )
51
+
52
+ interface SheetContentProps
53
+ extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
54
+ VariantProps<typeof sheetVariants> {}
55
+
56
+ const SheetContent = React.forwardRef<
57
+ React.ElementRef<typeof SheetPrimitive.Content>,
58
+ SheetContentProps
59
+ >(({ side = 'right', className, children, ...props }, ref) => (
60
+ <SheetPortal>
61
+ <SheetOverlay />
62
+ <SheetPrimitive.Content
63
+ ref={ref}
64
+ className={cn(sheetVariants({ side }), className)}
65
+ {...props}
66
+ >
67
+ {children}
68
+ <SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity data-[state=open]:bg-neutral-100 hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-neutral-950 focus:ring-offset-2 disabled:pointer-events-none dark:ring-offset-neutral-950 dark:data-[state=open]:bg-neutral-800 dark:focus:ring-neutral-300">
69
+ <X className="h-4 w-4" />
70
+ <span className="sr-only">Close</span>
71
+ </SheetPrimitive.Close>
72
+ </SheetPrimitive.Content>
73
+ </SheetPortal>
74
+ ))
75
+ SheetContent.displayName = SheetPrimitive.Content.displayName
76
+
77
+ const SheetHeader = ({
78
+ className,
79
+ ...props
80
+ }: React.HTMLAttributes<HTMLDivElement>) => (
81
+ <div
82
+ className={cn(
83
+ 'flex flex-col space-y-2 text-center sm:text-left',
84
+ className
85
+ )}
86
+ {...props}
87
+ />
88
+ )
89
+ SheetHeader.displayName = 'SheetHeader'
90
+
91
+ const SheetFooter = ({
92
+ className,
93
+ ...props
94
+ }: React.HTMLAttributes<HTMLDivElement>) => (
95
+ <div
96
+ className={cn(
97
+ 'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
98
+ className
99
+ )}
100
+ {...props}
101
+ />
102
+ )
103
+ SheetFooter.displayName = 'SheetFooter'
104
+
105
+ const SheetTitle = React.forwardRef<
106
+ React.ElementRef<typeof SheetPrimitive.Title>,
107
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
108
+ >(({ className, ...props }, ref) => (
109
+ <SheetPrimitive.Title
110
+ ref={ref}
111
+ className={cn(
112
+ 'text-lg font-semibold text-neutral-950 dark:text-neutral-50',
113
+ className
114
+ )}
115
+ {...props}
116
+ />
117
+ ))
118
+ SheetTitle.displayName = SheetPrimitive.Title.displayName
119
+
120
+ const SheetDescription = React.forwardRef<
121
+ React.ElementRef<typeof SheetPrimitive.Description>,
122
+ React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
123
+ >(({ className, ...props }, ref) => (
124
+ <SheetPrimitive.Description
125
+ ref={ref}
126
+ className={cn('text-sm text-neutral-500 dark:text-neutral-400', className)}
127
+ {...props}
128
+ />
129
+ ))
130
+ SheetDescription.displayName = SheetPrimitive.Description.displayName
131
+
132
+ export {
133
+ Sheet,
134
+ SheetPortal,
135
+ SheetOverlay,
136
+ SheetTrigger,
137
+ SheetClose,
138
+ SheetContent,
139
+ SheetHeader,
140
+ SheetFooter,
141
+ SheetTitle,
142
+ SheetDescription,
143
+ }
src/components/ui/sonner.tsx CHANGED
@@ -15,13 +15,13 @@ const Toaster = ({ ...props }: ToasterProps) => {
15
  toastOptions={{
16
  classNames: {
17
  toast:
18
- 'group toast group-[.toaster]:bg-white group-[.toaster]:text-stone-950 group-[.toaster]:border-stone-200 group-[.toaster]:shadow-lg dark:group-[.toaster]:bg-stone-950 dark:group-[.toaster]:text-stone-50 dark:group-[.toaster]:border-stone-800',
19
  description:
20
- 'group-[.toast]:text-stone-500 dark:group-[.toast]:text-stone-400',
21
  actionButton:
22
- 'group-[.toast]:bg-stone-900 group-[.toast]:text-stone-50 dark:group-[.toast]:bg-stone-50 dark:group-[.toast]:text-stone-900',
23
  cancelButton:
24
- 'group-[.toast]:bg-stone-100 group-[.toast]:text-stone-500 dark:group-[.toast]:bg-stone-800 dark:group-[.toast]:text-stone-400',
25
  },
26
  }}
27
  {...props}
 
15
  toastOptions={{
16
  classNames: {
17
  toast:
18
+ 'group toast group-[.toaster]:bg-white group-[.toaster]:text-neutral-950 group-[.toaster]:border-neutral-200 group-[.toaster]:shadow-lg dark:group-[.toaster]:bg-neutral-950 dark:group-[.toaster]:text-neutral-50 dark:group-[.toaster]:border-neutral-800',
19
  description:
20
+ 'group-[.toast]:text-neutral-500 dark:group-[.toast]:text-neutral-400',
21
  actionButton:
22
+ 'group-[.toast]:bg-neutral-900 group-[.toast]:text-neutral-50 dark:group-[.toast]:bg-neutral-50 dark:group-[.toast]:text-neutral-900',
23
  cancelButton:
24
+ 'group-[.toast]:bg-neutral-100 group-[.toast]:text-neutral-500 dark:group-[.toast]:bg-neutral-800 dark:group-[.toast]:text-neutral-400',
25
  },
26
  }}
27
  {...props}
src/components/ui/table.tsx CHANGED
@@ -43,7 +43,7 @@ const TableFooter = React.forwardRef<
43
  <tfoot
44
  ref={ref}
45
  className={cn(
46
- 'bg-stone-900 font-medium text-stone-50 dark:bg-stone-50 dark:text-stone-900',
47
  className
48
  )}
49
  {...props}
@@ -58,7 +58,7 @@ const TableRow = React.forwardRef<
58
  <tr
59
  ref={ref}
60
  className={cn(
61
- 'border-b transition-colors data-[state=selected]:bg-stone-100 hover:bg-stone-100/50 dark:data-[state=selected]:bg-stone-800 dark:hover:bg-stone-800/50',
62
  className
63
  )}
64
  {...props}
@@ -73,7 +73,7 @@ const TableHead = React.forwardRef<
73
  <th
74
  ref={ref}
75
  className={cn(
76
- 'h-12 px-4 text-left align-middle font-medium text-stone-500 dark:text-stone-400 [&:has([role=checkbox])]:pr-0',
77
  className
78
  )}
79
  {...props}
@@ -99,7 +99,10 @@ const TableCaption = React.forwardRef<
99
  >(({ className, ...props }, ref) => (
100
  <caption
101
  ref={ref}
102
- className={cn('mt-4 text-sm text-stone-500 dark:text-stone-400', className)}
 
 
 
103
  {...props}
104
  />
105
  ))
 
43
  <tfoot
44
  ref={ref}
45
  className={cn(
46
+ 'bg-neutral-900 font-medium text-neutral-50 dark:bg-neutral-50 dark:text-neutral-900',
47
  className
48
  )}
49
  {...props}
 
58
  <tr
59
  ref={ref}
60
  className={cn(
61
+ 'border-b transition-colors data-[state=selected]:bg-neutral-100 hover:bg-neutral-100/50 dark:data-[state=selected]:bg-neutral-800 dark:hover:bg-neutral-800/50',
62
  className
63
  )}
64
  {...props}
 
73
  <th
74
  ref={ref}
75
  className={cn(
76
+ 'h-12 px-4 text-left align-middle font-medium text-neutral-500 dark:text-neutral-400 [&:has([role=checkbox])]:pr-0',
77
  className
78
  )}
79
  {...props}
 
99
  >(({ className, ...props }, ref) => (
100
  <caption
101
  ref={ref}
102
+ className={cn(
103
+ 'mt-4 text-sm text-neutral-500 dark:text-neutral-400',
104
+ className
105
+ )}
106
  {...props}
107
  />
108
  ))
src/components/ui/tabs.tsx CHANGED
@@ -14,7 +14,7 @@ const TabsList = React.forwardRef<
14
  <TabsPrimitive.List
15
  ref={ref}
16
  className={cn(
17
- 'inline-flex h-10 items-center justify-center rounded-md bg-stone-100 p-1 text-stone-500 dark:bg-stone-800 dark:text-stone-400',
18
  className
19
  )}
20
  {...props}
@@ -29,7 +29,7 @@ const TabsTrigger = React.forwardRef<
29
  <TabsPrimitive.Trigger
30
  ref={ref}
31
  className={cn(
32
- 'inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-white transition-all data-[state=active]:bg-white data-[state=active]:text-stone-950 data-[state=active]:shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-stone-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-stone-950 dark:data-[state=active]:bg-stone-950 dark:data-[state=active]:text-stone-50 dark:focus-visible:ring-stone-300',
33
  className
34
  )}
35
  {...props}
@@ -44,7 +44,7 @@ const TabsContent = React.forwardRef<
44
  <TabsPrimitive.Content
45
  ref={ref}
46
  className={cn(
47
- 'mt-2 ring-offset-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-stone-950 focus-visible:ring-offset-2 dark:ring-offset-stone-950 dark:focus-visible:ring-stone-300',
48
  className
49
  )}
50
  {...props}
 
14
  <TabsPrimitive.List
15
  ref={ref}
16
  className={cn(
17
+ 'inline-flex h-10 items-center justify-center rounded-md bg-neutral-100 p-1 text-neutral-500 dark:bg-neutral-800 dark:text-neutral-400',
18
  className
19
  )}
20
  {...props}
 
29
  <TabsPrimitive.Trigger
30
  ref={ref}
31
  className={cn(
32
+ 'inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-white transition-all data-[state=active]:bg-white data-[state=active]:text-neutral-950 data-[state=active]:shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-neutral-950 dark:data-[state=active]:bg-neutral-950 dark:data-[state=active]:text-neutral-50 dark:focus-visible:ring-neutral-300',
33
  className
34
  )}
35
  {...props}
 
44
  <TabsPrimitive.Content
45
  ref={ref}
46
  className={cn(
47
+ 'mt-2 ring-offset-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-950 focus-visible:ring-offset-2 dark:ring-offset-neutral-950 dark:focus-visible:ring-neutral-300',
48
  className
49
  )}
50
  {...props}
src/components/ui/textarea.tsx CHANGED
@@ -10,7 +10,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
10
  return (
11
  <textarea
12
  className={cn(
13
- 'flex min-h-[80px] w-full rounded-md border border-stone-200 bg-transparent px-3 py-2 text-sm ring-offset-white placeholder:text-stone-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-stone-400 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-stone-800 dark:ring-offset-stone-950 dark:placeholder:text-stone-400 dark:focus-visible:ring-stone-800',
14
  className
15
  )}
16
  ref={ref}
 
10
  return (
11
  <textarea
12
  className={cn(
13
+ 'flex min-h-[80px] w-full rounded-md border border-neutral-200 bg-transparent px-3 py-2 text-sm ring-offset-white placeholder:text-neutral-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-400 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-neutral-800 dark:ring-offset-neutral-950 dark:placeholder:text-neutral-400 dark:focus-visible:ring-neutral-800',
14
  className
15
  )}
16
  ref={ref}
src/components/ui/toast.tsx CHANGED
@@ -23,14 +23,14 @@ const ToastViewport = React.forwardRef<
23
  ToastViewport.displayName = ToastPrimitives.Viewport.displayName
24
 
25
  const toastVariants = cva(
26
- 'group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border border-stone-200 p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full dark:border-stone-800',
27
  {
28
  variants: {
29
  variant: {
30
  default:
31
- 'border bg-white text-stone-950 dark:bg-stone-950 dark:text-stone-50',
32
  destructive:
33
- 'destructive group border-red-500 bg-red-500 text-stone-50 dark:border-red-900 dark:bg-red-900 dark:text-stone-50',
34
  },
35
  },
36
  defaultVariants: {
@@ -61,7 +61,7 @@ const ToastAction = React.forwardRef<
61
  <ToastPrimitives.Action
62
  ref={ref}
63
  className={cn(
64
- 'inline-flex h-8 shrink-0 items-center justify-center rounded-md border border-stone-200 bg-transparent px-3 text-sm font-medium ring-offset-white transition-colors group-[.destructive]:border-stone-100/40 hover:bg-stone-100 group-[.destructive]:hover:border-red-500/30 group-[.destructive]:hover:bg-red-500 group-[.destructive]:hover:text-stone-50 focus:outline-none focus:ring-2 focus:ring-stone-950 focus:ring-offset-2 group-[.destructive]:focus:ring-red-500 disabled:pointer-events-none disabled:opacity-50 dark:border-stone-800 dark:ring-offset-stone-950 dark:group-[.destructive]:border-stone-800/40 dark:hover:bg-stone-800 dark:group-[.destructive]:hover:border-red-900/30 dark:group-[.destructive]:hover:bg-red-900 dark:group-[.destructive]:hover:text-stone-50 dark:focus:ring-stone-300 dark:group-[.destructive]:focus:ring-red-900',
65
  className
66
  )}
67
  {...props}
@@ -76,7 +76,7 @@ const ToastClose = React.forwardRef<
76
  <ToastPrimitives.Close
77
  ref={ref}
78
  className={cn(
79
- 'absolute right-2 top-2 rounded-md p-1 text-stone-950/50 opacity-0 transition-opacity group-hover:opacity-100 group-[.destructive]:text-red-300 hover:text-stone-950 group-[.destructive]:hover:text-red-50 focus:opacity-100 focus:outline-none focus:ring-2 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600 dark:text-stone-50/50 dark:hover:text-stone-50',
80
  className
81
  )}
82
  toast-close=""
 
23
  ToastViewport.displayName = ToastPrimitives.Viewport.displayName
24
 
25
  const toastVariants = cva(
26
+ 'group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border border-neutral-200 p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full dark:border-neutral-800',
27
  {
28
  variants: {
29
  variant: {
30
  default:
31
+ 'border bg-white text-neutral-950 dark:bg-neutral-950 dark:text-neutral-50',
32
  destructive:
33
+ 'destructive group border-red-500 bg-red-500 text-neutral-50 dark:border-red-900 dark:bg-red-900 dark:text-neutral-50',
34
  },
35
  },
36
  defaultVariants: {
 
61
  <ToastPrimitives.Action
62
  ref={ref}
63
  className={cn(
64
+ 'inline-flex h-8 shrink-0 items-center justify-center rounded-md border border-neutral-200 bg-transparent px-3 text-sm font-medium ring-offset-white transition-colors group-[.destructive]:border-neutral-100/40 hover:bg-neutral-100 group-[.destructive]:hover:border-red-500/30 group-[.destructive]:hover:bg-red-500 group-[.destructive]:hover:text-neutral-50 focus:outline-none focus:ring-2 focus:ring-neutral-950 focus:ring-offset-2 group-[.destructive]:focus:ring-red-500 disabled:pointer-events-none disabled:opacity-50 dark:border-neutral-800 dark:ring-offset-neutral-950 dark:group-[.destructive]:border-neutral-800/40 dark:hover:bg-neutral-800 dark:group-[.destructive]:hover:border-red-900/30 dark:group-[.destructive]:hover:bg-red-900 dark:group-[.destructive]:hover:text-neutral-50 dark:focus:ring-neutral-300 dark:group-[.destructive]:focus:ring-red-900',
65
  className
66
  )}
67
  {...props}
 
76
  <ToastPrimitives.Close
77
  ref={ref}
78
  className={cn(
79
+ 'absolute right-2 top-2 rounded-md p-1 text-neutral-950/50 opacity-0 transition-opacity group-hover:opacity-100 group-[.destructive]:text-red-300 hover:text-neutral-950 group-[.destructive]:hover:text-red-50 focus:opacity-100 focus:outline-none focus:ring-2 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600 dark:text-neutral-50/50 dark:hover:text-neutral-50',
80
  className
81
  )}
82
  toast-close=""
src/components/ui/tooltip.tsx CHANGED
@@ -19,7 +19,7 @@ const TooltipContent = React.forwardRef<
19
  ref={ref}
20
  sideOffset={sideOffset}
21
  className={cn(
22
- 'z-50 overflow-hidden rounded-md border border-stone-200 bg-white px-3 py-1.5 text-sm text-stone-950 shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 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 dark:border-stone-800 dark:bg-stone-950 dark:text-stone-50',
23
  className
24
  )}
25
  {...props}
 
19
  ref={ref}
20
  sideOffset={sideOffset}
21
  className={cn(
22
+ 'z-50 overflow-hidden rounded-md border border-neutral-200 bg-white px-3 py-1.5 text-sm text-neutral-950 shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 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 dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50',
23
  className
24
  )}
25
  {...props}
src/components/ui/vertical-slider.tsx CHANGED
@@ -17,10 +17,10 @@ const VerticalSlider = React.forwardRef<
17
  )}
18
  {...props}
19
  >
20
- <SliderPrimitive.Track className="relative h-full w-2 grow overflow-hidden rounded-full bg-stone-300 dark:bg-stone-700">
21
- <SliderPrimitive.Range className="absolute w-full bg-stone-700 dark:bg-stone-50" />
22
  </SliderPrimitive.Track>
23
- <SliderPrimitive.Thumb className="-ml-1.5 block h-5 w-5 rounded-full border-2 border-stone-700 bg-stone-300 ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-stone-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:border-stone-50 dark:bg-stone-700 dark:ring-offset-stone-950 dark:focus-visible:ring-stone-300" />
24
  </SliderPrimitive.Root>
25
  ))
26
  VerticalSlider.displayName = 'VerticalSlider'
 
17
  )}
18
  {...props}
19
  >
20
+ <SliderPrimitive.Track className="relative h-full w-2 grow overflow-hidden rounded-full bg-neutral-300 dark:bg-neutral-700">
21
+ <SliderPrimitive.Range className="absolute w-full bg-neutral-700 dark:bg-neutral-50" />
22
  </SliderPrimitive.Track>
23
+ <SliderPrimitive.Thumb className="-ml-1.5 block h-5 w-5 rounded-full border-2 border-neutral-700 bg-neutral-300 ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:border-neutral-50 dark:bg-neutral-700 dark:ring-offset-neutral-950 dark:focus-visible:ring-neutral-300" />
24
  </SliderPrimitive.Root>
25
  ))
26
  VerticalSlider.displayName = 'VerticalSlider'
src/components/wizards/project/index.tsx CHANGED
@@ -64,7 +64,7 @@ export function ProjectCreationWizard() {
64
  <Button
65
  key={key}
66
  variant="ghost"
67
- className="flex w-full flex-col items-end text-right text-lg font-thin capitalize text-stone-300"
68
  onClick={() =>
69
  setProjectCreationWizardStep(key as ProjectCreationWizardStep)
70
  }
@@ -75,14 +75,14 @@ export function ProjectCreationWizard() {
75
  </div>
76
  </ScrollArea>
77
 
78
- <div className="flex h-full max-w-[calc(100%-150px)] flex-grow select-none flex-col justify-between border-l border-stone-800 pl-8">
79
  <ScrollArea className="flex h-full flex-row">
80
  {stepPanels[projectCreationWizardStep]}
81
  </ScrollArea>
82
  <DialogFooter>
83
  <Button
84
  variant="outline"
85
- className="text-sm font-light dark:border-stone-700 dark:bg-stone-800 dark:text-stone-400"
86
  onClick={() => {
87
  setProjectCreationWizardStep(ProjectCreationWizardStep.NONE)
88
  }}
 
64
  <Button
65
  key={key}
66
  variant="ghost"
67
+ className="flex w-full flex-col items-end text-right text-lg font-thin capitalize text-neutral-300"
68
  onClick={() =>
69
  setProjectCreationWizardStep(key as ProjectCreationWizardStep)
70
  }
 
75
  </div>
76
  </ScrollArea>
77
 
78
+ <div className="flex h-full max-w-[calc(100%-150px)] flex-grow select-none flex-col justify-between border-l border-neutral-800 pl-8">
79
  <ScrollArea className="flex h-full flex-row">
80
  {stepPanels[projectCreationWizardStep]}
81
  </ScrollArea>
82
  <DialogFooter>
83
  <Button
84
  variant="outline"
85
+ className="text-sm font-light dark:border-neutral-700 dark:bg-neutral-800 dark:text-neutral-400"
86
  onClick={() => {
87
  setProjectCreationWizardStep(ProjectCreationWizardStep.NONE)
88
  }}
src/experiments/moodboard/MoodboardView.tsx CHANGED
@@ -58,7 +58,7 @@ export function MoodboardView() {
58
 
59
  const onConnect: OnConnect = useCallback(
60
  (params) => setEdges((eds) => addEdge(params, eds)),
61
- []
62
  )
63
 
64
  return (
 
58
 
59
  const onConnect: OnConnect = useCallback(
60
  (params) => setEdges((eds) => addEdge(params, eds)),
61
+ [setEdges]
62
  )
63
 
64
  return (
src/experiments/moodboard/Node.tsx CHANGED
@@ -11,7 +11,7 @@ function NodeComponent({
11
  }
12
  }) {
13
  return (
14
- <div className="rounded-md border-2 border-stone-400 bg-white px-4 py-2 shadow-md">
15
  <div className="flex">
16
  <div className="flex h-12 w-12 items-center justify-center rounded-full bg-gray-100">
17
  {data.emoji}
 
11
  }
12
  }) {
13
  return (
14
+ <div className="rounded-md border-2 border-neutral-400 bg-white px-4 py-2 shadow-md">
15
  <div className="flex">
16
  <div className="flex h-12 w-12 items-center justify-center rounded-full bg-gray-100">
17
  {data.emoji}
src/lib/core/constants.ts CHANGED
@@ -3,7 +3,7 @@
3
  export const HARD_LIMIT_NB_MAX_ASSETS_TO_GENERATE_IN_PARALLEL = 32
4
 
5
  export const APP_NAME = 'Clapper.app'
6
- export const APP_REVISION = '20240812+1800'
7
 
8
  export const APP_DOMAIN = 'Clapper.app'
9
  export const APP_LINK = 'https://clapper.app'
 
3
  export const HARD_LIMIT_NB_MAX_ASSETS_TO_GENERATE_IN_PARALLEL = 32
4
 
5
  export const APP_NAME = 'Clapper.app'
6
+ export const APP_REVISION = '20240814+1439'
7
 
8
  export const APP_DOMAIN = 'Clapper.app'
9
  export const APP_LINK = 'https://clapper.app'
src/services/debug.ts CHANGED
@@ -11,6 +11,7 @@ import {
11
  useProjectEditor,
12
  useScriptEditor,
13
  useSegmentEditor,
 
14
  } from './editors'
15
  import { useIO } from './io/useIO'
16
  import { useMetrics } from './metrics/useMetrics'
@@ -24,6 +25,7 @@ import { useWindows } from './windows/useWindows'
24
 
25
  // those are just used for developer convenience
26
  // to help debug things in the chrome developer console
 
27
  if (typeof window !== 'undefined') {
28
  const w = window as any
29
  w.useTasks = useTasks
@@ -31,13 +33,18 @@ if (typeof window !== 'undefined') {
31
  w.useMic = useMic
32
  w.useAudio = useAudio
33
  w.useBroadcast = useBroadcast
34
- w.useEditors = useEditors
 
 
 
 
 
 
35
  w.useEntityEditor = useEntityEditor
36
  w.useFilterEditor = useFilterEditor
37
  w.useProjectEditor = useProjectEditor
38
  w.useScriptEditor = useScriptEditor
39
  w.useSegmentEditor = useSegmentEditor
40
- w.useIO = useIO
41
  w.useMetrics = useMetrics
42
  w.useMonitor = useMonitor
43
  w.useRenderer = useRenderer
 
11
  useProjectEditor,
12
  useScriptEditor,
13
  useSegmentEditor,
14
+ useWorkflowEditor,
15
  } from './editors'
16
  import { useIO } from './io/useIO'
17
  import { useMetrics } from './metrics/useMetrics'
 
25
 
26
  // those are just used for developer convenience
27
  // to help debug things in the chrome developer console
28
+
29
  if (typeof window !== 'undefined') {
30
  const w = window as any
31
  w.useTasks = useTasks
 
33
  w.useMic = useMic
34
  w.useAudio = useAudio
35
  w.useBroadcast = useBroadcast
36
+
37
+ // I think we have a cyclic dependency somewhere,
38
+ // because uncommenting the following lines will crash the app
39
+ // w.useEditors = useEditors
40
+ // w.useWorkflowEditor = useWorkflowEditor
41
+ // w.useIO = useIO
42
+
43
  w.useEntityEditor = useEntityEditor
44
  w.useFilterEditor = useFilterEditor
45
  w.useProjectEditor = useProjectEditor
46
  w.useScriptEditor = useScriptEditor
47
  w.useSegmentEditor = useSegmentEditor
 
48
  w.useMetrics = useMetrics
49
  w.useMonitor = useMonitor
50
  w.useRenderer = useRenderer
src/services/editors/script-editor/useScriptEditor.ts CHANGED
@@ -18,7 +18,7 @@ import {
18
  } from '@aitube/clapper-services'
19
 
20
  import { getDefaultScriptEditorState } from './getDefaultScriptEditorState'
21
- import { useAssistant } from '@/services/assistant/useAssistant'
22
 
23
  export const useScriptEditor = create<ScriptEditorStore>((set, get) => ({
24
  ...getDefaultScriptEditorState(),
@@ -85,7 +85,8 @@ export const useScriptEditor = create<ScriptEditorStore>((set, get) => ({
85
  // console.log('existingText:', existingText)
86
  // console.log('newTextAddition:', newTextAddition)
87
 
88
- const assistant = useAssistant.getState()
 
89
 
90
  // call the LLM, with a prompt like:
91
  },
 
18
  } from '@aitube/clapper-services'
19
 
20
  import { getDefaultScriptEditorState } from './getDefaultScriptEditorState'
21
+ // import { useAssistant } from '@/services/assistant/useAssistant'
22
 
23
  export const useScriptEditor = create<ScriptEditorStore>((set, get) => ({
24
  ...getDefaultScriptEditorState(),
 
85
  // console.log('existingText:', existingText)
86
  // console.log('newTextAddition:', newTextAddition)
87
 
88
+ // attention here! the assistant might not be initialized
89
+ // const assistant = useAssistant.getState()
90
 
91
  // call the LLM, with a prompt like:
92
  },