jbilcke-hf HF staff commited on
Commit
88a7433
2 Parent(s): c35e031 c608ace

upgrade dependencies and fix README.md

Browse files
.gitattributes CHANGED
@@ -2,6 +2,6 @@
2
  *.xcf filter=lfs diff=lfs merge=lfs -text
3
  *.jpeg filter=lfs diff=lfs merge=lfs -text
4
  *.jpg filter=lfs diff=lfs merge=lfs -text
5
- public/datasets/baby-names-us-year-of-birth-full.csv filter=lfs diff=lfs merge=lfs -text
6
  *.png filter=lfs diff=lfs merge=lfs -text
7
  *.excalidraw filter=lfs diff=lfs merge=lfs -text
 
2
  *.xcf filter=lfs diff=lfs merge=lfs -text
3
  *.jpeg filter=lfs diff=lfs merge=lfs -text
4
  *.jpg filter=lfs diff=lfs merge=lfs -text
5
+ *.csv filter=lfs diff=lfs merge=lfs -text
6
  *.png filter=lfs diff=lfs merge=lfs -text
7
  *.excalidraw filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -60,11 +60,40 @@ Clapper is under a GPL v3 licence, see the [LICENCE](LICENSE.txt) file for more
60
 
61
  ### Installation
62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  ```bash
64
  npm i
65
  npm run dev
66
  ```
67
 
 
 
 
 
 
 
 
 
 
68
  ### Code conventions
69
 
70
  I haven't setup Prettier or a Linter yet.
 
60
 
61
  ### Installation
62
 
63
+ ### Prerequisites
64
+
65
+ As a prerequisite you need to have [git lfs](https://git-lfs.com/) installed (see the `.gitattributes` file at the root of project):
66
+
67
+ ```bash
68
+ git lfs install
69
+ ```
70
+
71
+ Clapper has been tested with Node `20.9.*`.
72
+
73
+ To make sure you use this version, you can use [NVM](https://github.com/nvm-sh/nvm) to activate it:
74
+
75
+ ```bash
76
+ nvm use
77
+ ```
78
+
79
+ If you find that Clapper is working with a more recent (stable) version of Node, or have a better version management to suggest, please open a ticket.
80
+
81
+ ### Installing and running the app
82
+
83
  ```bash
84
  npm i
85
  npm run dev
86
  ```
87
 
88
+ ### Making sure everything is working properly
89
+
90
+ There are no tests yet (I will create a ticket for that),
91
+ but until then you can run the following command to make sure all the types are consistant and properly set:
92
+
93
+ ```bash
94
+ npm run build
95
+ ```
96
+
97
  ### Code conventions
98
 
99
  I haven't setup Prettier or a Linter yet.
package-lock.json CHANGED
The diff for this file is too large to render. See raw diff
 
package.json CHANGED
@@ -1,6 +1,6 @@
1
  {
2
  "name": "@aitube/clapper",
3
- "version": "0.0.2",
4
  "private": true,
5
  "description": "🎬 Clapper",
6
  "scripts": {
@@ -12,9 +12,9 @@
12
  "dependencies": {
13
  "@aitube/broadway": "0.0.22",
14
  "@aitube/clap": "0.0.30",
15
- "@aitube/clapper-services": "0.0.16",
16
  "@aitube/engine": "0.0.26",
17
- "@aitube/timeline": "0.0.40",
18
  "@fal-ai/serverless-client": "^0.11.0",
19
  "@gradio/client": "^1.1.1",
20
  "@huggingface/hub": "^0.15.1",
 
1
  {
2
  "name": "@aitube/clapper",
3
+ "version": "0.0.3",
4
  "private": true,
5
  "description": "🎬 Clapper",
6
  "scripts": {
 
12
  "dependencies": {
13
  "@aitube/broadway": "0.0.22",
14
  "@aitube/clap": "0.0.30",
15
+ "@aitube/clapper-services": "0.0.22",
16
  "@aitube/engine": "0.0.26",
17
+ "@aitube/timeline": "0.0.42",
18
  "@fal-ai/serverless-client": "^0.11.0",
19
  "@gradio/client": "^1.1.1",
20
  "@huggingface/hub": "^0.15.1",
public/samples/claps/empty_project.clap ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:42bb7d554f5209f9c0e801c3d71b3b11730f80a2c7a39cf4ac7baf94743d7be1
3
+ size 375
public/samples/claps/empty_project.yaml ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ - format: clap-0
2
+ numberOfEntities: 0
3
+ numberOfScenes: 0
4
+ numberOfSegments: 27
5
+ - id: 5ffcfd0b-cb91-465e-87b6-bde41037373e
6
+ title: ""
7
+ description: An exciting new project
8
+ synopsis: ""
9
+ licence: ""
10
+ orientation: landscape
11
+ durationInMs: 18000
12
+ width: 1024
13
+ height: 575
14
+ defaultVideoModel: AnimateDiff-Lightning
15
+ extraPositivePrompt: []
16
+ screenplay: ""
17
+ isLoop: false
18
+ isInteractive: false
19
+ - id: e16478cf-3535-48e9-98e8-f702ec9ca348
20
+ track: 0
21
+ startTimeInMs: 0
22
+ endTimeInMs: 0
23
+ category: video
24
+ outputType: video
src/components/editors/EntityEditor/index.tsx CHANGED
@@ -1,88 +1,208 @@
1
- import { TimelineSegment, useTimeline } from "@aitube/timeline"
 
 
2
 
3
- import { FormFile } from "@/components/forms/FormFile"
4
- import { FormInput } from "@/components/forms/FormInput"
5
- import { FormSection } from "@/components/forms/FormSection"
6
- import { useEntityEditor, useRenderer } from "@/services"
7
- import { useEffect } from "react"
8
- import { ClapEntity, ClapProject } from "@aitube/clap"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
  export function EntityEditor() {
11
- const clap: ClapProject = useTimeline(s => s.clap)
12
-
13
- const segmentsChanged: number = useTimeline(s => s.segmentsChanged)
14
- const selectedSegments: TimelineSegment[] = useTimeline(s => s.selectedSegments)
15
- const { activeSegments } = useRenderer(s => s.bufferedSegments)
16
-
17
- const current = useEntityEditor(s => s.current)
18
- const setCurrent = useEntityEditor(s => s.setCurrent)
19
- const history = useEntityEditor(s => s.history)
20
- const undo = useEntityEditor(s => s.undo)
21
- const redo = useEntityEditor(s => s.redo)
22
-
23
- let segment = selectedSegments.at(-1)
24
- let entity: ClapEntity | undefined = clap.entityIndex[segment?.entityId as any]
25
-
26
- if (!entity) {
27
- segment = activeSegments.find(s => clap.entityIndex[s?.entityId as any])
28
- entity = clap.entityIndex[segment?.entityId as any]
29
- }
30
 
31
  useEffect(() => {
32
- setCurrent(entity)
33
- }, [clap, entity, segmentsChanged])
34
 
35
- if (!current) {
36
- return <div>
37
- No Entity selected
38
- </div>
39
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
- // TODO: adapt the editor based on the kind of
42
- // entity (character, location..)
43
- //
44
- // I think we can use UI elements of our legacy character editor
45
- // that I did in a Hugging Face space
46
  return (
47
- <FormSection
48
- label={"Entity editor"}
49
- className="p-4">
50
- <label>Visual identity</label>
51
- {current?.imageId
52
- ? <img src={current?.imageId}></img>
53
- : null}
54
- <FormFile
55
- label={"Visual identity file (TODO)"}
56
- />
57
- {/*
58
- <FormInput<string>
59
- label={"Audio identity"}
60
- value={current?.audioId.slice(0, 20)}
61
- />
62
- */}
63
- <FormFile
64
- label={"Audio identity file (TODO)"}
65
- />
66
- <FormInput<string>
67
- label={"Label"}
68
- value={current.label}
69
- />
70
- <FormInput<string>
71
- label={"Description"}
72
- value={current.description}
73
- />
74
- <FormInput<number>
75
- label={"Age"}
76
- value={current.age}
77
- />
78
- <FormInput<string>
79
- label={"Gender"}
80
- value={current.gender}
81
- />
82
- <FormInput<string>
83
- label={"Appearance"}
84
- value={current.appearance}
85
- />
86
- </FormSection>
87
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  }
 
1
+ import { useEffect } from "react";
2
+ import { ClapEntity, ClapSegmentCategory, newEntity } from "@aitube/clap";
3
+ import { useTimeline } from "@aitube/timeline";
4
 
5
+ import { FormFile } from "@/components/forms/FormFile";
6
+ import { FormInput } from "@/components/forms/FormInput";
7
+ import { FormSection } from "@/components/forms/FormSection";
8
+ import { FormSelect } from "@/components/forms/FormSelect";
9
+ import { Button } from "@/components/ui/button";
10
+ import { useEntityEditor, useIO } from "@/services";
11
+
12
+ function EntityList({ onSelectEntity }: {
13
+ onSelectEntity: (entityId: string) => void;
14
+ }) {
15
+ const entities = useTimeline(s => s.entities);
16
+ const setCurrent = useEntityEditor(s => s.setCurrent);
17
+ const addEntity = useEntityEditor(s => s.addEntity);
18
+ const removeEntity = useEntityEditor(s => s.removeEntity);
19
+
20
+ const handleAddEntity = () => {
21
+ const entity: ClapEntity = newEntity({
22
+ id: Date.now().toString(),
23
+ label: "NEW_ENTITY",
24
+ category: ClapSegmentCategory.CHARACTER,
25
+ description: "",
26
+ appearance: "",
27
+ }); // ignoring some fields for now
28
+ addEntity(entity);
29
+ };
30
+
31
+ return (
32
+ <div className="pt-4">
33
+ <div className="mb-2">
34
+ <h1 className="px-4 mb-4 text-xl font-bold inline">Entities</h1>
35
+ <Button onClick={handleAddEntity} className="absolute top-2 right-2" variant="secondary">
36
+ New +
37
+ </Button>
38
+ </div>
39
+ <ul>
40
+ {entities.map((entity: ClapEntity) => (
41
+ <li key={entity.id} className={`flex py-1 px-2`}>
42
+ <Button
43
+ onClick={() => {
44
+ setCurrent(entity);
45
+ onSelectEntity(entity.id);
46
+ }}
47
+ variant="ghost"
48
+ >
49
+ {entity.label} ({entity.category})
50
+ </Button>
51
+ <Button onClick={() => removeEntity(entity.id)} className={`ml-2 ml-auto`} variant="destructive" size="sm">
52
+ Remove
53
+ </Button>
54
+ </li>
55
+ ))}
56
+ </ul>
57
+ </div>
58
+ );
59
+ }
60
 
61
  export function EntityEditor() {
62
+ const entities = useTimeline(s => s.entities);
63
+ const updateEntities = useTimeline(s => s.updateEntities);
64
+
65
+ const saveEntitiesToClap = useIO(s => s.saveEntitiesToClap);
66
+ const openEntitiesFromClap = useIO(s => s.openEntitiesFromClap);
67
+
68
+ const current = useEntityEditor(s => s.current);
69
+ const setCurrent = useEntityEditor(s => s.setCurrent);
70
+
71
+ const draft = useEntityEditor(s => s.draft);
72
+ const setDraft = useEntityEditor(s => s.setDraft);
73
+
74
+ const showEntityList = useEntityEditor(s => s.showEntityList);
75
+ const setShowEntityList = useEntityEditor(s => s.setShowEntityList);
 
 
 
 
 
76
 
77
  useEffect(() => {
78
+ setCurrent(entities.at(0));
79
+ }, [entities, setCurrent]);
80
 
81
+ useEffect(() => {
82
+ setDraft(current);
83
+ }, [current, setDraft]);
84
+
85
+ const handleInputChange = (field: keyof ClapEntity, value: string | number | undefined) => {
86
+ if (!draft) { return }
87
+ let updatedValue = value;
88
+ if (field === "age") {
89
+ updatedValue = value === "" ? undefined : parseInt(value as string);
90
+ }
91
+ if (field === "label") {
92
+ updatedValue = value?.toString().toUpperCase();
93
+ }
94
+
95
+ setDraft({ ...draft, [field]: updatedValue })
96
+ };
97
+
98
+ const handleSave = () => {
99
+ if (!draft) { return }
100
+ updateEntities([ draft ]);
101
+ };
102
+
103
+ const handleFileUpload = async (field: "imageId" | "audioId", file: File) => {
104
+ if (!draft) { return }
105
+ const dataUrl = await new Promise<string>((resolve) => {
106
+ const reader = new FileReader();
107
+ reader.onload = (e) => resolve(e.target?.result as string);
108
+ reader.readAsDataURL(file);
109
+ });
110
+ setDraft({ ...draft, [field]: dataUrl });
111
+ };
112
+
113
+ const handleExport = async () => {
114
+ if (!draft) { return }
115
+ await saveEntitiesToClap([ draft ]);
116
+ };
117
+
118
+ const handleImport = async (file: File) => {
119
+ await openEntitiesFromClap(file);
120
+ };
121
+
122
+ const handleBack = () => {
123
+ setShowEntityList(true);
124
+ };
125
+
126
+ const handleSelectEntity = (entityId: string) => {
127
+ setShowEntityList(false);
128
+ };
129
 
 
 
 
 
 
130
  return (
131
+ <div className="flex flex-col w-full h-full overflow-x-auto">
132
+ {showEntityList ? (
133
+ <div className="mb-4">
134
+ <EntityList onSelectEntity={handleSelectEntity} />
135
+ </div>
136
+ ) : (
137
+ <div className="flex">
138
+ {draft && (
139
+ <FormSection className="px-2">
140
+ <Button onClick={handleBack}>Back</Button>
141
+ <FormInput
142
+ label="Identifier (UPPERCASE)"
143
+ value={draft.label || ""}
144
+ onChange={(value) => handleInputChange("label", value)}
145
+ />
146
+ <FormSelect<ClapSegmentCategory>
147
+ label="Category"
148
+ selectedItemId={draft.category}
149
+ items={Object.values(ClapSegmentCategory).map((category: ClapSegmentCategory) => ({
150
+ id: category,
151
+ label: category,
152
+ value: category,
153
+ }))}
154
+ onSelect={(value) => handleInputChange("category", value)}
155
+ />
156
+ {/* ... form fields ... */}
157
+ <FormFile
158
+ label="Visual Identity"
159
+ onChange={(files) => files[0] && handleFileUpload("imageId", files[0])}
160
+ />
161
+ {draft.imageId && (
162
+ <div className="mt-2">
163
+ <img src={draft.imageId} alt="Entity Preview" className="max-w-full h-auto" />
164
+ </div>
165
+ )}
166
+ <FormFile
167
+ label="Audio Identity"
168
+ onChange={(files) => files[0] && handleFileUpload("audioId", files[0])}
169
+ />
170
+ {draft.audioId && (
171
+ <div className="mt-2">
172
+ <audio controls src={draft.audioId} />
173
+ </div>
174
+ )}
175
+ <FormInput
176
+ label="Description"
177
+ value={draft.description || ""}
178
+ onChange={(value) => handleInputChange("description", value)}
179
+ />
180
+ <FormInput
181
+ label="Appearance"
182
+ value={draft.appearance || ""}
183
+ onChange={(value) => handleInputChange("appearance", value)}
184
+ />
185
+ <FormInput
186
+ label="Age"
187
+ value={draft.age?.toString() || ""}
188
+ onChange={(value) => handleInputChange("age", value)}
189
+ />
190
+ <FormInput
191
+ label="Gender"
192
+ value={draft.gender || ""}
193
+ onChange={(value) => handleInputChange("gender", value)}
194
+ />
195
+ <div className="mt-4 flex space-x-2">
196
+ <Button onClick={handleSave}>Save</Button>
197
+ <Button onClick={handleExport}>Export</Button>
198
+ </div>
199
+ <div className="mt-4 flex space-x-2">
200
+ <FormFile label="Import" onChange={(files) => files[0] && handleImport(files[0])} />
201
+ </div>
202
+ </FormSection>
203
+ )}
204
+ </div>
205
+ )}
206
+ </div>
207
+ );
208
  }
src/components/tasks/useTasks.tsx CHANGED
@@ -342,6 +342,3 @@ export const useTasks = create<TasksStore>((set, get) => ({
342
  }
343
  }));
344
 
345
- if (typeof window !== "undefined") {
346
- (window as any).useTasks = useTasks;
347
- }
 
342
  }
343
  }));
344
 
 
 
 
src/components/toolbars/top-menu/file/index.tsx CHANGED
@@ -3,10 +3,10 @@ import { useEffect } from "react"
3
  import { useTimeline } from "@aitube/timeline"
4
  import { useHotkeys } from "react-hotkeys-hook"
5
 
6
- import { MenubarContent, MenubarItem, MenubarMenu, MenubarSeparator, MenubarShortcut, MenubarTrigger } from "@/components/ui/menubar"
7
  import { useOpenFilePicker, useQueryStringParams } from "@/lib/hooks"
8
  import { IframeWarning } from "@/components/dialogs/iframe-warning"
9
- import { useIO } from "@/services/io/useIO"
10
 
11
  export function TopMenuFile() {
12
  const { clapUrl } = useQueryStringParams({
@@ -31,6 +31,8 @@ export function TopMenuFile() {
31
  const saveZipFile = useIO(s => s.saveZipFile)
32
  const saveKdenline = useIO(s => s.saveKdenline)
33
 
 
 
34
  useEffect(() => {
35
  (async () => {
36
  if (!clapUrl) {
@@ -55,6 +57,13 @@ export function TopMenuFile() {
55
  <MenubarMenu>
56
  <MenubarTrigger>File</MenubarTrigger>
57
  <MenubarContent>
 
 
 
 
 
 
 
58
  <MenubarItem onClick={() => {
59
  openFilePicker()
60
  }}>
@@ -67,6 +76,17 @@ export function TopMenuFile() {
67
  Save project (.clap)<MenubarShortcut>⌘S</MenubarShortcut>
68
  </MenubarItem>
69
  <MenubarSeparator />
 
 
 
 
 
 
 
 
 
 
 
70
  {/*
71
  <MenubarItem
72
  onClick={() => {
 
3
  import { useTimeline } from "@aitube/timeline"
4
  import { useHotkeys } from "react-hotkeys-hook"
5
 
6
+ import { MenubarContent, MenubarItem, MenubarMenu, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger } from "@/components/ui/menubar"
7
  import { useOpenFilePicker, useQueryStringParams } from "@/lib/hooks"
8
  import { IframeWarning } from "@/components/dialogs/iframe-warning"
9
+ import { useIO, useUI } from "@/services"
10
 
11
  export function TopMenuFile() {
12
  const { clapUrl } = useQueryStringParams({
 
31
  const saveZipFile = useIO(s => s.saveZipFile)
32
  const saveKdenline = useIO(s => s.saveKdenline)
33
 
34
+ const hasBetaAccess = useUI(s => s.hasBetaAccess)
35
+
36
  useEffect(() => {
37
  (async () => {
38
  if (!clapUrl) {
 
57
  <MenubarMenu>
58
  <MenubarTrigger>File</MenubarTrigger>
59
  <MenubarContent>
60
+ {hasBetaAccess &&
61
+ <MenubarItem onClick={() => {
62
+ openClapUrl('/samples/claps/empty_project.clap')
63
+ }}>
64
+ New Project<MenubarShortcut>⌘N</MenubarShortcut>
65
+ </MenubarItem>
66
+ }
67
  <MenubarItem onClick={() => {
68
  openFilePicker()
69
  }}>
 
76
  Save project (.clap)<MenubarShortcut>⌘S</MenubarShortcut>
77
  </MenubarItem>
78
  <MenubarSeparator />
79
+ <MenubarSub>
80
+ <MenubarSubTrigger>Examples</MenubarSubTrigger>
81
+ <MenubarSubContent>
82
+ <MenubarItem onClick={() => {
83
+ openClapUrl('/samples/claps/wasteland.clap')
84
+ }}>
85
+ Wasteland
86
+ </MenubarItem>
87
+ </MenubarSubContent>
88
+ </MenubarSub>
89
+ <MenubarSeparator />
90
  {/*
91
  <MenubarItem
92
  onClick={() => {
src/services/assistant/useAssistant.ts CHANGED
@@ -279,7 +279,3 @@ export function useInitAssistant() {
279
  setVoiceTranscript(transcript)
280
  }, [transcript])
281
  }
282
-
283
- if (typeof window !== "undefined") {
284
- (window as any).useAssistant = useAssistant
285
- }
 
279
  setVoiceTranscript(transcript)
280
  }, [transcript])
281
  }
 
 
 
 
src/services/audio/useAudio.ts CHANGED
@@ -101,8 +101,3 @@ export const useAudio = create<AudioStore>((set, get) => ({
101
  })
102
  },
103
  }))
104
-
105
-
106
- if (typeof window !== "undefined") {
107
- (window as any).useAudio = useAudio
108
- }
 
101
  })
102
  },
103
  }))
 
 
 
 
 
src/services/broadcast/useBroadcast.ts CHANGED
@@ -24,7 +24,3 @@ export const useBroadcast = create<BroadcastStore>((set, get) => ({
24
  return isBroadcasting
25
  },
26
  }))
27
-
28
- if (typeof window !== "undefined") {
29
- (window as any).useBroadcast = useBroadcast
30
- }
 
24
  return isBroadcasting
25
  },
26
  }))
 
 
 
 
src/services/debug.ts ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useTasks } from "@/components/tasks/useTasks"
2
+ import { useAssistant } from "./assistant/useAssistant"
3
+ import { useAudio } from "./audio/useAudio"
4
+ import { useBroadcast } from "./broadcast/useBroadcast"
5
+ import { useEditors, useEntityEditor, useProjectEditor, useScriptEditor, useSegmentEditor } from "./editors"
6
+ import { useIO } from "./io/useIO"
7
+ import { useMetrics } from "./metrics/useMetrics"
8
+ import { useMonitor } from "./monitor/useMonitor"
9
+ import { useRenderer } from "./renderer/useRenderer"
10
+ import { useResolver } from "./resolver/useResolver"
11
+ import { useSettings } from "./settings/useSettings"
12
+ import { useSimulator } from "./simulator/useSimulator"
13
+ import { useUI } from "./ui/useUI"
14
+
15
+ // those are just used for developer convenience
16
+ // to help debug things in the chrome developer console
17
+ if (typeof window !== "undefined") {
18
+ const w = window as any
19
+ w.useTasks = useTasks
20
+ w.useAssistant = useAssistant
21
+ w.useAudio = useAudio
22
+ w.useBroadcast = useBroadcast
23
+ w.useEditors = useEditors
24
+ w.useEntityEditor = useEntityEditor
25
+ w.useProjectEditor = useProjectEditor
26
+ w.useScriptEditor = useScriptEditor
27
+ w.useSegmentEditor = useSegmentEditor
28
+ w.useIO = useIO
29
+ w.useMetrics = useMetrics
30
+ w.useMonitor = useMonitor
31
+ w.useRenderer = useRenderer
32
+ w.useResolver = useResolver
33
+ w.useSettings = useSettings
34
+ w.useSimulator = useSimulator
35
+ w.useUI = useUI
36
+ }
src/services/editors/entity-editor/getDefaultEntityEditorState.ts CHANGED
@@ -5,6 +5,9 @@ export function getDefaultEntityEditorState(): EntityEditorState {
5
  current: undefined,
6
  version: 0,
7
  history: [],
 
 
 
8
  }
9
 
10
  return state
 
5
  current: undefined,
6
  version: 0,
7
  history: [],
8
+
9
+ draft: undefined,
10
+ showEntityList: false,
11
  }
12
 
13
  return state
src/services/editors/entity-editor/useEntityEditor.ts CHANGED
@@ -1,19 +1,31 @@
1
- "use client"
2
-
3
  import { create } from "zustand"
4
  import { ClapEntity } from "@aitube/clap"
5
  import { EntityEditorStore } from "@aitube/clapper-services"
 
6
 
7
  import { getDefaultEntityEditorState } from "./getDefaultEntityEditorState"
8
 
9
  export const useEntityEditor = create<EntityEditorStore>((set, get) => ({
10
  ...getDefaultEntityEditorState(),
11
- setCurrent: (current?: ClapEntity) => { set({ current }) },
12
  undo: () => {},
13
  redo: () => {},
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  }))
15
-
16
-
17
- if (typeof window !== "undefined") {
18
- (window as any).useEntityEditor = useEntityEditor
19
- }
 
 
 
1
  import { create } from "zustand"
2
  import { ClapEntity } from "@aitube/clap"
3
  import { EntityEditorStore } from "@aitube/clapper-services"
4
+ import { TimelineStore, useTimeline } from "@aitube/timeline"
5
 
6
  import { getDefaultEntityEditorState } from "./getDefaultEntityEditorState"
7
 
8
  export const useEntityEditor = create<EntityEditorStore>((set, get) => ({
9
  ...getDefaultEntityEditorState(),
10
+ setCurrent: (current?: ClapEntity) => set({ current }),
11
  undo: () => {},
12
  redo: () => {},
13
+ setDraft: (draft?: ClapEntity) => set({ draft }),
14
+ selectEntity: (id: string) => {
15
+ const { entityIndex }: TimelineStore = useTimeline.getState()
16
+ set({ current: entityIndex[id] })
17
+ },
18
+ addEntity: (entity: ClapEntity) => {
19
+ const { addEntities }: TimelineStore = useTimeline.getState()
20
+ addEntities([entity])
21
+ get().setCurrent(entity)
22
+ },
23
+ removeEntity: (id: string) => {
24
+ const { deleteEntities }: TimelineStore = useTimeline.getState()
25
+ deleteEntities([id])
26
+ get().setCurrent(undefined)
27
+ },
28
+ setShowEntityList: (showEntityList: boolean) => {
29
+ set({ showEntityList })
30
+ }
31
  }))
 
 
 
 
 
src/services/editors/project-editor/useProjectEditor.ts CHANGED
@@ -19,8 +19,3 @@ export const useProjectEditor = create<ProjectEditorStore>((set, get) => ({
19
  undo: () => {},
20
  redo: () => {},
21
  }))
22
-
23
-
24
- if (typeof window !== "undefined") {
25
- (window as any).useProjectEditor = useProjectEditor
26
- }
 
19
  undo: () => {},
20
  redo: () => {},
21
  }))
 
 
 
 
 
src/services/editors/script-editor/useScriptEditor.ts CHANGED
@@ -207,8 +207,3 @@ export const useScriptEditor = create<ScriptEditorStore>((set, get) => ({
207
  undo: () => {},
208
  redo: () => {},
209
  }))
210
-
211
-
212
- if (typeof window !== "undefined") {
213
- (window as any).useScriptEditor = useScriptEditor
214
- }
 
207
  undo: () => {},
208
  redo: () => {},
209
  }))
 
 
 
 
 
src/services/editors/segment-editor/useSegmentEditor.ts CHANGED
@@ -12,8 +12,3 @@ export const useSegmentEditor = create<SegmentEditorStore>((set, get) => ({
12
  undo: () => {},
13
  redo: () => {},
14
  }))
15
-
16
-
17
- if (typeof window !== "undefined") {
18
- (window as any).useSegmentEditor = useSegmentEditor
19
- }
 
12
  undo: () => {},
13
  redo: () => {},
14
  }))
 
 
 
 
 
src/services/editors/useEditors.ts CHANGED
@@ -10,7 +10,3 @@ export const useEditors = create<EditorsStore>((set, get) => ({
10
  setView: (view: EditorView) => { set({ view }) },
11
  }))
12
 
13
-
14
- if (typeof window !== "undefined") {
15
- (window as any).useEditors = useEditors
16
- }
 
10
  setView: (view: EditorView) => { set({ view }) },
11
  }))
12
 
 
 
 
 
src/services/index.ts CHANGED
@@ -18,4 +18,6 @@ export { useRenderer } from "./renderer/useRenderer"
18
  export { useResolver } from "./resolver/useResolver"
19
  export { useSettings } from "./settings/useSettings"
20
  export { useUI } from "./ui/useUI"
21
- export { useTasks }
 
 
 
18
  export { useResolver } from "./resolver/useResolver"
19
  export { useSettings } from "./settings/useSettings"
20
  export { useUI } from "./ui/useUI"
21
+ export { useTasks }
22
+
23
+ import "./debug"
src/services/io/useIO.ts CHANGED
@@ -766,7 +766,3 @@ export const useIO = create<IOStore>((set, get) => ({
766
  return entities
767
  },
768
  }))
769
-
770
- if (typeof window !== "undefined") {
771
- (window as any).useIO = useIO
772
- }
 
766
  return entities
767
  },
768
  }))
 
 
 
 
src/services/metrics/useMetrics.ts CHANGED
@@ -10,8 +10,3 @@ export const useMetrics = create<MetricsStore>((set, get) => ({
10
 
11
  // TODO: add a track metric callback
12
  }))
13
-
14
-
15
- if (typeof window !== "undefined") {
16
- (window as any).useMetrics = useMetrics
17
- }
 
10
 
11
  // TODO: add a track metric callback
12
  }))
 
 
 
 
 
src/services/monitor/useMonitor.ts CHANGED
@@ -147,8 +147,3 @@ setTimeout(() => {
147
  useMonitor.getState().bindShortcuts()
148
  }
149
  }, 0)
150
-
151
-
152
- if (typeof window !== "undefined") {
153
- (window as any).useMonitor = useMonitor
154
- }
 
147
  useMonitor.getState().bindShortcuts()
148
  }
149
  }, 0)
 
 
 
 
 
src/services/plugins/usePlugins.ts CHANGED
@@ -2,22 +2,22 @@
2
 
3
  import { create } from "zustand"
4
  import { ClapperPlugin, ClapperPluginApi, ClapperPluginMeta, PluginsStore, PublicServices } from "@aitube/clapper-services"
 
5
 
6
  import { getDefaultPluginsState } from "./getDefaultPluginsState"
7
  import { useScriptEditor } from "../editors/script-editor/useScriptEditor"
8
  import { useMonitor } from "../monitor/useMonitor"
9
- import { useTimeline } from "@aitube/timeline"
10
  import { useRenderer } from "../renderer"
11
  import { useBroadcast } from "../broadcast/useBroadcast"
12
  import { useResolver } from "../resolver/useResolver"
13
  import { useAssistant } from "../assistant/useAssistant"
14
  import { useAudio } from "../audio/useAudio"
15
  import { useUI } from "../ui"
16
-
17
  import { fetchAndRun } from "./fetchAndRun"
18
- import { useTasks } from "@/components/tasks/useTasks"
19
  import { useEditors, useEntityEditor, useProjectEditor, useSegmentEditor } from "../editors"
20
  import { useSimulator } from "../simulator/useSimulator"
 
21
 
22
  export const usePlugins = create<PluginsStore>((set, get) => ({
23
  ...getDefaultPluginsState(),
@@ -62,6 +62,7 @@ export const usePlugins = create<PluginsStore>((set, get) => ({
62
  broadcast: useBroadcast,
63
  simulator: useSimulator,
64
  ui: useUI,
 
65
  }
66
  },
67
  pluginApiGetSettings: async (id: string) => {
 
2
 
3
  import { create } from "zustand"
4
  import { ClapperPlugin, ClapperPluginApi, ClapperPluginMeta, PluginsStore, PublicServices } from "@aitube/clapper-services"
5
+ import { useTimeline } from "@aitube/timeline"
6
 
7
  import { getDefaultPluginsState } from "./getDefaultPluginsState"
8
  import { useScriptEditor } from "../editors/script-editor/useScriptEditor"
9
  import { useMonitor } from "../monitor/useMonitor"
10
+ import { useTasks } from "@/components/tasks/useTasks"
11
  import { useRenderer } from "../renderer"
12
  import { useBroadcast } from "../broadcast/useBroadcast"
13
  import { useResolver } from "../resolver/useResolver"
14
  import { useAssistant } from "../assistant/useAssistant"
15
  import { useAudio } from "../audio/useAudio"
16
  import { useUI } from "../ui"
 
17
  import { fetchAndRun } from "./fetchAndRun"
 
18
  import { useEditors, useEntityEditor, useProjectEditor, useSegmentEditor } from "../editors"
19
  import { useSimulator } from "../simulator/useSimulator"
20
+ import { useIO } from "../io/useIO"
21
 
22
  export const usePlugins = create<PluginsStore>((set, get) => ({
23
  ...getDefaultPluginsState(),
 
62
  broadcast: useBroadcast,
63
  simulator: useSimulator,
64
  ui: useUI,
65
+ io: useIO,
66
  }
67
  },
68
  pluginApiGetSettings: async (id: string) => {
src/services/renderer/useRenderer.ts CHANGED
@@ -226,7 +226,3 @@ export const useRenderer = create<RendererStore>((set, get) => ({
226
 
227
  }
228
  }))
229
-
230
- if (typeof window !== "undefined") {
231
- (window as any).useRenderer = useRenderer
232
- }
 
226
 
227
  }
228
  }))
 
 
 
 
src/services/resolver/useResolver.ts CHANGED
@@ -590,9 +590,4 @@ export const useResolver = create<ResolverStore>((set, get) => ({
590
  }
591
  return segment
592
  }
593
-
594
  }))
595
-
596
- if (typeof window !== "undefined") {
597
- (window as any).useResolver = useResolver
598
- }
 
590
  }
591
  return segment
592
  }
 
593
  }))
 
 
 
 
src/services/settings/useSettings.ts CHANGED
@@ -662,7 +662,3 @@ export const useSettings = create<SettingsStore>()(
662
  },
663
  ),
664
  )
665
-
666
- if (typeof window !== "undefined") {
667
- (window as any).useSettings = useSettings
668
- }
 
662
  },
663
  ),
664
  )
 
 
 
 
src/services/simulator/useSimulator.ts CHANGED
@@ -28,7 +28,3 @@ export const useSimulator = create<SimulatorStore>((set, get) => ({
28
  return isRunning
29
  }
30
  }))
31
-
32
- if (typeof window !== "undefined") {
33
- (window as any).useSimulator = useSimulator
34
- }
 
28
  return isRunning
29
  }
30
  }))
 
 
 
 
src/services/ui/theme.ts CHANGED
@@ -15,33 +15,31 @@
15
 
16
  import { ClapSegmentCategory } from "@aitube/clap"
17
  import { UITheme, UIThemeName } from "@aitube/clapper-services"
18
- import { ClapSegmentCategoryHues, ClapTimelineTheme } from "@aitube/timeline"
19
 
20
- // TODO: give more control here, we should be able to adjust the individual
21
- // saturation and lightness
22
- export const baseClapSegmentCategoryHues: ClapSegmentCategoryHues = {
23
- [ClapSegmentCategory.SPLAT]: 347,
24
- [ClapSegmentCategory.MESH]: 32,
25
- [ClapSegmentCategory.DEPTH]: 242,
26
- [ClapSegmentCategory.EVENT]: 270,
27
- [ClapSegmentCategory.INTERFACE]: 216,
28
- [ClapSegmentCategory.PHENOMENON]: 270,
29
- [ClapSegmentCategory.VIDEO]: 70,
30
- [ClapSegmentCategory.STORYBOARD]: 70,
31
- [ClapSegmentCategory.TRANSITION]: 55,
32
- [ClapSegmentCategory.CHARACTER]: 285.8,
33
- [ClapSegmentCategory.LOCATION]: 80.9,
34
- [ClapSegmentCategory.TIME]: 250,
35
- [ClapSegmentCategory.ERA]: 250,
36
- [ClapSegmentCategory.LIGHTING]: 50,
37
- [ClapSegmentCategory.WEATHER]: 197.2,
38
- [ClapSegmentCategory.ACTION]: 3,
39
- [ClapSegmentCategory.MUSIC]: 100,
40
- [ClapSegmentCategory.SOUND]: 60,
41
- [ClapSegmentCategory.DIALOGUE]: 23,
42
- [ClapSegmentCategory.STYLE]: 285,
43
- [ClapSegmentCategory.CAMERA]: 10,
44
- [ClapSegmentCategory.GENERIC]: 200,
45
  }
46
 
47
  // a grey and yellow theme, inspired by the entertainment world
@@ -86,9 +84,7 @@ export const backstage: UITheme = {
86
  backgroundColor: "#27272A",
87
  },
88
  cell: {
89
- saturation: 30,
90
- lightness: 78.6,
91
- hues: baseClapSegmentCategoryHues,
92
 
93
  waveform: {
94
  // "original" style
@@ -153,10 +149,7 @@ export const midnight: UITheme = {
153
  backgroundColor: "#27372A",
154
  },
155
  cell: {
156
- saturation: 30,
157
- lightness: 78.6,
158
- hues: baseClapSegmentCategoryHues,
159
-
160
  waveform: {
161
  // "original" style
162
  // lineSpacing: 2,
@@ -219,10 +212,7 @@ export const lavender: UITheme = {
219
  backgroundColor: "#27272A",
220
  },
221
  cell: {
222
- saturation: 30,
223
- lightness: 78.6,
224
- hues: baseClapSegmentCategoryHues,
225
-
226
  waveform: {
227
  // "original" style
228
  // lineSpacing: 2,
@@ -285,9 +275,7 @@ export const flix: UITheme = {
285
  backgroundColor: "#27272A",
286
  },
287
  cell: {
288
- saturation: 30,
289
- lightness: 78.6,
290
- hues: baseClapSegmentCategoryHues,
291
 
292
  waveform: {
293
  // "original" style
@@ -351,9 +339,7 @@ export const lore: UITheme = {
351
  backgroundColor: "#27272A",
352
  },
353
  cell: {
354
- saturation: 30,
355
- lightness: 78.6,
356
- hues: baseClapSegmentCategoryHues,
357
 
358
  waveform: {
359
  // "original" style
@@ -418,9 +404,7 @@ export const gordon: UITheme = {
418
  backgroundColor: "#535353",
419
  },
420
  cell: {
421
- saturation: 30,
422
- lightness: 78.6,
423
- hues: baseClapSegmentCategoryHues,
424
 
425
  waveform: {
426
  // "original" style
@@ -486,9 +470,7 @@ export const system360: UITheme = {
486
  backgroundColor: "#f0f0e8",
487
  },
488
  cell: {
489
- saturation: 30,
490
- lightness: 78.6,
491
- hues: baseClapSegmentCategoryHues,
492
 
493
  waveform: {
494
  // "original" style
@@ -552,9 +534,30 @@ export const silent: UITheme = {
552
  backgroundColor: "#1f1f1f",
553
  },
554
  cell: {
555
- saturation: 0,
556
- lightness: 85,
557
- hues: baseClapSegmentCategoryHues,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
558
 
559
  waveform: {
560
  // "original" style
@@ -618,9 +621,7 @@ export const sandy: UITheme = {
618
  backgroundColor: "#b0a3a3",
619
  },
620
  cell: {
621
- saturation: 30,
622
- lightness: 78.6,
623
- hues: baseClapSegmentCategoryHues,
624
 
625
  waveform: {
626
  // "original" style
 
15
 
16
  import { ClapSegmentCategory } from "@aitube/clap"
17
  import { UITheme, UIThemeName } from "@aitube/clapper-services"
18
+ import { ClapSegmentCategoryColors } from "@aitube/timeline"
19
 
20
+ export const baseClapSegmentCategoryColors: ClapSegmentCategoryColors = {
21
+ [ClapSegmentCategory.SPLAT]: { hue: 347, saturation: 30, lightness: 78.6 },
22
+ [ClapSegmentCategory.MESH]: { hue: 32, saturation: 30, lightness: 78.6 },
23
+ [ClapSegmentCategory.DEPTH]: { hue: 242, saturation: 30, lightness: 78.6 },
24
+ [ClapSegmentCategory.EVENT]: { hue: 270, saturation: 30, lightness: 78.6 },
25
+ [ClapSegmentCategory.INTERFACE]: { hue: 216, saturation: 30, lightness: 78.6 },
26
+ [ClapSegmentCategory.PHENOMENON]: { hue: 270, saturation: 30, lightness: 78.6 },
27
+ [ClapSegmentCategory.VIDEO]: { hue: 70, saturation: 30, lightness: 78.6 },
28
+ [ClapSegmentCategory.STORYBOARD]: { hue: 70, saturation: 30, lightness: 78.6 },
29
+ [ClapSegmentCategory.TRANSITION]: { hue: 55, saturation: 30, lightness: 78.6 },
30
+ [ClapSegmentCategory.CHARACTER]: { hue: 285.8, saturation: 30, lightness: 78.6 },
31
+ [ClapSegmentCategory.LOCATION]: { hue: 80.9, saturation: 30, lightness: 78.6 },
32
+ [ClapSegmentCategory.TIME]: { hue: 250, saturation: 30, lightness: 78.6 },
33
+ [ClapSegmentCategory.ERA]: { hue: 250, saturation: 30, lightness: 78.6 },
34
+ [ClapSegmentCategory.LIGHTING]: { hue: 50, saturation: 30, lightness: 78.6 },
35
+ [ClapSegmentCategory.WEATHER]: { hue: 197.2, saturation: 30, lightness: 78.6 },
36
+ [ClapSegmentCategory.ACTION]: { hue: 3, saturation: 30, lightness: 78.6 },
37
+ [ClapSegmentCategory.MUSIC]: { hue: 100, saturation: 30, lightness: 78.6 },
38
+ [ClapSegmentCategory.SOUND]: { hue: 60, saturation: 30, lightness: 78.6 },
39
+ [ClapSegmentCategory.DIALOGUE]: { hue: 23, saturation: 30, lightness: 78.6 },
40
+ [ClapSegmentCategory.STYLE]: { hue: 285, saturation: 30, lightness: 78.6 },
41
+ [ClapSegmentCategory.CAMERA]: { hue: 10, saturation: 30, lightness: 78.6 },
42
+ [ClapSegmentCategory.GENERIC]: { hue: 200, saturation: 30, lightness: 78.6 },
 
 
43
  }
44
 
45
  // a grey and yellow theme, inspired by the entertainment world
 
84
  backgroundColor: "#27272A",
85
  },
86
  cell: {
87
+ categoryColors: baseClapSegmentCategoryColors,
 
 
88
 
89
  waveform: {
90
  // "original" style
 
149
  backgroundColor: "#27372A",
150
  },
151
  cell: {
152
+ categoryColors: baseClapSegmentCategoryColors,
 
 
 
153
  waveform: {
154
  // "original" style
155
  // lineSpacing: 2,
 
212
  backgroundColor: "#27272A",
213
  },
214
  cell: {
215
+ categoryColors: baseClapSegmentCategoryColors,
 
 
 
216
  waveform: {
217
  // "original" style
218
  // lineSpacing: 2,
 
275
  backgroundColor: "#27272A",
276
  },
277
  cell: {
278
+ categoryColors: baseClapSegmentCategoryColors,
 
 
279
 
280
  waveform: {
281
  // "original" style
 
339
  backgroundColor: "#27272A",
340
  },
341
  cell: {
342
+ categoryColors: baseClapSegmentCategoryColors,
 
 
343
 
344
  waveform: {
345
  // "original" style
 
404
  backgroundColor: "#535353",
405
  },
406
  cell: {
407
+ categoryColors: baseClapSegmentCategoryColors,
 
 
408
 
409
  waveform: {
410
  // "original" style
 
470
  backgroundColor: "#f0f0e8",
471
  },
472
  cell: {
473
+ categoryColors: baseClapSegmentCategoryColors,
 
 
474
 
475
  waveform: {
476
  // "original" style
 
534
  backgroundColor: "#1f1f1f",
535
  },
536
  cell: {
537
+ categoryColors: {
538
+ [ClapSegmentCategory.SPLAT]: { hue: 347, saturation: 0, lightness: 85 },
539
+ [ClapSegmentCategory.MESH]: { hue: 32, saturation: 0, lightness: 85 },
540
+ [ClapSegmentCategory.DEPTH]: { hue: 242, saturation: 0, lightness: 85 },
541
+ [ClapSegmentCategory.EVENT]: { hue: 270, saturation: 0, lightness: 85 },
542
+ [ClapSegmentCategory.INTERFACE]: { hue: 216, saturation: 0, lightness: 85 },
543
+ [ClapSegmentCategory.PHENOMENON]: { hue: 270, saturation: 0, lightness: 85 },
544
+ [ClapSegmentCategory.VIDEO]: { hue: 70, saturation: 0, lightness: 85 },
545
+ [ClapSegmentCategory.STORYBOARD]: { hue: 70, saturation: 0, lightness: 85 },
546
+ [ClapSegmentCategory.TRANSITION]: { hue: 55, saturation: 0, lightness: 85 },
547
+ [ClapSegmentCategory.CHARACTER]: { hue: 285.8, saturation: 0, lightness: 85 },
548
+ [ClapSegmentCategory.LOCATION]: { hue: 80.9, saturation: 0, lightness: 85 },
549
+ [ClapSegmentCategory.TIME]: { hue: 250, saturation: 0, lightness: 85 },
550
+ [ClapSegmentCategory.ERA]: { hue: 250, saturation: 0, lightness: 85 },
551
+ [ClapSegmentCategory.LIGHTING]: { hue: 50, saturation: 0, lightness: 85 },
552
+ [ClapSegmentCategory.WEATHER]: { hue: 197.2, saturation: 0, lightness: 85 },
553
+ [ClapSegmentCategory.ACTION]: { hue: 3, saturation: 0, lightness: 85 },
554
+ [ClapSegmentCategory.MUSIC]: { hue: 100, saturation: 0, lightness: 85 },
555
+ [ClapSegmentCategory.SOUND]: { hue: 60, saturation: 0, lightness: 85 },
556
+ [ClapSegmentCategory.DIALOGUE]: { hue: 23, saturation: 0, lightness: 85 },
557
+ [ClapSegmentCategory.STYLE]: { hue: 285, saturation: 0, lightness: 85 },
558
+ [ClapSegmentCategory.CAMERA]: { hue: 10, saturation: 0, lightness: 85 },
559
+ [ClapSegmentCategory.GENERIC]: { hue: 200, saturation: 0, lightness: 85 },
560
+ },
561
 
562
  waveform: {
563
  // "original" style
 
621
  backgroundColor: "#b0a3a3",
622
  },
623
  cell: {
624
+ categoryColors: baseClapSegmentCategoryColors,
 
 
625
 
626
  waveform: {
627
  // "original" style
src/services/ui/useUI.ts CHANGED
@@ -75,7 +75,3 @@ export const useUI = create<UIStore>()(
75
  },
76
  ),
77
  )
78
-
79
- if (typeof window !== "undefined") {
80
- (window as any).useUI = useUI
81
- }
 
75
  },
76
  ),
77
  )