@neta-art/cohub 5.10.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -46
- package/dist/board/animation.d.ts +156 -67
- package/dist/board/animation.js +161 -305
- package/dist/board/geometry.js +4 -4
- package/dist/board/index.d.ts +4 -4
- package/dist/board/index.js +3 -4
- package/dist/board/mutation.d.ts +5 -11
- package/dist/board/mutation.js +9 -46
- package/dist/chunks/http.d.ts +23 -12
- package/dist/chunks/http.js +674 -316
- package/dist/chunks/websocket.d.ts +2623 -201
- package/dist/http.d.ts +3 -3
- package/dist/index.d.ts +158 -299
- package/dist/index.js +164 -306
- package/dist/protocol/dist/board-authoring.d.ts +1 -0
- package/dist/protocol/dist/board-authoring.js +217 -0
- package/dist/protocol/dist/board-capability-registry.d.ts +2 -0
- package/dist/protocol/dist/board-capability-registry.js +74 -0
- package/dist/protocol/dist/board-codec.d.ts +2 -0
- package/dist/protocol/dist/board-codec.js +4 -0
- package/dist/protocol/dist/board-composition.d.ts +258 -0
- package/dist/protocol/dist/board-composition.js +318 -0
- package/dist/protocol/dist/board-constants.js +0 -25
- package/dist/protocol/dist/board-node.d.ts +1 -12
- package/dist/protocol/dist/board-node.js +1 -81
- package/dist/protocol/dist/board-upgrade.d.ts +1 -0
- package/dist/protocol/dist/board-upgrade.js +2 -0
- package/dist/protocol/dist/board.d.ts +19 -92
- package/dist/protocol/dist/board.js +18 -78
- package/dist/protocol/dist/index.d.ts +8 -3
- package/dist/protocol/dist/index.js +9 -4
- package/dist/types.d.ts +2 -1
- package/docs/work-runtime-guide.md +19 -3
- package/package.json +3 -2
- package/dist/board/nodes.d.ts +0 -113
- package/dist/board/nodes.js +0 -154
package/dist/chunks/http.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { D as getRealtimeBoardRoom, O as getRealtimeSpaceRoom, n as HttpTransport, t as HttpError, y as isUuid } from "./transport.js";
|
|
2
2
|
import { a as resolveApiBaseUrl } from "./environment.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
import "perfect-freehand";
|
|
5
4
|
//#region src/apis/channels.ts
|
|
6
5
|
var ChannelsApi = class {
|
|
7
6
|
transport;
|
|
@@ -454,7 +453,6 @@ const DEFAULT_BOARD_RENDER_LIMITS = {
|
|
|
454
453
|
simulationSteps: 1e5
|
|
455
454
|
};
|
|
456
455
|
const BOARD_BUILTIN_CLIP_KINDS = [
|
|
457
|
-
"motion.keyframes",
|
|
458
456
|
"motion.path",
|
|
459
457
|
"draw.reveal",
|
|
460
458
|
"draw.handwrite",
|
|
@@ -463,9 +461,6 @@ const BOARD_BUILTIN_CLIP_KINDS = [
|
|
|
463
461
|
"effects.trail",
|
|
464
462
|
"effects.impact",
|
|
465
463
|
"effects.flash",
|
|
466
|
-
"effects.color",
|
|
467
|
-
"camera.pan",
|
|
468
|
-
"camera.zoom",
|
|
469
464
|
"camera.focus",
|
|
470
465
|
"camera.shake"
|
|
471
466
|
];
|
|
@@ -478,16 +473,6 @@ function clampBoardStrokeSize(size) {
|
|
|
478
473
|
}
|
|
479
474
|
function clipSchema(id) {
|
|
480
475
|
switch (id) {
|
|
481
|
-
case "motion.keyframes": return { params: {
|
|
482
|
-
x: {
|
|
483
|
-
coordinateSpace: "world-offset",
|
|
484
|
-
unit: "board"
|
|
485
|
-
},
|
|
486
|
-
y: {
|
|
487
|
-
coordinateSpace: "world-offset",
|
|
488
|
-
unit: "board"
|
|
489
|
-
}
|
|
490
|
-
} };
|
|
491
476
|
case "motion.path": return { params: { points: {
|
|
492
477
|
coordinateSpace: "world-offset",
|
|
493
478
|
unit: "board"
|
|
@@ -496,17 +481,6 @@ function clipSchema(id) {
|
|
|
496
481
|
coordinateSpace: "world",
|
|
497
482
|
unit: "board"
|
|
498
483
|
} } };
|
|
499
|
-
case "camera.pan": return { params: {
|
|
500
|
-
x: {
|
|
501
|
-
coordinateSpace: "screen-offset",
|
|
502
|
-
unit: "css-px"
|
|
503
|
-
},
|
|
504
|
-
y: {
|
|
505
|
-
coordinateSpace: "screen-offset",
|
|
506
|
-
unit: "css-px"
|
|
507
|
-
}
|
|
508
|
-
} };
|
|
509
|
-
case "camera.zoom": return { params: { scale: { unit: "ratio" } } };
|
|
510
484
|
case "camera.focus": return { params: {
|
|
511
485
|
focus: {
|
|
512
486
|
coordinateSpace: "world",
|
|
@@ -693,13 +667,529 @@ function createBoardConnection(input) {
|
|
|
693
667
|
};
|
|
694
668
|
}
|
|
695
669
|
//#endregion
|
|
696
|
-
//#region
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
670
|
+
//#region ../protocol/dist/board-composition.js
|
|
671
|
+
const idSchema$3 = z.string().min(1).max(160);
|
|
672
|
+
const finiteSchema$3 = z.number().finite();
|
|
673
|
+
const jsonObjectSchema$2 = z.record(z.string(), z.unknown());
|
|
674
|
+
const BoardEasingSchema = z.enum([
|
|
675
|
+
"linear",
|
|
676
|
+
"ease-in-quad",
|
|
677
|
+
"ease-out-quad",
|
|
678
|
+
"ease-in-out-quad",
|
|
679
|
+
"ease-in-cubic",
|
|
680
|
+
"ease-out-cubic",
|
|
681
|
+
"ease-in-out-cubic",
|
|
682
|
+
"ease-out-quart",
|
|
683
|
+
"ease-out-expo"
|
|
684
|
+
]);
|
|
685
|
+
const BoardAnimationTargetSchema = z.discriminatedUnion("type", [
|
|
686
|
+
z.object({
|
|
687
|
+
type: z.literal("item"),
|
|
688
|
+
itemId: idSchema$3
|
|
689
|
+
}).strict(),
|
|
690
|
+
z.object({
|
|
691
|
+
type: z.literal("effect"),
|
|
692
|
+
effectId: idSchema$3
|
|
693
|
+
}).strict(),
|
|
694
|
+
z.object({ type: z.literal("camera") }).strict(),
|
|
695
|
+
z.object({ type: z.literal("board") }).strict()
|
|
696
|
+
]);
|
|
697
|
+
const vector2Schema = z.object({
|
|
698
|
+
x: finiteSchema$3,
|
|
699
|
+
y: finiteSchema$3
|
|
700
|
+
}).strict();
|
|
701
|
+
const scaleSchema = z.union([finiteSchema$3.positive(), z.object({
|
|
702
|
+
x: finiteSchema$3.positive(),
|
|
703
|
+
y: finiteSchema$3.positive()
|
|
704
|
+
}).strict()]);
|
|
705
|
+
const BOARD_ANIMATION_CHANNELS = {
|
|
706
|
+
"transform.translation": {
|
|
707
|
+
targets: ["item"],
|
|
708
|
+
value: vector2Schema,
|
|
709
|
+
interpolations: ["linear", "step"],
|
|
710
|
+
coordinateSpace: "world-offset",
|
|
711
|
+
unit: "board"
|
|
712
|
+
},
|
|
713
|
+
"transform.rotation": {
|
|
714
|
+
targets: ["item"],
|
|
715
|
+
value: finiteSchema$3,
|
|
716
|
+
interpolations: ["linear", "step"],
|
|
717
|
+
unit: "radian"
|
|
718
|
+
},
|
|
719
|
+
"transform.scale": {
|
|
720
|
+
targets: ["item"],
|
|
721
|
+
value: scaleSchema,
|
|
722
|
+
interpolations: ["linear", "step"],
|
|
723
|
+
unit: "ratio"
|
|
724
|
+
},
|
|
725
|
+
"style.opacity": {
|
|
726
|
+
targets: ["item"],
|
|
727
|
+
value: finiteSchema$3.min(0).max(1),
|
|
728
|
+
interpolations: ["linear", "step"],
|
|
729
|
+
unit: "ratio"
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
const BoardTrackKeyframeSchema = z.object({
|
|
733
|
+
time: finiteSchema$3.nonnegative(),
|
|
734
|
+
value: z.unknown(),
|
|
735
|
+
easing: BoardEasingSchema.optional()
|
|
736
|
+
}).strict();
|
|
737
|
+
const BoardTrackSchema = z.object({
|
|
738
|
+
id: idSchema$3,
|
|
739
|
+
target: BoardAnimationTargetSchema,
|
|
740
|
+
channel: z.string().min(1).max(160),
|
|
741
|
+
channelVersion: z.number().int().positive().default(1),
|
|
742
|
+
interpolation: z.enum(["linear", "step"]).default("linear"),
|
|
743
|
+
fill: z.enum([
|
|
744
|
+
"none",
|
|
745
|
+
"backwards",
|
|
746
|
+
"forwards",
|
|
747
|
+
"both"
|
|
748
|
+
]).default("none"),
|
|
749
|
+
keyframes: z.array(BoardTrackKeyframeSchema).min(1).max(1e5),
|
|
750
|
+
metadata: jsonObjectSchema$2.default({})
|
|
751
|
+
}).strict().superRefine((track, context) => {
|
|
752
|
+
let previous = -1;
|
|
753
|
+
for (const [index, keyframe] of track.keyframes.entries()) {
|
|
754
|
+
if (keyframe.time <= previous) context.addIssue({
|
|
755
|
+
code: "custom",
|
|
756
|
+
message: "keyframe times must be strictly increasing",
|
|
757
|
+
path: [
|
|
758
|
+
"keyframes",
|
|
759
|
+
index,
|
|
760
|
+
"time"
|
|
761
|
+
]
|
|
762
|
+
});
|
|
763
|
+
previous = keyframe.time;
|
|
764
|
+
}
|
|
765
|
+
const channel = BOARD_ANIMATION_CHANNELS[track.channel];
|
|
766
|
+
if (!channel) {
|
|
767
|
+
if (!/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+){2,}$/.test(track.channel)) context.addIssue({
|
|
768
|
+
code: "custom",
|
|
769
|
+
message: `unknown channel: ${track.channel}`,
|
|
770
|
+
path: ["channel"]
|
|
771
|
+
});
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
if (!channel.targets.includes(track.target.type)) context.addIssue({
|
|
775
|
+
code: "custom",
|
|
776
|
+
message: `${track.channel} cannot target ${track.target.type}`,
|
|
777
|
+
path: ["target"]
|
|
778
|
+
});
|
|
779
|
+
if (!channel.interpolations.includes(track.interpolation)) context.addIssue({
|
|
780
|
+
code: "custom",
|
|
781
|
+
message: `${track.channel} does not support ${track.interpolation} interpolation`,
|
|
782
|
+
path: ["interpolation"]
|
|
783
|
+
});
|
|
784
|
+
for (const [index, keyframe] of track.keyframes.entries()) {
|
|
785
|
+
const parsed = channel.value.safeParse(keyframe.value);
|
|
786
|
+
if (!parsed.success) context.addIssue({
|
|
787
|
+
code: "custom",
|
|
788
|
+
message: parsed.error.issues[0]?.message ?? `invalid value for ${track.channel}`,
|
|
789
|
+
path: [
|
|
790
|
+
"keyframes",
|
|
791
|
+
index,
|
|
792
|
+
"value"
|
|
793
|
+
]
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
});
|
|
797
|
+
const extensionKindSchema = z.string().regex(/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)+$/).max(160);
|
|
798
|
+
const BoardProceduralClipSchema = z.object({
|
|
799
|
+
id: idSchema$3,
|
|
800
|
+
kind: extensionKindSchema,
|
|
801
|
+
kindVersion: z.number().int().positive(),
|
|
802
|
+
target: BoardAnimationTargetSchema,
|
|
803
|
+
start: finiteSchema$3.nonnegative(),
|
|
804
|
+
duration: finiteSchema$3.positive(),
|
|
805
|
+
layer: z.enum([
|
|
806
|
+
"behind",
|
|
807
|
+
"content",
|
|
808
|
+
"front",
|
|
809
|
+
"screen"
|
|
810
|
+
]).default("content"),
|
|
811
|
+
fill: z.enum([
|
|
812
|
+
"none",
|
|
813
|
+
"backwards",
|
|
814
|
+
"forwards",
|
|
815
|
+
"both"
|
|
816
|
+
]).default("none"),
|
|
817
|
+
easing: BoardEasingSchema.default("linear"),
|
|
818
|
+
params: jsonObjectSchema$2.default({}),
|
|
819
|
+
assetRefs: z.array(z.object({
|
|
820
|
+
type: z.enum(["space-file", "extension"]),
|
|
821
|
+
ref: z.string().min(1).max(4096),
|
|
822
|
+
digest: z.string().min(16).max(160).optional()
|
|
823
|
+
}).strict()).default([]),
|
|
824
|
+
seed: idSchema$3,
|
|
825
|
+
metadata: jsonObjectSchema$2.default({})
|
|
826
|
+
}).strict();
|
|
827
|
+
const BoardTimelineMarkerSchema = z.object({
|
|
828
|
+
id: idSchema$3,
|
|
829
|
+
time: finiteSchema$3.nonnegative(),
|
|
830
|
+
duration: finiteSchema$3.nonnegative().optional(),
|
|
831
|
+
metadata: jsonObjectSchema$2.default({})
|
|
832
|
+
}).strict();
|
|
833
|
+
const BoardTimelineSchema = z.object({
|
|
834
|
+
duration: finiteSchema$3.nonnegative(),
|
|
835
|
+
tracks: z.array(BoardTrackSchema).max(5e4).default([]),
|
|
836
|
+
clips: z.array(BoardProceduralClipSchema).max(5e4).default([]),
|
|
837
|
+
markers: z.array(BoardTimelineMarkerSchema).max(1e4).default([])
|
|
838
|
+
}).strict().superRefine((timeline, context) => {
|
|
839
|
+
const ids = /* @__PURE__ */ new Set();
|
|
840
|
+
const channels = /* @__PURE__ */ new Set();
|
|
841
|
+
for (const [index, track] of timeline.tracks.entries()) {
|
|
842
|
+
if (ids.has(track.id)) context.addIssue({
|
|
843
|
+
code: "custom",
|
|
844
|
+
message: `duplicate timeline id: ${track.id}`,
|
|
845
|
+
path: [
|
|
846
|
+
"tracks",
|
|
847
|
+
index,
|
|
848
|
+
"id"
|
|
849
|
+
]
|
|
850
|
+
});
|
|
851
|
+
ids.add(track.id);
|
|
852
|
+
const targetId = track.target.type === "item" ? track.target.itemId : track.target.type === "effect" ? track.target.effectId : track.target.type;
|
|
853
|
+
const key = `${track.target.type}:${targetId}:${track.channel}`;
|
|
854
|
+
if (channels.has(key)) context.addIssue({
|
|
855
|
+
code: "custom",
|
|
856
|
+
message: `multiple tracks control ${key}`,
|
|
857
|
+
path: [
|
|
858
|
+
"tracks",
|
|
859
|
+
index,
|
|
860
|
+
"channel"
|
|
861
|
+
]
|
|
862
|
+
});
|
|
863
|
+
channels.add(key);
|
|
864
|
+
for (const [keyframeIndex, keyframe] of track.keyframes.entries()) if (keyframe.time > timeline.duration) context.addIssue({
|
|
865
|
+
code: "custom",
|
|
866
|
+
message: "keyframe exceeds timeline duration",
|
|
867
|
+
path: [
|
|
868
|
+
"tracks",
|
|
869
|
+
index,
|
|
870
|
+
"keyframes",
|
|
871
|
+
keyframeIndex,
|
|
872
|
+
"time"
|
|
873
|
+
]
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
for (const [index, clip] of timeline.clips.entries()) {
|
|
877
|
+
if (ids.has(clip.id)) context.addIssue({
|
|
878
|
+
code: "custom",
|
|
879
|
+
message: `duplicate timeline id: ${clip.id}`,
|
|
880
|
+
path: [
|
|
881
|
+
"clips",
|
|
882
|
+
index,
|
|
883
|
+
"id"
|
|
884
|
+
]
|
|
885
|
+
});
|
|
886
|
+
ids.add(clip.id);
|
|
887
|
+
if (clip.start + clip.duration > timeline.duration) context.addIssue({
|
|
888
|
+
code: "custom",
|
|
889
|
+
message: "clip exceeds timeline duration",
|
|
890
|
+
path: [
|
|
891
|
+
"clips",
|
|
892
|
+
index,
|
|
893
|
+
"duration"
|
|
894
|
+
]
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
for (const [index, marker] of timeline.markers.entries()) {
|
|
898
|
+
if (ids.has(marker.id)) context.addIssue({
|
|
899
|
+
code: "custom",
|
|
900
|
+
message: `duplicate timeline id: ${marker.id}`,
|
|
901
|
+
path: [
|
|
902
|
+
"markers",
|
|
903
|
+
index,
|
|
904
|
+
"id"
|
|
905
|
+
]
|
|
906
|
+
});
|
|
907
|
+
ids.add(marker.id);
|
|
908
|
+
if (marker.time + (marker.duration ?? 0) > timeline.duration) context.addIssue({
|
|
909
|
+
code: "custom",
|
|
910
|
+
message: "marker exceeds timeline duration",
|
|
911
|
+
path: ["markers", index]
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
});
|
|
915
|
+
const BoardCompositionPlaybackSchema = z.object({
|
|
916
|
+
loop: z.boolean().default(false),
|
|
917
|
+
endBehavior: z.enum(["hold", "reset"]).default("hold"),
|
|
918
|
+
reducedMotion: z.discriminatedUnion("mode", [
|
|
919
|
+
z.object({ mode: z.literal("base") }).strict(),
|
|
920
|
+
z.object({
|
|
921
|
+
mode: z.literal("time"),
|
|
922
|
+
time: finiteSchema$3.nonnegative()
|
|
923
|
+
}).strict(),
|
|
924
|
+
z.object({
|
|
925
|
+
mode: z.literal("marker"),
|
|
926
|
+
markerId: idSchema$3
|
|
927
|
+
}).strict()
|
|
928
|
+
]).default({ mode: "base" })
|
|
929
|
+
}).strict();
|
|
930
|
+
const compositionFields = {
|
|
931
|
+
id: idSchema$3,
|
|
932
|
+
name: z.string().min(1).max(255),
|
|
933
|
+
timeline: BoardTimelineSchema,
|
|
934
|
+
playback: BoardCompositionPlaybackSchema.default({
|
|
935
|
+
loop: false,
|
|
936
|
+
endBehavior: "hold",
|
|
937
|
+
reducedMotion: { mode: "base" }
|
|
938
|
+
}),
|
|
939
|
+
metadata: jsonObjectSchema$2.default({})
|
|
940
|
+
};
|
|
941
|
+
function validateCompositionFallback(composition, context) {
|
|
942
|
+
const fallback = composition.playback.reducedMotion;
|
|
943
|
+
if (fallback.mode === "time" && fallback.time > composition.timeline.duration) context.addIssue({
|
|
944
|
+
code: "custom",
|
|
945
|
+
message: "reduced-motion time exceeds timeline duration",
|
|
946
|
+
path: [
|
|
947
|
+
"playback",
|
|
948
|
+
"reducedMotion",
|
|
949
|
+
"time"
|
|
950
|
+
]
|
|
951
|
+
});
|
|
952
|
+
if (fallback.mode === "marker" && !composition.timeline.markers.some((marker) => marker.id === fallback.markerId)) context.addIssue({
|
|
953
|
+
code: "custom",
|
|
954
|
+
message: `marker does not exist: ${fallback.markerId}`,
|
|
955
|
+
path: [
|
|
956
|
+
"playback",
|
|
957
|
+
"reducedMotion",
|
|
958
|
+
"markerId"
|
|
959
|
+
]
|
|
701
960
|
});
|
|
702
961
|
}
|
|
962
|
+
const BoardCompositionInputSchema = z.object(compositionFields).strict().superRefine(validateCompositionFallback);
|
|
963
|
+
const BoardCompositionSchema = z.object({
|
|
964
|
+
...compositionFields,
|
|
965
|
+
revision: z.number().int().nonnegative().default(0)
|
|
966
|
+
}).strict().superRefine(validateCompositionFallback);
|
|
967
|
+
/** Accept inspect/get output as apply input while stripping server-owned revision. */
|
|
968
|
+
function parseBoardCompositionInput(value) {
|
|
969
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
970
|
+
const { revision: _revision, ...input } = value;
|
|
971
|
+
return BoardCompositionInputSchema.parse(input);
|
|
972
|
+
}
|
|
973
|
+
return BoardCompositionInputSchema.parse(value);
|
|
974
|
+
}
|
|
975
|
+
const BOARD_ANIMATION_CHANNEL_CAPABILITIES = Object.entries(BOARD_ANIMATION_CHANNELS).map(([id, definition]) => ({
|
|
976
|
+
id,
|
|
977
|
+
version: 1,
|
|
978
|
+
targets: definition.targets,
|
|
979
|
+
interpolations: definition.interpolations,
|
|
980
|
+
..."coordinateSpace" in definition ? { coordinateSpace: definition.coordinateSpace } : {},
|
|
981
|
+
..."unit" in definition ? { unit: definition.unit } : {},
|
|
982
|
+
valueSchema: z.toJSONSchema(definition.value)
|
|
983
|
+
}));
|
|
984
|
+
//#endregion
|
|
985
|
+
//#region ../protocol/dist/board-authoring.js
|
|
986
|
+
const BOARD_COLOR_IDS$1 = [
|
|
987
|
+
"brand",
|
|
988
|
+
"neutral",
|
|
989
|
+
"black",
|
|
990
|
+
"white",
|
|
991
|
+
"blue",
|
|
992
|
+
"green",
|
|
993
|
+
"amber",
|
|
994
|
+
"violet",
|
|
995
|
+
"rose"
|
|
996
|
+
];
|
|
997
|
+
const BOARD_GEO_KINDS$1 = [
|
|
998
|
+
"rectangle",
|
|
999
|
+
"rounded",
|
|
1000
|
+
"ellipse",
|
|
1001
|
+
"diamond",
|
|
1002
|
+
"triangle"
|
|
1003
|
+
];
|
|
1004
|
+
const idSchema$2 = z.string().min(1).max(160);
|
|
1005
|
+
const jsonObjectSchema$1 = z.record(z.string(), z.unknown());
|
|
1006
|
+
const finiteSchema$2 = z.number().finite();
|
|
1007
|
+
const extensionTypeSchema = z.string().regex(/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)+$/).max(160);
|
|
1008
|
+
const BoardAuthoringFrameSchema = z.object({
|
|
1009
|
+
x: finiteSchema$2,
|
|
1010
|
+
y: finiteSchema$2,
|
|
1011
|
+
width: finiteSchema$2.positive(),
|
|
1012
|
+
height: finiteSchema$2.positive(),
|
|
1013
|
+
rotation: finiteSchema$2.default(0)
|
|
1014
|
+
}).strict();
|
|
1015
|
+
const pointSchema$1 = z.object({
|
|
1016
|
+
x: finiteSchema$2,
|
|
1017
|
+
y: finiteSchema$2,
|
|
1018
|
+
p: finiteSchema$2.min(0).max(1).default(.5)
|
|
1019
|
+
}).strict();
|
|
1020
|
+
const worldPointSchema$1 = z.object({
|
|
1021
|
+
x: finiteSchema$2,
|
|
1022
|
+
y: finiteSchema$2
|
|
1023
|
+
}).strict();
|
|
1024
|
+
const cropSchema = z.object({
|
|
1025
|
+
x: finiteSchema$2.min(0).max(1),
|
|
1026
|
+
y: finiteSchema$2.min(0).max(1),
|
|
1027
|
+
w: finiteSchema$2.min(0).max(1),
|
|
1028
|
+
h: finiteSchema$2.min(0).max(1)
|
|
1029
|
+
}).strict();
|
|
1030
|
+
const BoardItemStyleSchema$1 = z.object({
|
|
1031
|
+
color: z.enum(BOARD_COLOR_IDS$1).optional(),
|
|
1032
|
+
strokeWidth: finiteSchema$2.min(1).max(64).optional(),
|
|
1033
|
+
fillOpacity: finiteSchema$2.min(0).max(1).optional()
|
|
1034
|
+
}).strict();
|
|
1035
|
+
const sourceSnapshotSchema = z.record(z.string(), z.unknown());
|
|
1036
|
+
function isSafeBoardSourcePath(value) {
|
|
1037
|
+
return value.length > 0 && value.length <= 4096 && !value.startsWith("/") && !value.startsWith("\\") && !value.includes("\\") && value.split("/").every((part) => part.length > 0 && part !== "." && part !== "..");
|
|
1038
|
+
}
|
|
1039
|
+
const BoardItemSourceSchema = z.object({
|
|
1040
|
+
kind: z.literal("space-file"),
|
|
1041
|
+
path: z.string().refine(isSafeBoardSourcePath, "source path must be a safe relative Board file path"),
|
|
1042
|
+
snapshot: sourceSnapshotSchema.optional()
|
|
1043
|
+
}).strict();
|
|
1044
|
+
const baseFields = {
|
|
1045
|
+
id: idSchema$2,
|
|
1046
|
+
frame: BoardAuthoringFrameSchema,
|
|
1047
|
+
parentId: idSchema$2.nullable().optional(),
|
|
1048
|
+
locked: z.boolean().optional(),
|
|
1049
|
+
metadata: jsonObjectSchema$1.optional()
|
|
1050
|
+
};
|
|
1051
|
+
const styledBaseFields = {
|
|
1052
|
+
...baseFields,
|
|
1053
|
+
style: BoardItemStyleSchema$1.optional()
|
|
1054
|
+
};
|
|
1055
|
+
const BoardTextAuthoringItemSchema = z.object({
|
|
1056
|
+
...styledBaseFields,
|
|
1057
|
+
type: z.literal("text"),
|
|
1058
|
+
props: z.object({
|
|
1059
|
+
text: z.string().default(""),
|
|
1060
|
+
fontSize: finiteSchema$2.min(2).max(512).default(24)
|
|
1061
|
+
}).strict()
|
|
1062
|
+
}).strict();
|
|
1063
|
+
const BoardGeoAuthoringItemSchema = z.object({
|
|
1064
|
+
...styledBaseFields,
|
|
1065
|
+
type: z.literal("geo"),
|
|
1066
|
+
props: z.object({
|
|
1067
|
+
shape: z.enum(BOARD_GEO_KINDS$1).default("rectangle"),
|
|
1068
|
+
text: z.string().default("")
|
|
1069
|
+
}).strict()
|
|
1070
|
+
}).strict();
|
|
1071
|
+
const BoardDrawAuthoringItemSchema = z.object({
|
|
1072
|
+
...styledBaseFields,
|
|
1073
|
+
type: z.literal("draw"),
|
|
1074
|
+
props: z.object({ points: z.array(pointSchema$1).min(1) }).strict()
|
|
1075
|
+
}).strict();
|
|
1076
|
+
const BoardArrowAuthoringItemSchema = z.object({
|
|
1077
|
+
...styledBaseFields,
|
|
1078
|
+
type: z.literal("arrow"),
|
|
1079
|
+
props: z.object({
|
|
1080
|
+
start: worldPointSchema$1,
|
|
1081
|
+
end: worldPointSchema$1,
|
|
1082
|
+
bend: finiteSchema$2.min(-.85).max(.85).default(0),
|
|
1083
|
+
arrowStart: z.boolean().default(false),
|
|
1084
|
+
arrowEnd: z.boolean().default(true),
|
|
1085
|
+
label: z.string().default("")
|
|
1086
|
+
}).strict()
|
|
1087
|
+
}).strict();
|
|
1088
|
+
const BoardFrameAuthoringItemSchema = z.object({
|
|
1089
|
+
...styledBaseFields,
|
|
1090
|
+
type: z.literal("frame"),
|
|
1091
|
+
props: z.object({ label: z.string().default("Frame") }).strict()
|
|
1092
|
+
}).strict();
|
|
1093
|
+
const fileBackedFields = {
|
|
1094
|
+
...baseFields,
|
|
1095
|
+
style: z.object({}).strict().optional(),
|
|
1096
|
+
source: BoardItemSourceSchema
|
|
1097
|
+
};
|
|
1098
|
+
const builtinItemSchemas = [
|
|
1099
|
+
BoardTextAuthoringItemSchema,
|
|
1100
|
+
BoardGeoAuthoringItemSchema,
|
|
1101
|
+
BoardDrawAuthoringItemSchema,
|
|
1102
|
+
BoardArrowAuthoringItemSchema,
|
|
1103
|
+
BoardFrameAuthoringItemSchema,
|
|
1104
|
+
z.object({
|
|
1105
|
+
...fileBackedFields,
|
|
1106
|
+
type: z.literal("image"),
|
|
1107
|
+
props: z.object({ crop: cropSchema.optional() }).strict()
|
|
1108
|
+
}).strict(),
|
|
1109
|
+
z.object({
|
|
1110
|
+
...fileBackedFields,
|
|
1111
|
+
type: z.literal("video"),
|
|
1112
|
+
props: z.object({}).strict()
|
|
1113
|
+
}).strict(),
|
|
1114
|
+
z.object({
|
|
1115
|
+
...fileBackedFields,
|
|
1116
|
+
type: z.literal("audio"),
|
|
1117
|
+
props: z.object({}).strict()
|
|
1118
|
+
}).strict(),
|
|
1119
|
+
z.object({
|
|
1120
|
+
...fileBackedFields,
|
|
1121
|
+
type: z.literal("file"),
|
|
1122
|
+
props: z.object({}).strict()
|
|
1123
|
+
}).strict(),
|
|
1124
|
+
z.object({
|
|
1125
|
+
...baseFields,
|
|
1126
|
+
type: z.literal("task"),
|
|
1127
|
+
props: z.object({
|
|
1128
|
+
taskRunId: z.string().min(1),
|
|
1129
|
+
snapshot: z.record(z.string(), z.unknown())
|
|
1130
|
+
}).strict(),
|
|
1131
|
+
style: z.object({}).strict().optional()
|
|
1132
|
+
}).strict()
|
|
1133
|
+
];
|
|
1134
|
+
const BoardBuiltinAuthoringItemSchema = z.discriminatedUnion("type", builtinItemSchemas);
|
|
1135
|
+
const BoardExtensionAuthoringItemSchema = z.object({
|
|
1136
|
+
...baseFields,
|
|
1137
|
+
type: extensionTypeSchema,
|
|
1138
|
+
kindVersion: z.number().int().positive(),
|
|
1139
|
+
props: jsonObjectSchema$1,
|
|
1140
|
+
style: jsonObjectSchema$1.optional(),
|
|
1141
|
+
source: z.object({
|
|
1142
|
+
kind: z.string().min(1).max(80),
|
|
1143
|
+
ref: z.string().min(1).max(4096),
|
|
1144
|
+
snapshot: jsonObjectSchema$1.optional()
|
|
1145
|
+
}).strict().optional()
|
|
1146
|
+
}).strict();
|
|
1147
|
+
const BoardAuthoringItemSchema = z.union([BoardBuiltinAuthoringItemSchema, BoardExtensionAuthoringItemSchema]);
|
|
1148
|
+
const framePatchSchema = z.object({
|
|
1149
|
+
x: finiteSchema$2.optional(),
|
|
1150
|
+
y: finiteSchema$2.optional(),
|
|
1151
|
+
width: finiteSchema$2.positive().optional(),
|
|
1152
|
+
height: finiteSchema$2.positive().optional(),
|
|
1153
|
+
rotation: finiteSchema$2.optional()
|
|
1154
|
+
}).strict();
|
|
1155
|
+
/** JSON Merge Patch semantics, constrained to the stable Item envelope. */
|
|
1156
|
+
const BoardItemPatchSchema = z.object({
|
|
1157
|
+
frame: framePatchSchema.optional(),
|
|
1158
|
+
parentId: idSchema$2.nullable().optional(),
|
|
1159
|
+
locked: z.boolean().nullable().optional(),
|
|
1160
|
+
props: jsonObjectSchema$1.optional(),
|
|
1161
|
+
style: jsonObjectSchema$1.nullable().optional(),
|
|
1162
|
+
source: jsonObjectSchema$1.nullable().optional(),
|
|
1163
|
+
metadata: jsonObjectSchema$1.nullable().optional()
|
|
1164
|
+
}).strict().refine((patch) => Object.keys(patch).length > 0, "item patch is empty");
|
|
1165
|
+
const BoardSemanticCommandSchema = z.discriminatedUnion("type", [
|
|
1166
|
+
z.object({
|
|
1167
|
+
type: z.literal("item.create"),
|
|
1168
|
+
item: BoardAuthoringItemSchema
|
|
1169
|
+
}).strict(),
|
|
1170
|
+
z.object({
|
|
1171
|
+
type: z.literal("item.patch"),
|
|
1172
|
+
itemId: idSchema$2,
|
|
1173
|
+
patch: BoardItemPatchSchema
|
|
1174
|
+
}).strict(),
|
|
1175
|
+
z.object({
|
|
1176
|
+
type: z.literal("item.replace"),
|
|
1177
|
+
itemId: idSchema$2,
|
|
1178
|
+
item: BoardAuthoringItemSchema
|
|
1179
|
+
}).strict(),
|
|
1180
|
+
z.object({
|
|
1181
|
+
type: z.literal("item.delete"),
|
|
1182
|
+
itemId: idSchema$2,
|
|
1183
|
+
cascade: z.boolean().default(false)
|
|
1184
|
+
}).strict()
|
|
1185
|
+
]);
|
|
1186
|
+
z.object({
|
|
1187
|
+
mutationId: idSchema$2,
|
|
1188
|
+
baseVersion: z.number().int().nonnegative(),
|
|
1189
|
+
clientId: idSchema$2.optional(),
|
|
1190
|
+
undoGroupId: idSchema$2.optional(),
|
|
1191
|
+
commands: z.array(BoardSemanticCommandSchema).min(1).max(5e4)
|
|
1192
|
+
}).strict();
|
|
703
1193
|
//#endregion
|
|
704
1194
|
//#region ../protocol/dist/board.js
|
|
705
1195
|
const BOARD_DOCUMENT_KIND = "cohub.board";
|
|
@@ -714,18 +1204,6 @@ z.object({
|
|
|
714
1204
|
boardId: z.string().uuid(),
|
|
715
1205
|
title: z.string().min(1).max(255)
|
|
716
1206
|
});
|
|
717
|
-
const BoardTargetSchema = z.discriminatedUnion("type", [
|
|
718
|
-
z.object({
|
|
719
|
-
type: z.literal("node"),
|
|
720
|
-
nodeId: idSchema$1
|
|
721
|
-
}),
|
|
722
|
-
z.object({
|
|
723
|
-
type: z.literal("effect"),
|
|
724
|
-
effectId: idSchema$1
|
|
725
|
-
}),
|
|
726
|
-
z.object({ type: z.literal("board") }),
|
|
727
|
-
z.object({ type: z.literal("camera") })
|
|
728
|
-
]);
|
|
729
1207
|
z.object({
|
|
730
1208
|
centerX: finiteSchema$1,
|
|
731
1209
|
centerY: finiteSchema$1,
|
|
@@ -742,12 +1220,12 @@ const BoardCameraFocusSchema = z.discriminatedUnion("type", [
|
|
|
742
1220
|
})
|
|
743
1221
|
}),
|
|
744
1222
|
z.object({
|
|
745
|
-
type: z.literal("
|
|
746
|
-
|
|
1223
|
+
type: z.literal("item"),
|
|
1224
|
+
itemId: idSchema$1
|
|
747
1225
|
}),
|
|
748
1226
|
z.object({
|
|
749
|
-
type: z.literal("
|
|
750
|
-
|
|
1227
|
+
type: z.literal("items"),
|
|
1228
|
+
itemIds: z.array(idSchema$1).min(1).max(1e3)
|
|
751
1229
|
}),
|
|
752
1230
|
z.object({
|
|
753
1231
|
type: z.literal("frame"),
|
|
@@ -772,45 +1250,13 @@ const BoardAssetRefSchema = z.object({
|
|
|
772
1250
|
ref: z.string().min(1).max(4096),
|
|
773
1251
|
digest: z.string().min(16).max(160).optional()
|
|
774
1252
|
});
|
|
775
|
-
const BoardKeyframeSchema = z.object({
|
|
776
|
-
at: finiteSchema$1.nonnegative(),
|
|
777
|
-
value: z.unknown(),
|
|
778
|
-
easing: z.string().min(1).max(80).optional()
|
|
779
|
-
});
|
|
780
|
-
const BoardClipSchema = z.object({
|
|
781
|
-
id: idSchema$1,
|
|
782
|
-
sequenceId: idSchema$1,
|
|
783
|
-
kind: extensionIdSchema,
|
|
784
|
-
kindVersion: z.number().int().positive(),
|
|
785
|
-
target: BoardTargetSchema,
|
|
786
|
-
start: finiteSchema$1.nonnegative(),
|
|
787
|
-
duration: finiteSchema$1.positive(),
|
|
788
|
-
layer: z.enum([
|
|
789
|
-
"behind",
|
|
790
|
-
"content",
|
|
791
|
-
"front",
|
|
792
|
-
"screen"
|
|
793
|
-
]).default("content"),
|
|
794
|
-
fill: z.enum([
|
|
795
|
-
"none",
|
|
796
|
-
"backwards",
|
|
797
|
-
"forwards",
|
|
798
|
-
"both"
|
|
799
|
-
]).default("none"),
|
|
800
|
-
easing: z.string().min(1).max(80).default("linear"),
|
|
801
|
-
params: jsonObjectSchema.default({}),
|
|
802
|
-
keyframes: z.array(BoardKeyframeSchema).default([]),
|
|
803
|
-
assetRefs: z.array(BoardAssetRefSchema).default([]),
|
|
804
|
-
seed: z.string().min(1).max(160),
|
|
805
|
-
metadata: jsonObjectSchema.default({})
|
|
806
|
-
});
|
|
807
1253
|
const BoardEffectSchema = z.object({
|
|
808
1254
|
id: idSchema$1,
|
|
809
1255
|
boardId: z.string().uuid(),
|
|
810
1256
|
target: z.discriminatedUnion("type", [z.object({
|
|
811
|
-
type: z.literal("
|
|
812
|
-
|
|
813
|
-
}), z.object({ type: z.literal("board") })]),
|
|
1257
|
+
type: z.literal("item"),
|
|
1258
|
+
itemId: idSchema$1
|
|
1259
|
+
}).strict(), z.object({ type: z.literal("board") }).strict()]),
|
|
814
1260
|
kind: extensionIdSchema,
|
|
815
1261
|
kindVersion: z.number().int().positive(),
|
|
816
1262
|
enabled: z.boolean().default(true),
|
|
@@ -835,17 +1281,7 @@ const BoardEffectSchema = z.object({
|
|
|
835
1281
|
metadata: jsonObjectSchema.default({}),
|
|
836
1282
|
revision: z.number().int().nonnegative()
|
|
837
1283
|
});
|
|
838
|
-
|
|
839
|
-
id: idSchema$1,
|
|
840
|
-
boardId: z.string().uuid(),
|
|
841
|
-
name: z.string().min(1).max(255),
|
|
842
|
-
duration: finiteSchema$1.nonnegative(),
|
|
843
|
-
seed: z.string().min(1).max(160),
|
|
844
|
-
restPose: jsonObjectSchema.default({}),
|
|
845
|
-
metadata: jsonObjectSchema.default({}),
|
|
846
|
-
revision: z.number().int().nonnegative()
|
|
847
|
-
});
|
|
848
|
-
const BoardNodeInputSchema = z.object({
|
|
1284
|
+
z.object({
|
|
849
1285
|
nodeId: idSchema$1,
|
|
850
1286
|
type: z.string().min(1).max(40),
|
|
851
1287
|
parentId: idSchema$1.nullable(),
|
|
@@ -868,27 +1304,20 @@ z.object({
|
|
|
868
1304
|
mutationId: z.string().max(128).optional(),
|
|
869
1305
|
title: z.string().min(1).max(255).optional(),
|
|
870
1306
|
metadata: jsonObjectSchema.optional(),
|
|
871
|
-
|
|
1307
|
+
items: z.array(BoardAuthoringItemSchema).max(5e4).optional(),
|
|
872
1308
|
connections: z.array(BoardConnectionSchema).max(5e4).optional(),
|
|
873
1309
|
effects: z.array(BoardEffectSchema.omit({
|
|
874
1310
|
boardId: true,
|
|
875
1311
|
revision: true
|
|
876
1312
|
})).optional(),
|
|
877
|
-
|
|
878
|
-
sequence: BoardSequenceSchema.omit({
|
|
879
|
-
boardId: true,
|
|
880
|
-
revision: true
|
|
881
|
-
}),
|
|
882
|
-
clips: z.array(BoardClipSchema.omit({ sequenceId: true }))
|
|
883
|
-
})).optional()
|
|
1313
|
+
compositions: z.array(BoardCompositionInputSchema).optional()
|
|
884
1314
|
});
|
|
885
1315
|
z.object({
|
|
886
1316
|
include: z.array(z.enum([
|
|
887
1317
|
"nodes",
|
|
888
1318
|
"connections",
|
|
889
1319
|
"effects",
|
|
890
|
-
"
|
|
891
|
-
"clips",
|
|
1320
|
+
"compositions",
|
|
892
1321
|
"playback"
|
|
893
1322
|
])).optional(),
|
|
894
1323
|
viewport: z.object({
|
|
@@ -900,10 +1329,9 @@ z.object({
|
|
|
900
1329
|
});
|
|
901
1330
|
/** Persisted on `boards.metadata.playback`: how a Board plays when opened. */
|
|
902
1331
|
const BoardPlaybackPolicySchema = z.object({
|
|
903
|
-
|
|
1332
|
+
compositionId: idSchema$1,
|
|
904
1333
|
/** Delay before the first local playback after opening the Board, in milliseconds. */
|
|
905
|
-
delayMs: finiteSchema$1.nonnegative().default(0)
|
|
906
|
-
loop: z.boolean().default(false)
|
|
1334
|
+
delayMs: finiteSchema$1.nonnegative().default(0)
|
|
907
1335
|
});
|
|
908
1336
|
function parseBoardPlaybackPolicy(metadata) {
|
|
909
1337
|
const parsed = BoardPlaybackPolicySchema.safeParse(metadata.playback);
|
|
@@ -913,7 +1341,7 @@ z.discriminatedUnion("type", [
|
|
|
913
1341
|
z.object({
|
|
914
1342
|
commandId: idSchema$1,
|
|
915
1343
|
type: z.literal("play"),
|
|
916
|
-
|
|
1344
|
+
compositionId: idSchema$1,
|
|
917
1345
|
position: finiteSchema$1.nonnegative().optional(),
|
|
918
1346
|
timeScale: finiteSchema$1.positive().max(4).optional(),
|
|
919
1347
|
shared: z.boolean().optional(),
|
|
@@ -936,27 +1364,77 @@ z.discriminatedUnion("type", [
|
|
|
936
1364
|
playbackId: z.string().uuid()
|
|
937
1365
|
})
|
|
938
1366
|
]);
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
]
|
|
948
|
-
const
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
})
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1367
|
+
//#endregion
|
|
1368
|
+
//#region ../protocol/dist/board-capability-registry.js
|
|
1369
|
+
const CLIPS = new Set(BOARD_BUILTIN_CLIP_KINDS);
|
|
1370
|
+
new Set(BOARD_BUILTIN_EFFECT_KINDS);
|
|
1371
|
+
const record = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1372
|
+
const finite = (value) => typeof value === "number" && Number.isFinite(value);
|
|
1373
|
+
function validateBuiltinBoardClip(clip, path = "clip") {
|
|
1374
|
+
if (!CLIPS.has(clip.kind)) return [];
|
|
1375
|
+
const errors = [];
|
|
1376
|
+
const error = (message, suffix, coordinateSpace, code = "INVALID_BOARD_CLIP") => {
|
|
1377
|
+
errors.push({
|
|
1378
|
+
severity: "error",
|
|
1379
|
+
code,
|
|
1380
|
+
message,
|
|
1381
|
+
path: `${path}.${suffix}`,
|
|
1382
|
+
...coordinateSpace ? { coordinateSpace } : {}
|
|
1383
|
+
});
|
|
1384
|
+
};
|
|
1385
|
+
if ((clip.kind.startsWith("motion.") || clip.kind.startsWith("draw.") || clip.kind === "text.reveal" || clip.kind === "effects.trail") && clip.target.type !== "item") error(`${clip.kind} must target an item`, "target");
|
|
1386
|
+
if (clip.kind.startsWith("camera.") && clip.target.type !== "camera") error(`${clip.kind} must target the camera`, "target");
|
|
1387
|
+
if (clip.kind === "motion.path") {
|
|
1388
|
+
const points = clip.params.points;
|
|
1389
|
+
if (!Array.isArray(points) || points.length < 2 || points.length > 1e4) error("motion path must contain 2 to 10000 world-offset points", "params.points", "world-offset");
|
|
1390
|
+
else for (const [index, point] of points.entries()) if (!record(point) || !finite(point.x) || !finite(point.y)) error("motion path point must contain finite x and y", `params.points.${index}`, "world-offset");
|
|
1391
|
+
}
|
|
1392
|
+
if (clip.kind === "effects.particles") {
|
|
1393
|
+
const count = clip.params.count;
|
|
1394
|
+
if (!Number.isSafeInteger(count) || count < 1 || count > DEFAULT_BOARD_RENDER_LIMITS.particles) error(`particle count must be an integer from 1 to ${DEFAULT_BOARD_RENDER_LIMITS.particles}`, "params.count", void 0, "INVALID_PARTICLE_COUNT");
|
|
1395
|
+
const bounds = clip.params.bounds;
|
|
1396
|
+
if (!record(bounds) || !finite(bounds.x) || !finite(bounds.y) || !finite(bounds.width) || !finite(bounds.height) || bounds.width <= 0 || bounds.height <= 0) error("particles require positive finite world bounds", "params.bounds", "world", "PARTICLE_BOUNDS_REQUIRED");
|
|
1397
|
+
}
|
|
1398
|
+
if (clip.kind === "camera.focus") {
|
|
1399
|
+
const parsed = BoardCameraFocusParamsSchema.safeParse(clip.params);
|
|
1400
|
+
if (!parsed.success) error(parsed.error.issues[0]?.message ?? "invalid camera focus", "params", "world");
|
|
1401
|
+
}
|
|
1402
|
+
return errors;
|
|
1403
|
+
}
|
|
1404
|
+
function estimateBuiltinBoardClipCost(clip) {
|
|
1405
|
+
switch (clip.kind) {
|
|
1406
|
+
case "effects.particles": {
|
|
1407
|
+
const count = Number.isSafeInteger(clip.params.count) ? Math.max(0, clip.params.count) : 0;
|
|
1408
|
+
return {
|
|
1409
|
+
particles: count,
|
|
1410
|
+
vertices: count * 4,
|
|
1411
|
+
dynamicVertices: count * 4,
|
|
1412
|
+
drawCalls: 1,
|
|
1413
|
+
bufferBytes: count * 48,
|
|
1414
|
+
simulationSteps: count
|
|
1415
|
+
};
|
|
1416
|
+
}
|
|
1417
|
+
case "effects.trail": return {
|
|
1418
|
+
vertices: 32,
|
|
1419
|
+
dynamicVertices: 32,
|
|
1420
|
+
drawCalls: 1,
|
|
1421
|
+
bufferBytes: 1024,
|
|
1422
|
+
simulationSteps: 16
|
|
1423
|
+
};
|
|
1424
|
+
case "effects.impact":
|
|
1425
|
+
case "effects.flash": return {
|
|
1426
|
+
vertices: 64,
|
|
1427
|
+
drawCalls: 1
|
|
1428
|
+
};
|
|
1429
|
+
case "draw.reveal":
|
|
1430
|
+
case "draw.handwrite": return {
|
|
1431
|
+
drawCalls: 1,
|
|
1432
|
+
dynamicVertices: 1
|
|
1433
|
+
};
|
|
1434
|
+
case "motion.path": return { simulationSteps: Array.isArray(clip.params.points) ? clip.params.points.length : 0 };
|
|
1435
|
+
default: return {};
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
960
1438
|
//#endregion
|
|
961
1439
|
//#region ../protocol/dist/board-url.js
|
|
962
1440
|
const BOARD_REMOTE_URL_MAX_LENGTH = 4096;
|
|
@@ -1687,6 +2165,27 @@ function validateBoardNodeInput(node, path = "node") {
|
|
|
1687
2165
|
}
|
|
1688
2166
|
return [];
|
|
1689
2167
|
}
|
|
2168
|
+
const BoardContentKindSchema = z.enum([
|
|
2169
|
+
"text",
|
|
2170
|
+
"image",
|
|
2171
|
+
"video",
|
|
2172
|
+
"audio",
|
|
2173
|
+
"file",
|
|
2174
|
+
"json",
|
|
2175
|
+
"collection"
|
|
2176
|
+
]);
|
|
2177
|
+
const BoardPortSchema = z.object({
|
|
2178
|
+
id: z.string().min(1).max(120),
|
|
2179
|
+
kind: BoardContentKindSchema,
|
|
2180
|
+
role: z.string().min(1).max(80).optional(),
|
|
2181
|
+
required: z.boolean().optional(),
|
|
2182
|
+
multiple: z.boolean().optional(),
|
|
2183
|
+
maxItems: z.number().int().positive().optional()
|
|
2184
|
+
}).strict();
|
|
2185
|
+
z.object({
|
|
2186
|
+
inputs: z.array(BoardPortSchema),
|
|
2187
|
+
outputs: z.array(BoardPortSchema)
|
|
2188
|
+
}).strict();
|
|
1690
2189
|
//#endregion
|
|
1691
2190
|
//#region ../protocol/dist/realtime/board-awareness.js
|
|
1692
2191
|
const idSchema = z.string().min(1).max(160);
|
|
@@ -2147,186 +2646,12 @@ const buildWorkComposerChipClear = (key) => ({
|
|
|
2147
2646
|
key
|
|
2148
2647
|
});
|
|
2149
2648
|
//#endregion
|
|
2150
|
-
//#region src/
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
}
|
|
2156
|
-
/** Axis-aligned bounds of a stroke in its local space, padded by stroke width. */
|
|
2157
|
-
function computeDrawBounds(points, size) {
|
|
2158
|
-
if (points.length === 0) return {
|
|
2159
|
-
x: 0,
|
|
2160
|
-
y: 0,
|
|
2161
|
-
width: 1,
|
|
2162
|
-
height: 1
|
|
2163
|
-
};
|
|
2164
|
-
let minX = Number.POSITIVE_INFINITY;
|
|
2165
|
-
let minY = Number.POSITIVE_INFINITY;
|
|
2166
|
-
let maxX = Number.NEGATIVE_INFINITY;
|
|
2167
|
-
let maxY = Number.NEGATIVE_INFINITY;
|
|
2168
|
-
for (const point of points) {
|
|
2169
|
-
const r = sampleRadius(size, point.p);
|
|
2170
|
-
minX = Math.min(minX, point.x - r);
|
|
2171
|
-
minY = Math.min(minY, point.y - r);
|
|
2172
|
-
maxX = Math.max(maxX, point.x + r);
|
|
2173
|
-
maxY = Math.max(maxY, point.y + r);
|
|
2174
|
-
}
|
|
2175
|
-
return {
|
|
2176
|
-
x: minX,
|
|
2177
|
-
y: minY,
|
|
2178
|
-
width: Math.max(1, maxX - minX),
|
|
2179
|
-
height: Math.max(1, maxY - minY)
|
|
2180
|
-
};
|
|
2181
|
-
}
|
|
2182
|
-
//#endregion
|
|
2183
|
-
//#region src/board/nodes.ts
|
|
2184
|
-
var BoardInputError = class extends Error {
|
|
2185
|
-
code = "INVALID_BOARD_NODE";
|
|
2186
|
-
diagnostics;
|
|
2187
|
-
body;
|
|
2188
|
-
constructor(diagnostics) {
|
|
2189
|
-
const message = diagnostics[0]?.message ?? "Invalid Board node";
|
|
2190
|
-
super(message);
|
|
2191
|
-
this.name = "BoardInputError";
|
|
2192
|
-
this.diagnostics = diagnostics;
|
|
2193
|
-
this.body = {
|
|
2194
|
-
code: this.code,
|
|
2195
|
-
message,
|
|
2196
|
-
diagnostics
|
|
2197
|
-
};
|
|
2198
|
-
}
|
|
2199
|
-
};
|
|
2200
|
-
function baseNode(spec, frame) {
|
|
2201
|
-
return {
|
|
2202
|
-
nodeId: spec.id,
|
|
2203
|
-
type: spec.type,
|
|
2204
|
-
parentId: spec.parentId ?? null,
|
|
2205
|
-
orderKey: spec.orderKey ?? null,
|
|
2206
|
-
x: frame.x,
|
|
2207
|
-
y: frame.y,
|
|
2208
|
-
width: frame.width,
|
|
2209
|
-
height: frame.height,
|
|
2210
|
-
rotation: frame.rotation ?? 0,
|
|
2211
|
-
refKind: null,
|
|
2212
|
-
refPath: null,
|
|
2213
|
-
refUrl: null,
|
|
2214
|
-
view: {},
|
|
2215
|
-
style: spec.style ?? {},
|
|
2216
|
-
data: {}
|
|
2217
|
-
};
|
|
2218
|
-
}
|
|
2219
|
-
function arrowFrame(start, end) {
|
|
2220
|
-
const padding = 16;
|
|
2221
|
-
return {
|
|
2222
|
-
x: Math.min(start.x, end.x) - padding,
|
|
2223
|
-
y: Math.min(start.y, end.y) - padding,
|
|
2224
|
-
width: Math.max(1, Math.abs(end.x - start.x) + padding * 2),
|
|
2225
|
-
height: Math.max(1, Math.abs(end.y - start.y) + padding * 2)
|
|
2226
|
-
};
|
|
2227
|
-
}
|
|
2228
|
-
/**
|
|
2229
|
-
* Create one validated wire node from semantic Board input.
|
|
2230
|
-
*
|
|
2231
|
-
* Box nodes take an explicit world-space frame. Draw samples and arrow endpoints
|
|
2232
|
-
* take world coordinates; the builder derives their frame and local storage form.
|
|
2233
|
-
*/
|
|
2234
|
-
function createBoardNode(spec) {
|
|
2235
|
-
let node;
|
|
2236
|
-
if (spec.type === "draw") {
|
|
2237
|
-
const size = spec.size ?? 4;
|
|
2238
|
-
const worldPoints = spec.points.map((point) => ({
|
|
2239
|
-
x: point.x,
|
|
2240
|
-
y: point.y,
|
|
2241
|
-
p: point.p ?? .5
|
|
2242
|
-
}));
|
|
2243
|
-
const bounds = computeDrawBounds(worldPoints, size);
|
|
2244
|
-
node = baseNode(spec, bounds);
|
|
2245
|
-
node.data = {
|
|
2246
|
-
points: worldPoints.map((point) => ({
|
|
2247
|
-
x: point.x - bounds.x,
|
|
2248
|
-
y: point.y - bounds.y,
|
|
2249
|
-
p: point.p
|
|
2250
|
-
})),
|
|
2251
|
-
color: spec.color ?? "brand",
|
|
2252
|
-
size
|
|
2253
|
-
};
|
|
2254
|
-
} else if (spec.type === "arrow") {
|
|
2255
|
-
node = baseNode(spec, arrowFrame(spec.start, spec.end));
|
|
2256
|
-
node.data = {
|
|
2257
|
-
start: spec.start,
|
|
2258
|
-
end: spec.end,
|
|
2259
|
-
bend: spec.bend ?? 0,
|
|
2260
|
-
color: spec.color ?? "brand",
|
|
2261
|
-
size: spec.size ?? 2.5,
|
|
2262
|
-
arrowStart: spec.arrowStart ?? false,
|
|
2263
|
-
arrowEnd: spec.arrowEnd ?? true,
|
|
2264
|
-
label: spec.label ?? ""
|
|
2265
|
-
};
|
|
2266
|
-
} else {
|
|
2267
|
-
node = baseNode(spec, spec.frame);
|
|
2268
|
-
switch (spec.type) {
|
|
2269
|
-
case "text":
|
|
2270
|
-
node.data = {
|
|
2271
|
-
text: spec.text ?? "",
|
|
2272
|
-
color: spec.color ?? "neutral",
|
|
2273
|
-
fontSize: spec.fontSize ?? 24
|
|
2274
|
-
};
|
|
2275
|
-
break;
|
|
2276
|
-
case "geo":
|
|
2277
|
-
node.data = {
|
|
2278
|
-
geo: spec.geo ?? "rectangle",
|
|
2279
|
-
text: spec.text ?? "",
|
|
2280
|
-
color: spec.color ?? "brand",
|
|
2281
|
-
fillOpacity: spec.fillOpacity ?? 0
|
|
2282
|
-
};
|
|
2283
|
-
break;
|
|
2284
|
-
case "frame":
|
|
2285
|
-
node.data = {
|
|
2286
|
-
label: spec.label ?? "Frame",
|
|
2287
|
-
color: spec.color ?? "neutral"
|
|
2288
|
-
};
|
|
2289
|
-
break;
|
|
2290
|
-
case "image":
|
|
2291
|
-
node.refKind = "space_file";
|
|
2292
|
-
node.refPath = spec.path;
|
|
2293
|
-
node.view = spec.snapshot ?? {};
|
|
2294
|
-
node.data = spec.crop ? { crop: spec.crop } : {};
|
|
2295
|
-
break;
|
|
2296
|
-
case "video":
|
|
2297
|
-
case "audio":
|
|
2298
|
-
node.refKind = "space_file";
|
|
2299
|
-
node.refPath = spec.path;
|
|
2300
|
-
node.view = spec.snapshot ?? {};
|
|
2301
|
-
break;
|
|
2302
|
-
case "file":
|
|
2303
|
-
node.refKind = "space_file";
|
|
2304
|
-
node.refPath = spec.path;
|
|
2305
|
-
node.view = spec.snapshot ?? {};
|
|
2306
|
-
break;
|
|
2307
|
-
case "task":
|
|
2308
|
-
node.view = spec.snapshot;
|
|
2309
|
-
node.data = { taskRunId: spec.taskRunId };
|
|
2310
|
-
break;
|
|
2311
|
-
}
|
|
2312
|
-
}
|
|
2313
|
-
assertBoardNodes([node]);
|
|
2314
|
-
return node;
|
|
2315
|
-
}
|
|
2316
|
-
function validateBoardNodes(nodes, path = "nodes") {
|
|
2317
|
-
return nodes.flatMap((node, index) => validateBoardNodeInput(node, `${path}.${index}`));
|
|
2318
|
-
}
|
|
2319
|
-
function assertBoardNodes(nodes, path = "nodes") {
|
|
2320
|
-
const diagnostics = validateBoardNodes(nodes, path);
|
|
2321
|
-
if (diagnostics.length > 0) throw new BoardInputError(diagnostics);
|
|
2322
|
-
}
|
|
2323
|
-
function assertBoardTransactionNodeCreates(operations) {
|
|
2324
|
-
const diagnostics = operations.flatMap((operation, index) => {
|
|
2325
|
-
if (operation.type !== "node.create") return [];
|
|
2326
|
-
const payload = operation.payload;
|
|
2327
|
-
return payload.node ? validateBoardNodeInput(payload.node, `operations.${index}.payload.node`) : [];
|
|
2649
|
+
//#region src/realtime.ts
|
|
2650
|
+
function ensureRealtimeConnected(websocketClient) {
|
|
2651
|
+
if (websocketClient.state === "open" || websocketClient.state === "connecting" || websocketClient.state === "reconnecting") return;
|
|
2652
|
+
websocketClient.connect().catch((error) => {
|
|
2653
|
+
console.error("[CohubClient] Failed to connect realtime websocket:", error);
|
|
2328
2654
|
});
|
|
2329
|
-
if (diagnostics.length > 0) throw new BoardInputError(diagnostics);
|
|
2330
2655
|
}
|
|
2331
2656
|
//#endregion
|
|
2332
2657
|
//#region src/session-patch-reducer.ts
|
|
@@ -4382,19 +4707,43 @@ var BoardClient = class {
|
|
|
4382
4707
|
summary(customFetch) {
|
|
4383
4708
|
return this.boards.summary(this.id, customFetch);
|
|
4384
4709
|
}
|
|
4710
|
+
authoring(customFetch) {
|
|
4711
|
+
return this.boards.authoring(this.id, customFetch);
|
|
4712
|
+
}
|
|
4713
|
+
mutateSemantic(input) {
|
|
4714
|
+
return this.boards.mutateSemantic(this.id, {
|
|
4715
|
+
...input,
|
|
4716
|
+
mutationId: input.mutationId ?? randomBoardId()
|
|
4717
|
+
});
|
|
4718
|
+
}
|
|
4385
4719
|
async mutate(input) {
|
|
4386
4720
|
const retries = input.retries ?? 1;
|
|
4387
4721
|
if (!Number.isSafeInteger(retries) || retries < 0 || retries > 3) throw new RangeError("Board mutation retries must be an integer from 0 to 3");
|
|
4388
4722
|
for (let attempt = 0;; attempt += 1) {
|
|
4389
4723
|
const current = await this.inspect({ include: input.include ?? [] });
|
|
4390
4724
|
const operations = await input.build(current);
|
|
4391
|
-
if (operations.length === 0) return
|
|
4725
|
+
if (operations.length === 0) return {
|
|
4726
|
+
mutationId: randomBoardId(),
|
|
4727
|
+
status: "validated",
|
|
4728
|
+
replayed: false,
|
|
4729
|
+
board: {
|
|
4730
|
+
id: current.board.id,
|
|
4731
|
+
version: current.board.version
|
|
4732
|
+
},
|
|
4733
|
+
changed: {
|
|
4734
|
+
items: [],
|
|
4735
|
+
connections: [],
|
|
4736
|
+
effects: [],
|
|
4737
|
+
compositions: [],
|
|
4738
|
+
board: false
|
|
4739
|
+
}
|
|
4740
|
+
};
|
|
4392
4741
|
try {
|
|
4393
4742
|
return await this.apply({
|
|
4394
4743
|
txId: randomBoardId(),
|
|
4395
4744
|
baseVersion: current.board.version,
|
|
4396
4745
|
operations
|
|
4397
|
-
}
|
|
4746
|
+
});
|
|
4398
4747
|
} catch (cause) {
|
|
4399
4748
|
if (!(cause instanceof BoardTransactionError) || !cause.isVersionConflict || attempt >= retries) throw cause;
|
|
4400
4749
|
}
|
|
@@ -4406,11 +4755,11 @@ var BoardClient = class {
|
|
|
4406
4755
|
boardId: this.id
|
|
4407
4756
|
});
|
|
4408
4757
|
}
|
|
4409
|
-
apply(transaction
|
|
4758
|
+
apply(transaction) {
|
|
4410
4759
|
return this.boards.apply({
|
|
4411
4760
|
...transaction,
|
|
4412
4761
|
boardId: this.id
|
|
4413
|
-
}
|
|
4762
|
+
});
|
|
4414
4763
|
}
|
|
4415
4764
|
updateAwareness(seq, update) {
|
|
4416
4765
|
if (!this.websocketClient) return Promise.resolve();
|
|
@@ -4540,7 +4889,7 @@ var SpaceBoardsApi = class {
|
|
|
4540
4889
|
return new BoardClient(this.spaceId, boardId, this.transport, this.websocketClient);
|
|
4541
4890
|
}
|
|
4542
4891
|
create(input) {
|
|
4543
|
-
|
|
4892
|
+
for (const item of input.items ?? []) BoardAuthoringItemSchema.parse(item);
|
|
4544
4893
|
return this.transport.request(`/api/spaces/${this.spaceId}/boards`, {
|
|
4545
4894
|
method: "POST",
|
|
4546
4895
|
headers: { "Content-Type": "application/json" },
|
|
@@ -4554,6 +4903,16 @@ var SpaceBoardsApi = class {
|
|
|
4554
4903
|
const query = params.toString();
|
|
4555
4904
|
return this.transport.request(`/api/spaces/${this.spaceId}/boards/${boardId}${query ? `?${query}` : ""}`, { fetch: customFetch });
|
|
4556
4905
|
}
|
|
4906
|
+
authoring(boardId, customFetch) {
|
|
4907
|
+
return this.transport.request(`/api/spaces/${this.spaceId}/boards/${boardId}/authoring`, { fetch: customFetch });
|
|
4908
|
+
}
|
|
4909
|
+
mutateSemantic(boardId, mutation) {
|
|
4910
|
+
return this.transport.request(`/api/spaces/${this.spaceId}/boards/${boardId}/mutations`, {
|
|
4911
|
+
method: "POST",
|
|
4912
|
+
headers: { "Content-Type": "application/json" },
|
|
4913
|
+
body: JSON.stringify(mutation)
|
|
4914
|
+
});
|
|
4915
|
+
}
|
|
4557
4916
|
summary(boardId, customFetch) {
|
|
4558
4917
|
return this.transport.request(`/api/spaces/${this.spaceId}/boards/${boardId}/summary`, { fetch: customFetch });
|
|
4559
4918
|
}
|
|
@@ -4567,10 +4926,9 @@ var SpaceBoardsApi = class {
|
|
|
4567
4926
|
body: JSON.stringify(transaction)
|
|
4568
4927
|
});
|
|
4569
4928
|
}
|
|
4570
|
-
async apply(transaction
|
|
4571
|
-
assertBoardTransactionNodeCreates(transaction.operations);
|
|
4929
|
+
async apply(transaction) {
|
|
4572
4930
|
try {
|
|
4573
|
-
return await this.transport.request(`/api/spaces/${this.spaceId}/boards/${transaction.boardId}/transactions
|
|
4931
|
+
return await this.transport.request(`/api/spaces/${this.spaceId}/boards/${transaction.boardId}/transactions`, {
|
|
4574
4932
|
method: "POST",
|
|
4575
4933
|
headers: { "Content-Type": "application/json" },
|
|
4576
4934
|
body: JSON.stringify(transaction)
|
|
@@ -5359,4 +5717,4 @@ var CohubHttpClient = class {
|
|
|
5359
5717
|
};
|
|
5360
5718
|
const createHttpClient = (options) => new CohubHttpClient(options);
|
|
5361
5719
|
//#endregion
|
|
5362
|
-
export {
|
|
5720
|
+
export { BOARD_GEO_KINDS as $, buildWorkComposerChipSet as A, UI_COMMAND_MAX_TIMEOUT_MS as B, ensureRealtimeConnected as C, SkillsApi as Ct, WORK_SURFACE_READY_TIMEOUT_MS as D, CronJobsApi as Dt, WORK_COMPOSER_CHIP_LABEL_MAX_LENGTH as E, GenerationsApi as Et, parseWorkComposerChipSet as F, UI_COMMAND_VERSION as G, UI_COMMAND_PENDING_TTL_SECONDS as H, parseWorkSurfaceReady as I, parseUiCommand as J, isTerminalUiCommandStatus as K, parseWorkSurfaceRequest as L, buildWorkSurfaceRequest as M, buildWorkSurfaceResponse as N, WORK_SURFACE_REQUEST_TIMEOUT_MS as O, ChannelsApi as Ot, parseWorkComposerChipClear as P, BOARD_COLOR_IDS as Q, parseWorkSurfaceResponse as R, createSessionPatchReducer as S, PublicAssetsApi as St, WORK_COMPOSER_CHIP_KEY_MAX_LENGTH as T, ModelsApi as Tt, UI_COMMAND_SETTLEMENT_GRACE_SECONDS as U, UI_COMMAND_PAYLOAD_MAX_BYTES as V, UI_COMMAND_TERMINAL_TTL_SECONDS as W, parseUsername as X, parseSpaceSlug as Y, BoardAwarenessClientPayloadSchema as Z, buildSpacePath as _, BOARD_BUILTIN_CAPABILITIES as _t, ReferralsApi as a, BoardEffectSchema as at, parseAssistantMessageCommit as b, ReferencesApi as bt, UiCommandsApi as c, BoardAuthoringItemSchema as ct, BoardTransactionError as d, BOARD_ANIMATION_CHANNEL_CAPABILITIES as dt, BOARD_NATIVE_NODE_TYPES as et, SpaceClient as f, BoardCompositionInputSchema as ft, buildSpaceInvitePath as g, parseBoardCompositionInput as gt, PublicInviteApi as h, BoardTrackSchema as ht, WorksApi as i, validateBuiltinBoardClip as it, buildWorkSurfaceReady as j, buildWorkComposerChipClear as k, TasksApi as l, BoardItemPatchSchema as lt, SpacesApi as m, BoardProceduralClipSchema as mt, createHttpClient as n, validateBoardNodeInput as nt, UsersApi as o, BoardPlaybackPolicySchema as ot, SpacePublicFilesApi as p, BoardCompositionSchema as pt, isUiSurfaceMethod as q, WorkCommerceApi as r, estimateBuiltinBoardClipCost as rt, UserApi as s, parseBoardPlaybackPolicy as st, CohubHttpClient as t, BOARD_NODE_CONTRACT as tt, BoardClient as u, BOARD_ANIMATION_CHANNELS as ut, SessionGenerationStreamClient as v, DEFAULT_BOARD_RENDER_LIMITS as vt, WORK_COMPOSER_CHIP_CONTENT_MAX_BYTES as w, PromptsApi as wt, SessionPatchReducer as x, SearchApi as xt, createSessionGenerationStreamClient as y, SessionAccessApi as yt, UI_COMMAND_DEFAULT_TIMEOUT_MS as z };
|