@hraness/slopcamera 3.2.7 → 3.2.8

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.
Files changed (67) hide show
  1. package/NOTICE.md +1 -7
  2. package/PRIVACY.md +16 -7
  3. package/README.md +37 -12
  4. package/apps/desktop/README.md +14 -130
  5. package/apps/desktop/application/context.ts +0 -9
  6. package/apps/desktop/application/default-registry.ts +0 -10
  7. package/apps/desktop/application/operation.ts +1 -5
  8. package/apps/desktop/application/operations/index.ts +0 -1
  9. package/apps/desktop/cli/args.ts +14 -86
  10. package/apps/desktop/cli/command-host-resources.ts +2 -1
  11. package/apps/desktop/cli/commands.ts +28 -67
  12. package/apps/desktop/cli/help.ts +12 -18
  13. package/apps/desktop/cli/html-scene.ts +139 -28
  14. package/apps/desktop/cli/io.ts +2 -1
  15. package/apps/desktop/cli/main.ts +23 -47
  16. package/apps/desktop/cli/menubar.ts +244 -0
  17. package/apps/desktop/cli/music-analysis-service.ts +61 -0
  18. package/apps/desktop/cli/portable-surface.ts +12 -4
  19. package/apps/desktop/code/application-node-planner.ts +0 -47
  20. package/apps/desktop/code/host-source-layout.ts +46 -0
  21. package/apps/desktop/code/public.ts +0 -1
  22. package/apps/desktop/code/runtime-identity.ts +50 -8
  23. package/apps/desktop/code/semantic-builder.ts +0 -43
  24. package/apps/desktop/code/source-bundle.ts +23 -6
  25. package/apps/desktop/code/source-typecheck.ts +131 -43
  26. package/apps/desktop/code/worker-client.ts +14 -2
  27. package/apps/desktop/core/music-analysis.ts +124 -1
  28. package/apps/desktop/dist/cli/main.js +244 -250
  29. package/apps/desktop/html-overlay/audio-reactivity.ts +127 -0
  30. package/apps/desktop/html-overlay/index.ts +9 -0
  31. package/apps/desktop/html-overlay/runtime.ts +2 -0
  32. package/apps/desktop/html-overlay/scene.ts +29 -0
  33. package/dist/cli.js +83 -81
  34. package/dist/{index-rvghzt7b.js → index-7131pg1c.js} +43 -236
  35. package/dist/{index-mcy8z0br.js → index-fava6pge.js} +1 -1
  36. package/dist/{index-z7239b4h.js → index-h1k0fnjq.js} +1 -1
  37. package/dist/{index-p63wavx0.js → index-zfnddgay.js} +1 -1
  38. package/dist/index.js +4 -18
  39. package/dist/operations.js +2 -2
  40. package/dist/vectorize/worker.js +1 -1
  41. package/dist/workflow.js +3 -3
  42. package/docs/README.md +1 -1
  43. package/docs/studio.md +1 -1
  44. package/package.json +6 -21
  45. package/skills/slopcamera/SKILL.md +8 -0
  46. package/skills/slopcamera/references/diagrams.md +5 -14
  47. package/skills/slopcamera/references/install.md +1 -1
  48. package/skills/slopcamera/references/music-video.md +23 -3
  49. package/skills/slopcamera/references/reference-led-3d.md +1 -1
  50. package/skills/slopcamera/references/support.md +25 -0
  51. package/skills/slopcamera/references/web-media-excerpts.md +120 -0
  52. package/src/cli.ts +28 -98
  53. package/src/index.ts +0 -23
  54. package/src/support-completion.ts +16 -0
  55. package/src/support.ts +42 -0
  56. package/src/version.ts +1 -1
  57. package/apps/desktop/application/operations/recording/index.ts +0 -5
  58. package/apps/desktop/application/operations/recording/pause.ts +0 -34
  59. package/apps/desktop/application/operations/recording/resume.ts +0 -34
  60. package/apps/desktop/application/operations/recording/shared.ts +0 -199
  61. package/apps/desktop/application/operations/recording/start.ts +0 -169
  62. package/apps/desktop/application/operations/recording/stop.ts +0 -34
  63. package/apps/desktop/capture/protocol.ts +0 -689
  64. package/apps/desktop/cli/capture-bundle.ts +0 -1117
  65. package/apps/desktop/cli/recording-controller.ts +0 -1567
  66. package/apps/desktop/cli/recording-daemon.ts +0 -931
  67. package/src/desktop.ts +0 -253
package/src/desktop.ts DELETED
@@ -1,253 +0,0 @@
1
- import { createHash } from "node:crypto"
2
- import { createReadStream, createWriteStream } from "node:fs"
3
- import { chmod, mkdir, readFile, rename, rm } from "node:fs/promises"
4
- import { homedir, platform as hostPlatform, arch as hostArch } from "node:os"
5
- import { dirname, join, resolve } from "node:path"
6
- import { Readable } from "node:stream"
7
- import { pipeline } from "node:stream/promises"
8
- import { pathExists } from "./fs.js"
9
- import { nonGatewayChildEnvironment } from "./process-environment.js"
10
-
11
- const releaseApi = "https://api.github.com/repos/tldraw/tldraw-offline/releases/latest"
12
- export const desktopDownloadPage = "https://offline.tldraw.com"
13
-
14
- export interface ReleaseAsset {
15
- readonly name: string
16
- readonly browser_download_url: string
17
- readonly size: number
18
- readonly digest: string | null
19
- }
20
-
21
- interface Release {
22
- readonly tag_name: string
23
- readonly html_url: string
24
- readonly assets: readonly ReleaseAsset[]
25
- }
26
-
27
- function isRecord(value: unknown): value is Record<string, unknown> {
28
- return typeof value === "object" && value !== null && !Array.isArray(value)
29
- }
30
-
31
- function parseRelease(value: unknown): Release {
32
- if (
33
- !isRecord(value) ||
34
- typeof value.tag_name !== "string" ||
35
- typeof value.html_url !== "string" ||
36
- !Array.isArray(value.assets)
37
- ) {
38
- throw new Error("GitHub returned an invalid tldraw Offline release")
39
- }
40
- const assets = value.assets.map((asset, index) => {
41
- if (
42
- !isRecord(asset) ||
43
- typeof asset.name !== "string" ||
44
- typeof asset.browser_download_url !== "string" ||
45
- typeof asset.size !== "number" ||
46
- (asset.digest !== null && asset.digest !== undefined && typeof asset.digest !== "string")
47
- ) {
48
- throw new Error(`GitHub returned an invalid release asset at index ${index}`)
49
- }
50
- return {
51
- name: asset.name,
52
- browser_download_url: asset.browser_download_url,
53
- size: asset.size,
54
- digest: asset.digest ?? null,
55
- }
56
- })
57
- return { tag_name: value.tag_name, html_url: value.html_url, assets }
58
- }
59
-
60
- export function selectDesktopAsset(
61
- release: Release,
62
- platform: NodeJS.Platform = hostPlatform(),
63
- architecture: string = hostArch(),
64
- ): ReleaseAsset {
65
- const expectedName =
66
- platform === "darwin"
67
- ? "tldraw-offline-mac-universal.dmg"
68
- : platform === "win32"
69
- ? architecture === "arm64"
70
- ? "tldraw-offline-win-arm64.exe"
71
- : "tldraw-offline-win-x64.exe"
72
- : platform === "linux"
73
- ? architecture === "arm64"
74
- ? "tldraw-offline-linux-arm64.AppImage"
75
- : "tldraw-offline-linux-x86_64.AppImage"
76
- : null
77
- if (expectedName === null) {
78
- throw new Error(`tldraw Offline has no automated installer for ${platform}/${architecture}`)
79
- }
80
- const asset = release.assets.find((candidate) => candidate.name === expectedName)
81
- if (asset === undefined) {
82
- throw new Error(`The latest tldraw Offline release does not contain ${expectedName}`)
83
- }
84
- return asset
85
- }
86
-
87
- export async function getLatestDesktopRelease(): Promise<Release> {
88
- const response = await fetch(releaseApi, {
89
- headers: {
90
- Accept: "application/vnd.github+json",
91
- "User-Agent": "hraness-slopcamera",
92
- "X-GitHub-Api-Version": "2022-11-28",
93
- },
94
- })
95
- if (!response.ok) throw new Error(`GitHub release lookup failed with HTTP ${response.status}`)
96
- return parseRelease((await response.json()) as unknown)
97
- }
98
-
99
- async function sha256(filePath: string): Promise<string> {
100
- const hash = createHash("sha256")
101
- for await (const chunk of createReadStream(filePath)) hash.update(chunk)
102
- return hash.digest("hex")
103
- }
104
-
105
- async function download(asset: ReleaseAsset, filePath: string): Promise<void> {
106
- const response = await fetch(asset.browser_download_url, {
107
- headers: { "User-Agent": "hraness-slopcamera" },
108
- redirect: "follow",
109
- })
110
- if (!response.ok || response.body === null) {
111
- throw new Error(`Installer download failed with HTTP ${response.status}`)
112
- }
113
- const temporary = `${filePath}.part-${process.pid}`
114
- await mkdir(dirname(filePath), { recursive: true })
115
- try {
116
- await pipeline(
117
- Readable.fromWeb(
118
- response.body as unknown as import("node:stream/web").ReadableStream,
119
- ),
120
- createWriteStream(temporary, { mode: 0o600 }),
121
- )
122
- const expected = asset.digest?.startsWith("sha256:") ? asset.digest.slice(7) : null
123
- if (expected === null) {
124
- throw new Error("GitHub did not publish a SHA-256 digest for this installer")
125
- }
126
- const actual = await sha256(temporary)
127
- if (actual !== expected) {
128
- throw new Error(`Installer checksum mismatch: expected ${expected}, received ${actual}`)
129
- }
130
- await rename(temporary, filePath)
131
- } catch (error) {
132
- await rm(temporary, { force: true })
133
- throw error
134
- }
135
- }
136
-
137
- function spawnDetached(command: readonly string[]): void {
138
- const child = Bun.spawn([...command], {
139
- env: nonGatewayChildEnvironment(),
140
- stdin: "ignore",
141
- stdout: "ignore",
142
- stderr: "ignore",
143
- })
144
- child.unref()
145
- }
146
-
147
- export async function installDesktop(options: {
148
- readonly downloadOnly?: boolean
149
- }): Promise<{ readonly filePath: string; readonly release: string }> {
150
- const release = await getLatestDesktopRelease()
151
- const asset = selectDesktopAsset(release)
152
- const cacheDirectory = join(homedir(), ".cache", "slopcamera", "installers", release.tag_name)
153
- const installerPath = join(cacheDirectory, asset.name)
154
- let reusable = false
155
- if (await pathExists(installerPath)) {
156
- const expected = asset.digest?.startsWith("sha256:") ? asset.digest.slice(7) : null
157
- reusable = expected !== null && (await sha256(installerPath)) === expected
158
- }
159
- if (!reusable) await download(asset, installerPath)
160
-
161
- if (hostPlatform() === "linux") {
162
- const installedPath = join(homedir(), ".local", "bin", "tldraw-offline")
163
- await mkdir(dirname(installedPath), { recursive: true })
164
- const temporary = `${installedPath}.tmp-${process.pid}`
165
- await Bun.write(temporary, Bun.file(installerPath))
166
- await chmod(temporary, 0o755)
167
- await rename(temporary, installedPath)
168
- if (!options.downloadOnly) spawnDetached([installedPath])
169
- return { filePath: installedPath, release: release.tag_name }
170
- }
171
-
172
- if (!options.downloadOnly) {
173
- if (hostPlatform() === "darwin") {
174
- spawnDetached(["open", installerPath])
175
- } else {
176
- spawnDetached(["cmd.exe", "/d", "/s", "/c", "start", "", installerPath])
177
- }
178
- }
179
- return { filePath: installerPath, release: release.tag_name }
180
- }
181
-
182
- export async function findDesktopApplication(): Promise<string | null> {
183
- const candidates =
184
- hostPlatform() === "darwin"
185
- ? [
186
- "/Applications/tldraw offline.app",
187
- join(homedir(), "Applications", "tldraw offline.app"),
188
- ]
189
- : hostPlatform() === "linux"
190
- ? [join(homedir(), ".local", "bin", "tldraw-offline")]
191
- : [
192
- join(
193
- process.env.LOCALAPPDATA ?? join(homedir(), "AppData", "Local"),
194
- "Programs",
195
- "tldraw offline",
196
- "tldraw offline.exe",
197
- ),
198
- ]
199
- for (const candidate of candidates) if (await pathExists(candidate)) return candidate
200
- return null
201
- }
202
-
203
- function serverFilePath(): string {
204
- if (hostPlatform() === "darwin") {
205
- return join(homedir(), "Library", "Application Support", "tldraw", "server.json")
206
- }
207
- if (hostPlatform() === "win32") {
208
- return join(process.env.APPDATA ?? join(homedir(), "AppData", "Roaming"), "tldraw", "server.json")
209
- }
210
- return join(process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"), "tldraw", "server.json")
211
- }
212
-
213
- export async function desktopStatus(): Promise<{
214
- readonly installedPath: string | null
215
- readonly server: { readonly port: number; readonly pid: number | null } | null
216
- }> {
217
- const installedPath = await findDesktopApplication()
218
- const filePath = serverFilePath()
219
- if (!(await pathExists(filePath))) return { installedPath, server: null }
220
- try {
221
- const parsed = JSON.parse(await readFile(filePath, "utf8")) as unknown
222
- if (!isRecord(parsed) || typeof parsed.port !== "number") {
223
- return { installedPath, server: null }
224
- }
225
- return {
226
- installedPath,
227
- server: {
228
- port: parsed.port,
229
- pid: typeof parsed.pid === "number" ? parsed.pid : null,
230
- },
231
- }
232
- } catch {
233
- return { installedPath, server: null }
234
- }
235
- }
236
-
237
- export async function openInDesktop(filePath: string): Promise<void> {
238
- const absolutePath = resolve(filePath)
239
- if (!(await pathExists(absolutePath))) throw new Error(`File does not exist: ${absolutePath}`)
240
- const application = await findDesktopApplication()
241
- if (application === null) {
242
- throw new Error(
243
- `tldraw Offline is not installed. Run "slopcamera canvas install" or visit ${desktopDownloadPage}`,
244
- )
245
- }
246
- if (hostPlatform() === "darwin") {
247
- spawnDetached(["open", "-a", application, absolutePath])
248
- } else if (hostPlatform() === "win32") {
249
- spawnDetached(["cmd.exe", "/d", "/s", "/c", "start", "", absolutePath])
250
- } else {
251
- spawnDetached([application, absolutePath])
252
- }
253
- }