@volter/editor-sdk 0.5.57
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/LICENSE +202 -0
- package/NOTICE +8 -0
- package/README.md +19 -0
- package/package.json +90 -0
- package/src/account.ts +210 -0
- package/src/chrome.ts +83 -0
- package/src/client.ts +1547 -0
- package/src/commands.ts +66 -0
- package/src/contributions.ts +985 -0
- package/src/document-probe.ts +237 -0
- package/src/editor-view.ts +220 -0
- package/src/extension.ts +40 -0
- package/src/generations.ts +178 -0
- package/src/host.ts +1167 -0
- package/src/http-transport.browser.ts +14 -0
- package/src/http-transport.node.ts +19 -0
- package/src/index.ts +128 -0
- package/src/layout-arrangements.ts +5 -0
- package/src/layouts.tsx +108 -0
- package/src/looks.ts +14 -0
- package/src/project/output-roots.ts +73 -0
- package/src/project/tab-census.ts +149 -0
- package/src/project-tool-catalog.ts +96 -0
- package/src/selection.tsx +108 -0
- package/src/services.ts +18 -0
- package/src/session/build-report.ts +19 -0
- package/src/session/collaboration-types.ts +262 -0
- package/src/session/command-table.ts +333 -0
- package/src/session/discovery.ts +90 -0
- package/src/session/editor-brand.ts +73 -0
- package/src/session/editor-compatibility.ts +248 -0
- package/src/session/editor-control-lifecycle.ts +68 -0
- package/src/session/editor-control-protocol.ts +5 -0
- package/src/session/entrypoint-selection-readers.ts +66 -0
- package/src/session/entrypoint-selection-source.ts +120 -0
- package/src/session/game-css-scope.ts +30 -0
- package/src/session/product-create.ts +24 -0
- package/src/session/product-locator.ts +389 -0
- package/src/session/project-module-url.ts +245 -0
- package/src/session/registry-format.ts +203 -0
- package/src/session/relative-path-guard.ts +56 -0
- package/src/session/source-glob.ts +15 -0
- package/src/session/tool-contribution-convention.ts +116 -0
- package/src/session/workbench-locator.ts +650 -0
- package/src/session.ts +41 -0
- package/src/share.ts +160 -0
- package/src/tools/errors.ts +91 -0
- package/src/tools/provider-execution.ts +70 -0
- package/src/tools/registry.ts +341 -0
- package/src/tools/types.ts +159 -0
- package/src/transport.ts +97 -0
- package/src/types.ts +1581 -0
- package/src/views.ts +164 -0
- package/src/widgets/design-system.ts +93 -0
- package/src/widgets/editor-appearance.ts +149 -0
- package/src/widgets/editor-material.ts +83 -0
- package/src/widgets/icon-set-registry.ts +105 -0
- package/src/widgets/index.ts +71 -0
- package/src/widgets/inspector-widgets/AlignmentGrid.tsx +182 -0
- package/src/widgets/inspector-widgets/AssetSlotPicker.tsx +123 -0
- package/src/widgets/inspector-widgets/BorderEditor.tsx +309 -0
- package/src/widgets/inspector-widgets/ColorPicker.tsx +549 -0
- package/src/widgets/inspector-widgets/CurveEditor.tsx +359 -0
- package/src/widgets/inspector-widgets/FilterEditor.tsx +108 -0
- package/src/widgets/inspector-widgets/FontPicker.tsx +191 -0
- package/src/widgets/inspector-widgets/GradientEditor.tsx +623 -0
- package/src/widgets/inspector-widgets/ScrubbableInput.tsx +180 -0
- package/src/widgets/inspector-widgets/ShadowEditor.tsx +319 -0
- package/src/widgets/inspector-widgets/color-utils.ts +201 -0
- package/src/widgets/inspector-widgets/curve-utils.ts +212 -0
- package/src/widgets/inspector-widgets/index.ts +24 -0
- package/src/widgets/inspector-widgets/shared.tsx +140 -0
- package/src/widgets/interactive-edit-scope.ts +33 -0
- package/src/widgets/patterns/Dialog.tsx +129 -0
- package/src/widgets/patterns/Fields.tsx +44 -0
- package/src/widgets/patterns/List.tsx +25 -0
- package/src/widgets/patterns/StateSurface.tsx +40 -0
- package/src/widgets/patterns/Surfaces.tsx +122 -0
- package/src/widgets/patterns/Tabs.tsx +80 -0
- package/src/widgets/patterns/Toolbar.tsx +72 -0
- package/src/widgets/patterns/Tree.tsx +72 -0
- package/src/widgets/primitives/AnchoredMenu.tsx +260 -0
- package/src/widgets/primitives/Button.tsx +62 -0
- package/src/widgets/primitives/ColorInput.tsx +78 -0
- package/src/widgets/primitives/DraftTextInput.tsx +63 -0
- package/src/widgets/primitives/EditorIcon.tsx +157 -0
- package/src/widgets/primitives/FormControls.tsx +88 -0
- package/src/widgets/primitives/HoverPreview.tsx +96 -0
- package/src/widgets/primitives/JsonInput.tsx +113 -0
- package/src/widgets/primitives/Layout.tsx +100 -0
- package/src/widgets/primitives/Menu.tsx +140 -0
- package/src/widgets/primitives/NumberInput.tsx +169 -0
- package/src/widgets/primitives/Panel.tsx +80 -0
- package/src/widgets/primitives/SectionHeader.tsx +77 -0
- package/src/widgets/primitives/Text.tsx +54 -0
- package/src/widgets/primitives/ThemeRootPortal.tsx +52 -0
- package/src/widgets/primitives/Tooltip.tsx +204 -0
- package/src/widgets/primitives/Vec3Input.tsx +70 -0
- package/src/widgets/primitives/banner-tones.ts +32 -0
- package/src/widgets/primitives/clamp-to-viewport.ts +44 -0
- package/src/widgets/primitives/editor-icons.ts +245 -0
- package/src/widgets/primitives/panel-header-styles.ts +42 -0
- package/src/widgets/theme.ts +2633 -0
- package/src/widgets/z-index.ts +25 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* W3.1 — `useSelection()`, the third tool-hooks contract member (alongside
|
|
3
|
+
* `useData`/`writeData` from `use-data.ts`). Where those two hooks are about
|
|
4
|
+
* DATA ASSETS, this one is about the currently-selected ENTITY.
|
|
5
|
+
*
|
|
6
|
+
* Scope, v1:
|
|
7
|
+
* - INSPECTOR-placement tools only (`placement: 'inspector'`,
|
|
8
|
+
* `define-tool.ts`). `EditorSelectionProvider` is host plumbing —
|
|
9
|
+
* `InspectorToolSection` wraps every tool render in it; dock tools
|
|
10
|
+
* (`ToolHost`/`BottomPanel`) have no adapter/selection in scope and never
|
|
11
|
+
* get a provider. Calling `useSelection()` there throws a teaching error,
|
|
12
|
+
* contained by the tool's `ToolErrorBoundary` (the editor survives).
|
|
13
|
+
* - READ-only. Write through `InspectorProvider.set` / `writeData` (the
|
|
14
|
+
* existing paths), never by mutating what this hook returns. Per-entity
|
|
15
|
+
* WRITE-from-tool is deferred with `useTunable` (§10).
|
|
16
|
+
*
|
|
17
|
+
* Liveness — two existing paths reused, no new mechanism (§4's "reuse that
|
|
18
|
+
* path" rule):
|
|
19
|
+
* 1. Selection changes: `Inspector.tsx`'s own
|
|
20
|
+
* `useSyncExternalStore(store.subscribe, store.getSnapshot)` re-renders
|
|
21
|
+
* the panel, each matched `Section` gets the new `nodeId` prop, and
|
|
22
|
+
* `InspectorToolSection` re-creates this provider's context value.
|
|
23
|
+
* 2. Edits while the SAME node stays selected: `adapter.subscribe` fires —
|
|
24
|
+
* this hook keeps a local version counter bumped per notification
|
|
25
|
+
* (exactly `game-state.tsx`'s `useRootObservation` pattern: the adapter
|
|
26
|
+
* has no snapshot/version of its own, `subscribe` is fire-only) and
|
|
27
|
+
* recomputes.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import type { AuthoringAdapter, EditorNode } from '@volter/editor-project/adapter';
|
|
31
|
+
import {
|
|
32
|
+
createContext,
|
|
33
|
+
type ReactNode,
|
|
34
|
+
useContext,
|
|
35
|
+
useMemo,
|
|
36
|
+
useRef,
|
|
37
|
+
useSyncExternalStore,
|
|
38
|
+
} from 'react';
|
|
39
|
+
|
|
40
|
+
interface SelectionContextValue {
|
|
41
|
+
adapter: AuthoringAdapter;
|
|
42
|
+
nodeId: string | null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const SelectionContext = createContext<SelectionContextValue | null>(null);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* EDITOR-side host plumbing: `InspectorToolSection` wraps a tool's rendered
|
|
49
|
+
* `<Component/>` in this, passing the adapter + nodeId it already holds.
|
|
50
|
+
* Project tools never render this themselves — it exists so `useSelection()`
|
|
51
|
+
* has something to read.
|
|
52
|
+
*/
|
|
53
|
+
export function EditorSelectionProvider({
|
|
54
|
+
adapter,
|
|
55
|
+
nodeId,
|
|
56
|
+
children,
|
|
57
|
+
}: {
|
|
58
|
+
adapter: AuthoringAdapter;
|
|
59
|
+
nodeId: string | null;
|
|
60
|
+
children: ReactNode;
|
|
61
|
+
}) {
|
|
62
|
+
const value = useMemo(() => ({ adapter, nodeId }), [adapter, nodeId]);
|
|
63
|
+
return <SelectionContext.Provider value={value}>{children}</SelectionContext.Provider>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface EditorSelection {
|
|
67
|
+
nodeId: string | null;
|
|
68
|
+
node: EditorNode | null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The selected entity, live (read-only — see module doc).
|
|
73
|
+
*
|
|
74
|
+
* Throws outside an `EditorSelectionProvider` — inspector tools always render
|
|
75
|
+
* inside one; a dock tool or game-code caller does not, and the error names
|
|
76
|
+
* the fix rather than failing silently.
|
|
77
|
+
*/
|
|
78
|
+
export function useSelection(): EditorSelection {
|
|
79
|
+
const ctx = useContext(SelectionContext);
|
|
80
|
+
if (!ctx) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
'useSelection: no editor selection in context — this hook only works inside an editor-hosted ' +
|
|
83
|
+
"INSPECTOR tool (placement: 'inspector'). Dock tools and " +
|
|
84
|
+
'game code have no selection to read.',
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
const { adapter, nodeId } = ctx;
|
|
88
|
+
|
|
89
|
+
// Local version counter bumped per adapter notification — the adapter has
|
|
90
|
+
// no snapshot/version of its own, `subscribe` is fire-only (same pattern
|
|
91
|
+
// as `useRootObservation`'s `versionRef`, game-state.tsx).
|
|
92
|
+
const versionRef = useRef(0);
|
|
93
|
+
const version = useSyncExternalStore(
|
|
94
|
+
(onStoreChange) =>
|
|
95
|
+
adapter.subscribe?.(() => {
|
|
96
|
+
versionRef.current++;
|
|
97
|
+
onStoreChange();
|
|
98
|
+
}) ?? (() => {}),
|
|
99
|
+
() => versionRef.current,
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
return useMemo(() => {
|
|
103
|
+
const node = nodeId ? adapter.hierarchy.node(nodeId) : null;
|
|
104
|
+
return { nodeId, node };
|
|
105
|
+
// `version` has no value of its own — only its CHANGE matters, as the
|
|
106
|
+
// liveness signal described above.
|
|
107
|
+
}, [adapter, nodeId, version]);
|
|
108
|
+
}
|
package/src/services.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE SERVICE POINT (page side) — code a package RUNS for as long as its
|
|
3
|
+
* contributions are loaded (WORKBENCH.md §Contribution points, `service`):
|
|
4
|
+
* a page-wide guard, a worker, a listener. The module declares the point and
|
|
5
|
+
* exports `start`, which returns the stop; the host starts every service
|
|
6
|
+
* after a contribution pass and stops it before the next.
|
|
7
|
+
*
|
|
8
|
+
* `audio-unlock.service.ts`
|
|
9
|
+
* export const point = 'workspace.service';
|
|
10
|
+
* export function start(): () => void { … }
|
|
11
|
+
*
|
|
12
|
+
* A service that must run BEFORE a project opens does not belong here — it
|
|
13
|
+
* is the host's boot. Everything that only matters once a project's own
|
|
14
|
+
* packages are known does.
|
|
15
|
+
*/
|
|
16
|
+
export interface ServiceContribution {
|
|
17
|
+
start(): (() => void) | undefined | void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transient facts about one native project build. The report crosses the
|
|
3
|
+
* editor server's existing export SSE response; it is not persisted as a
|
|
4
|
+
* project document or sidecar. Paths are relative to the project's `dist/`.
|
|
5
|
+
*/
|
|
6
|
+
export const WEB_BUILD_ARTIFACT = 'vgai-web.zip';
|
|
7
|
+
|
|
8
|
+
export interface BuildReportFile {
|
|
9
|
+
readonly path: string;
|
|
10
|
+
readonly bytes: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface BuildReport {
|
|
14
|
+
readonly artifact: string;
|
|
15
|
+
readonly artifactBytes: number;
|
|
16
|
+
readonly outputBytes: number;
|
|
17
|
+
readonly fileCount: number;
|
|
18
|
+
readonly largestFiles: readonly BuildReportFile[];
|
|
19
|
+
}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import type { ShareRole } from '../share';
|
|
2
|
+
|
|
3
|
+
export type CollaborationCapability =
|
|
4
|
+
| 'view'
|
|
5
|
+
| 'comment'
|
|
6
|
+
| 'test'
|
|
7
|
+
| 'edit'
|
|
8
|
+
| 'terminal'
|
|
9
|
+
| 'maintain';
|
|
10
|
+
/**
|
|
11
|
+
* A collaboration role is a {@link ShareRole} PLUS the local-only `maintainer`,
|
|
12
|
+
* and it is spelled that way so the relationship is CHECKED. Written as its own
|
|
13
|
+
* six-member union — which is what it was — the two lists drift with no
|
|
14
|
+
* compiler signal: a role added to the share wire is simply a role this session
|
|
15
|
+
* cannot hold, and the only symptom is a participant the UI refuses to promote.
|
|
16
|
+
*/
|
|
17
|
+
export type CollaborationRole = ShareRole | 'maintainer';
|
|
18
|
+
/**
|
|
19
|
+
* Least authority first; `maintainer` is local-only and has no share role.
|
|
20
|
+
*
|
|
21
|
+
* Declared as a `satisfies Record<CollaborationRole, …>` rather than an array
|
|
22
|
+
* annotated `readonly CollaborationRole[]`, because the array form accepts an
|
|
23
|
+
* INCOMPLETE list: adding a role to `CollaborationRole` and forgetting it here
|
|
24
|
+
* compiles, and the only symptom is that `assignableCollaborationRoles` quietly
|
|
25
|
+
* offers a participant one fewer role than they can hold. A missing key here is
|
|
26
|
+
* a compile error.
|
|
27
|
+
*/
|
|
28
|
+
const COLLABORATION_ROLE_RANK = {
|
|
29
|
+
viewer: 0,
|
|
30
|
+
commenter: 1,
|
|
31
|
+
tester: 2,
|
|
32
|
+
editor: 3,
|
|
33
|
+
terminal: 4,
|
|
34
|
+
maintainer: 5,
|
|
35
|
+
} as const satisfies Record<CollaborationRole, number>;
|
|
36
|
+
|
|
37
|
+
export const COLLABORATION_ROLE_ORDER: readonly CollaborationRole[] = (
|
|
38
|
+
Object.keys(COLLABORATION_ROLE_RANK) as CollaborationRole[]
|
|
39
|
+
).sort((a, b) => COLLABORATION_ROLE_RANK[a] - COLLABORATION_ROLE_RANK[b]);
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Which roles a UI may OFFER for one participant. Enforcement is the server's,
|
|
43
|
+
* always; this only keeps the editor from presenting an option the session
|
|
44
|
+
* would refuse. A participant that entered through the share gateway carries a
|
|
45
|
+
* verified `account` and can never reach `maintainer`, nor exceed the ceiling
|
|
46
|
+
* its invitation set.
|
|
47
|
+
*/
|
|
48
|
+
export function assignableCollaborationRoles(
|
|
49
|
+
participant: { account: { id: string } | null },
|
|
50
|
+
ceiling?: CollaborationRole,
|
|
51
|
+
): CollaborationRole[] {
|
|
52
|
+
if (!participant.account) return [...COLLABORATION_ROLE_ORDER];
|
|
53
|
+
const remoteLimit = COLLABORATION_ROLE_ORDER.indexOf('terminal');
|
|
54
|
+
const ceilingIndex = ceiling ? COLLABORATION_ROLE_ORDER.indexOf(ceiling) : remoteLimit;
|
|
55
|
+
const limit = Math.min(remoteLimit, ceilingIndex < 0 ? remoteLimit : ceilingIndex);
|
|
56
|
+
return COLLABORATION_ROLE_ORDER.slice(0, limit + 1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type ParticipantKind = 'human' | 'agent';
|
|
60
|
+
export type ParticipantStatus = 'active' | 'idle' | 'needs-input' | 'done';
|
|
61
|
+
|
|
62
|
+
export interface CollaborationParticipantLocation {
|
|
63
|
+
repositoryId: string;
|
|
64
|
+
worktreeId: string;
|
|
65
|
+
sessionId: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface CollaborationAgentIdentity {
|
|
69
|
+
harness: string;
|
|
70
|
+
conversationId: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface CollaborationCameraPose {
|
|
74
|
+
x: number;
|
|
75
|
+
y: number;
|
|
76
|
+
z: number;
|
|
77
|
+
targetX: number;
|
|
78
|
+
targetY: number;
|
|
79
|
+
targetZ: number;
|
|
80
|
+
fov: number;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function isCollaborationCameraPose(value: unknown): value is CollaborationCameraPose {
|
|
84
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
85
|
+
const camera = value as Record<string, unknown>;
|
|
86
|
+
const keys = ['x', 'y', 'z', 'targetX', 'targetY', 'targetZ', 'fov'] as const;
|
|
87
|
+
return (
|
|
88
|
+
Object.keys(camera).length === keys.length &&
|
|
89
|
+
keys.every((key) => typeof camera[key] === 'number' && Number.isFinite(camera[key])) &&
|
|
90
|
+
(camera['fov'] as number) > 0 &&
|
|
91
|
+
(camera['fov'] as number) < 180
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* THE owner of team-message size limits, read by the HTTP route guard and by
|
|
97
|
+
* `CollaborationSession`'s own methods alike.
|
|
98
|
+
*
|
|
99
|
+
* The two used to carry separate numbers and disagree in both directions — the
|
|
100
|
+
* route accepted 20k of text the session then refused, and refused 50
|
|
101
|
+
* references the session would have taken — so which error a caller saw
|
|
102
|
+
* depended on which layer happened to see the message first. Each value here is
|
|
103
|
+
* the tighter of the pair that split produced.
|
|
104
|
+
*/
|
|
105
|
+
export const TEAM_MESSAGE_LIMITS = {
|
|
106
|
+
text: 10_000,
|
|
107
|
+
mentions: 16,
|
|
108
|
+
mentionLength: 80,
|
|
109
|
+
references: 32,
|
|
110
|
+
} as const;
|
|
111
|
+
|
|
112
|
+
export const COLLABORATION_AGENT_MENTIONS = ['agent', 'claude', 'codex'] as const;
|
|
113
|
+
|
|
114
|
+
export function isCollaborationAgentMention(value: string): boolean {
|
|
115
|
+
return (COLLABORATION_AGENT_MENTIONS as readonly string[]).includes(value.toLowerCase());
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface CollaborationPresence {
|
|
119
|
+
document: string | null;
|
|
120
|
+
file: string | null;
|
|
121
|
+
oid: string | null;
|
|
122
|
+
selection: readonly string[];
|
|
123
|
+
camera: Readonly<CollaborationCameraPose> | null;
|
|
124
|
+
gesture: Readonly<Record<string, unknown>> | null;
|
|
125
|
+
playtest?: {
|
|
126
|
+
id: string;
|
|
127
|
+
play: 'starting' | 'ready' | 'error';
|
|
128
|
+
networking: 'not-declared' | 'connecting' | 'ready' | 'error';
|
|
129
|
+
roomName: string | null;
|
|
130
|
+
problem: string | null;
|
|
131
|
+
} | null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface CollaborationParticipant {
|
|
135
|
+
participantId: string;
|
|
136
|
+
/** Verified VGAI account authority. Null only for local-only host/agent
|
|
137
|
+
* participants that did not enter through a public share gateway. */
|
|
138
|
+
account: { id: string; email: string; name?: string } | null;
|
|
139
|
+
displayName: string;
|
|
140
|
+
kind: ParticipantKind;
|
|
141
|
+
color: string;
|
|
142
|
+
status: ParticipantStatus;
|
|
143
|
+
location: CollaborationParticipantLocation | null;
|
|
144
|
+
agent: CollaborationAgentIdentity | null;
|
|
145
|
+
role: CollaborationRole;
|
|
146
|
+
capabilities: readonly CollaborationCapability[];
|
|
147
|
+
connectedAt: string;
|
|
148
|
+
lastSeenAt: string;
|
|
149
|
+
presence: CollaborationPresence;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface CollaborationAuthorProjection {
|
|
153
|
+
participantId: string;
|
|
154
|
+
account: { id: string; email: string; name?: string } | null;
|
|
155
|
+
displayName: string;
|
|
156
|
+
kind: ParticipantKind;
|
|
157
|
+
color: string;
|
|
158
|
+
location: CollaborationParticipantLocation | null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const PARTICIPANT_COLORS = [
|
|
162
|
+
'#4f8cff',
|
|
163
|
+
'#e45c9d',
|
|
164
|
+
'#20a87a',
|
|
165
|
+
'#a46de8',
|
|
166
|
+
'#d9782d',
|
|
167
|
+
'#2f9eb8',
|
|
168
|
+
'#ca4f55',
|
|
169
|
+
'#6f9f32',
|
|
170
|
+
] as const;
|
|
171
|
+
|
|
172
|
+
export function collaborationParticipantColor(identity: string): string {
|
|
173
|
+
let hash = 2_166_136_261;
|
|
174
|
+
for (const codePoint of identity) {
|
|
175
|
+
hash ^= codePoint.codePointAt(0) ?? 0;
|
|
176
|
+
hash = Math.imul(hash, 16_777_619);
|
|
177
|
+
}
|
|
178
|
+
return PARTICIPANT_COLORS[(hash >>> 0) % PARTICIPANT_COLORS.length]!;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export type TeamMessageReference =
|
|
182
|
+
| { type: 'worktree'; worktreeId: string }
|
|
183
|
+
| { type: 'revision'; revision: number }
|
|
184
|
+
| { type: 'file'; path: string; revision: number }
|
|
185
|
+
| { type: 'oid'; oid: string; revision: number }
|
|
186
|
+
| { type: 'asset'; path: string; revision: number }
|
|
187
|
+
| { type: 'screenshot'; id: string; revision: number }
|
|
188
|
+
| { type: 'playtest'; id: string; revision: number }
|
|
189
|
+
| { type: 'diff'; fromRevision: number; toRevision: number };
|
|
190
|
+
|
|
191
|
+
export interface TeamMessage {
|
|
192
|
+
id: string;
|
|
193
|
+
authorId: string;
|
|
194
|
+
author: CollaborationAuthorProjection;
|
|
195
|
+
text: string;
|
|
196
|
+
mentions: readonly string[];
|
|
197
|
+
references: readonly TeamMessageReference[];
|
|
198
|
+
threadId: string | null;
|
|
199
|
+
editedAt: string | null;
|
|
200
|
+
deletedAt: string | null;
|
|
201
|
+
reactions: Readonly<Record<string, readonly string[]>>;
|
|
202
|
+
revision: number;
|
|
203
|
+
createdAt: string;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export interface SourceRevision {
|
|
207
|
+
revision: number;
|
|
208
|
+
authorId: string;
|
|
209
|
+
author: CollaborationAuthorProjection;
|
|
210
|
+
source: 'editor' | 'filesystem';
|
|
211
|
+
resources: readonly { path: string; sha: string | null }[];
|
|
212
|
+
createdAt: string;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export interface CollaborationAuditEvent {
|
|
216
|
+
id: string;
|
|
217
|
+
type: 'join' | 'leave' | 'role' | 'revision' | 'message' | 'agent' | 'playtest' | 'git';
|
|
218
|
+
participantId: string;
|
|
219
|
+
createdAt: string;
|
|
220
|
+
detail: Readonly<Record<string, unknown>>;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export interface TeamPlaytest {
|
|
224
|
+
id: string;
|
|
225
|
+
revision: number;
|
|
226
|
+
seed: number;
|
|
227
|
+
roomKey: string;
|
|
228
|
+
startedBy: string;
|
|
229
|
+
startedAt: string;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export interface CollaborationSnapshot {
|
|
233
|
+
sequence: number;
|
|
234
|
+
revision: number;
|
|
235
|
+
activePlaytest: TeamPlaytest | null;
|
|
236
|
+
participants: readonly CollaborationParticipant[];
|
|
237
|
+
messages: readonly TeamMessage[];
|
|
238
|
+
revisions: readonly SourceRevision[];
|
|
239
|
+
audit: readonly CollaborationAuditEvent[];
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export type CollaborationEventChannel =
|
|
243
|
+
| 'participants'
|
|
244
|
+
| 'presence'
|
|
245
|
+
| 'messages'
|
|
246
|
+
| 'revisions'
|
|
247
|
+
| 'audit'
|
|
248
|
+
| 'team-test';
|
|
249
|
+
|
|
250
|
+
export interface CollaborationEvent {
|
|
251
|
+
sequence: number;
|
|
252
|
+
channel: CollaborationEventChannel;
|
|
253
|
+
createdAt: string;
|
|
254
|
+
payload: unknown;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export interface CollaborationResume {
|
|
258
|
+
cursor: number;
|
|
259
|
+
reset: boolean;
|
|
260
|
+
events: readonly CollaborationEvent[];
|
|
261
|
+
snapshot?: CollaborationSnapshot;
|
|
262
|
+
}
|