loopat 0.1.4 → 0.1.6
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/package.json +1 -1
- package/server/src/config.ts +10 -0
- package/server/src/index.ts +8 -8
- package/server/src/loops.ts +97 -21
- package/web/dist/assets/{CodeEditor-DfIQi9Ks.js → CodeEditor-CPjl_-RF.js} +1 -1
- package/web/dist/assets/{Editor-CSZrkDg9.js → Editor-R_5WHbRu.js} +1 -1
- package/web/dist/assets/{Markdown-Ci1M6VXe.js → Markdown-DcTElXKz.js} +1 -1
- package/web/dist/assets/{MilkdownEditor-CkH6ILre.js → MilkdownEditor-Qch5gd21.js} +1 -1
- package/web/dist/assets/{Terminal-oo3S6Viy.js → Terminal-BdwxU0eF.js} +1 -1
- package/web/dist/assets/{index-Nx0RBMP4.js → index-BfKJexXk.js} +3 -3
- package/web/dist/index.html +1 -1
package/package.json
CHANGED
package/server/src/config.ts
CHANGED
|
@@ -210,6 +210,11 @@ export type PersonalConfigDisk = {
|
|
|
210
210
|
shell?: string
|
|
211
211
|
/** Optional. Missing = "fresh" (user hasn't started or dismissed yet). */
|
|
212
212
|
onboarding?: OnboardingState
|
|
213
|
+
/** Authoritative kn/notes remotes — the personal repo is self-describing.
|
|
214
|
+
* host config.json's knowledge/notes are a display mirror; these are what a
|
|
215
|
+
* loop actually connects to (with the user's vault key). */
|
|
216
|
+
knowledge?: RemoteSpec
|
|
217
|
+
notes?: RemoteSpec
|
|
213
218
|
}
|
|
214
219
|
|
|
215
220
|
export type PersonalConfig = {
|
|
@@ -224,6 +229,9 @@ export type PersonalConfig = {
|
|
|
224
229
|
vaultEnvs: Record<string, string>
|
|
225
230
|
shell?: string
|
|
226
231
|
onboarding?: OnboardingState
|
|
232
|
+
/** Authoritative kn/notes remotes (self-describing personal repo). */
|
|
233
|
+
knowledge?: RemoteSpec
|
|
234
|
+
notes?: RemoteSpec
|
|
227
235
|
}
|
|
228
236
|
|
|
229
237
|
/**
|
|
@@ -443,6 +451,8 @@ export async function loadPersonalConfig(
|
|
|
443
451
|
vaultEnvs,
|
|
444
452
|
...(disk.shell ? { shell: disk.shell } : {}),
|
|
445
453
|
...(disk.onboarding ? { onboarding: disk.onboarding } : {}),
|
|
454
|
+
...(disk.knowledge ? { knowledge: disk.knowledge } : {}),
|
|
455
|
+
...(disk.notes ? { notes: disk.notes } : {}),
|
|
446
456
|
}
|
|
447
457
|
personalCache.set(cacheKey, { cfg, configMtimeMs, envsDirMtimeMs })
|
|
448
458
|
return cfg
|
package/server/src/index.ts
CHANGED
|
@@ -1416,23 +1416,23 @@ function syncDirFor(resource: string, name?: string): string | null {
|
|
|
1416
1416
|
return null
|
|
1417
1417
|
}
|
|
1418
1418
|
|
|
1419
|
-
app.get("/api/sync/knowledge/status", requireAuth, async (c) => c.json(await inspectRepoSync(workspaceKnowledgeDir())))
|
|
1419
|
+
app.get("/api/sync/knowledge/status", requireAuth, async (c) => c.json(await inspectRepoSync(workspaceKnowledgeDir(), c.get("userId") as string)))
|
|
1420
1420
|
app.post("/api/sync/knowledge/pull", requireAuth, async (c) => {
|
|
1421
|
-
const r = await pullRepoFromRemote(workspaceKnowledgeDir())
|
|
1421
|
+
const r = await pullRepoFromRemote(workspaceKnowledgeDir(), c.get("userId") as string)
|
|
1422
1422
|
return r.ok ? c.json(r) : c.json({ error: r.error }, 400)
|
|
1423
1423
|
})
|
|
1424
1424
|
app.post("/api/sync/knowledge/push", requireAuth, async (c) => {
|
|
1425
|
-
const r = await pushRepoToRemote(workspaceKnowledgeDir())
|
|
1425
|
+
const r = await pushRepoToRemote(workspaceKnowledgeDir(), c.get("userId") as string)
|
|
1426
1426
|
return r.ok ? c.json(r) : c.json({ error: r.error }, 400)
|
|
1427
1427
|
})
|
|
1428
1428
|
|
|
1429
|
-
app.get("/api/sync/notes/status", requireAuth, async (c) => c.json(await inspectRepoSync(workspaceNotesDir())))
|
|
1429
|
+
app.get("/api/sync/notes/status", requireAuth, async (c) => c.json(await inspectRepoSync(workspaceNotesDir(), c.get("userId") as string)))
|
|
1430
1430
|
app.post("/api/sync/notes/pull", requireAuth, async (c) => {
|
|
1431
|
-
const r = await pullRepoFromRemote(workspaceNotesDir())
|
|
1431
|
+
const r = await pullRepoFromRemote(workspaceNotesDir(), c.get("userId") as string)
|
|
1432
1432
|
return r.ok ? c.json(r) : c.json({ error: r.error }, 400)
|
|
1433
1433
|
})
|
|
1434
1434
|
app.post("/api/sync/notes/push", requireAuth, async (c) => {
|
|
1435
|
-
const r = await pushRepoToRemote(workspaceNotesDir())
|
|
1435
|
+
const r = await pushRepoToRemote(workspaceNotesDir(), c.get("userId") as string)
|
|
1436
1436
|
return r.ok ? c.json(r) : c.json({ error: r.error }, 400)
|
|
1437
1437
|
})
|
|
1438
1438
|
|
|
@@ -1454,12 +1454,12 @@ app.get("/api/sync/repos", requireAuth, async (c) => {
|
|
|
1454
1454
|
app.get("/api/sync/repos/:name/status", requireAuth, async (c) => {
|
|
1455
1455
|
const dir = syncDirFor("repos", c.req.param("name"))
|
|
1456
1456
|
if (!dir) return c.json({ error: "repo not found" }, 404)
|
|
1457
|
-
return c.json(await inspectRepoSync(dir))
|
|
1457
|
+
return c.json(await inspectRepoSync(dir, c.get("userId") as string))
|
|
1458
1458
|
})
|
|
1459
1459
|
app.post("/api/sync/repos/:name/pull", requireAuth, async (c) => {
|
|
1460
1460
|
const dir = syncDirFor("repos", c.req.param("name"))
|
|
1461
1461
|
if (!dir) return c.json({ error: "repo not found" }, 404)
|
|
1462
|
-
const r = await pullRepoFromRemote(dir)
|
|
1462
|
+
const r = await pullRepoFromRemote(dir, c.get("userId") as string)
|
|
1463
1463
|
return r.ok ? c.json(r) : c.json({ error: r.error }, 400)
|
|
1464
1464
|
})
|
|
1465
1465
|
|
package/server/src/loops.ts
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
workspaceOriginsDir,
|
|
25
25
|
workspaceOriginPath,
|
|
26
26
|
personalDir,
|
|
27
|
+
personalVaultDir,
|
|
27
28
|
uiNotesDir,
|
|
28
29
|
personalMemoryDir,
|
|
29
30
|
workspaceMemoryDir,
|
|
@@ -35,7 +36,7 @@ import {
|
|
|
35
36
|
} from "./paths"
|
|
36
37
|
import type { RepoSpec } from "./config"
|
|
37
38
|
import { existsSync as existsSyncBase } from "node:fs"
|
|
38
|
-
import { loadConfig } from "./config"
|
|
39
|
+
import { loadConfig, loadPersonalConfig } from "./config"
|
|
39
40
|
import { ensurePersonalKeypair } from "./personal-keys"
|
|
40
41
|
import { composeLoopClaudeConfig, writeLoopSettings } from "./compose"
|
|
41
42
|
import { getProvider } from "./git-host"
|
|
@@ -316,6 +317,48 @@ async function ensureContextRepo(dir: string, name: string, url?: string): Promi
|
|
|
316
317
|
}
|
|
317
318
|
}
|
|
318
319
|
|
|
320
|
+
/**
|
|
321
|
+
* The personal repo is self-describing: its `.loopat/config.json` declares the
|
|
322
|
+
* authoritative kn/notes remotes, and a loop connects to them with the user's
|
|
323
|
+
* OWN key from the selected vault (`vaults/<vault>/mounts/home/.ssh/id`), not
|
|
324
|
+
* the host's ssh. Called at loop creation, which has the user + vault in hand.
|
|
325
|
+
*
|
|
326
|
+
* The startup clone (driven by host config.json) stays as a display mirror;
|
|
327
|
+
* here the personal-declared url wins and becomes the context repo's origin.
|
|
328
|
+
*
|
|
329
|
+
* It sets the origin, fetches with the vault key (host-side), AND persists a
|
|
330
|
+
* `core.sshCommand` pointing at the vault key's SANDBOX path so the AI's
|
|
331
|
+
* promote (git push from inside the sandbox) authenticates as the user with no
|
|
332
|
+
* interactive host-key prompt. Host-side git overrides that config via
|
|
333
|
+
* GIT_SSH_COMMAND (env beats config), so the sandbox path is never used here.
|
|
334
|
+
*/
|
|
335
|
+
export async function ensureUserContext(user: string, vault: string = "default"): Promise<void> {
|
|
336
|
+
const cfg = await loadPersonalConfig(user, vault)
|
|
337
|
+
const sshEnv = { ...process.env, GIT_SSH_COMMAND: sshCommandForUser(user, vault) }
|
|
338
|
+
const layers: Array<[string, string | undefined]> = [
|
|
339
|
+
[workspaceKnowledgeDir(), cfg.knowledge?.git],
|
|
340
|
+
[workspaceNotesDir(), cfg.notes?.git],
|
|
341
|
+
]
|
|
342
|
+
for (const [dir, url] of layers) {
|
|
343
|
+
if (!url) continue // personal didn't declare one → keep the host/local origin
|
|
344
|
+
if (!existsSyncBase(join(dir, ".git"))) continue // not initialized yet (startup hasn't run)
|
|
345
|
+
const has = await execFileP("git", ["-C", dir, "remote", "get-url", "origin"]).then(() => true).catch(() => false)
|
|
346
|
+
await execFileP("git", ["-C", dir, "remote", has ? "set-url" : "add", "origin", url]).catch(() => {})
|
|
347
|
+
// Sandbox-side promote: persist core.sshCommand pointing at the vault key's
|
|
348
|
+
// SANDBOX path (/loopat/home/<user>/.ssh/id, where the vault home-mount
|
|
349
|
+
// lands) with accept-new, so the AI's `git push` inside the sandbox
|
|
350
|
+
// authenticates as the user without an interactive host-key prompt. Host-
|
|
351
|
+
// side ops override this with GIT_SSH_COMMAND (env beats config), so the
|
|
352
|
+
// sandbox path is never used server-side.
|
|
353
|
+
const sandboxKey = `/loopat/home/${user}/.ssh/id`
|
|
354
|
+
await execFileP("git", ["-C", dir, "config", "core.sshCommand",
|
|
355
|
+
`ssh -i ${sandboxKey} -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null`]).catch(() => {})
|
|
356
|
+
// validate connectivity + populate origin/* with the user's key (transient,
|
|
357
|
+
// server-side only).
|
|
358
|
+
await execFileP("git", ["-C", dir, "fetch", "--quiet", "origin"], { env: sshEnv, timeout: 20_000 }).catch(() => {})
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
319
362
|
/**
|
|
320
363
|
* Repos are clone-on-demand — they can be large, so we don't pre-clone the
|
|
321
364
|
* whole set. Instead write a manifest (REPOS.md) listing the full roster, and
|
|
@@ -340,7 +383,7 @@ async function writeReposManifest(specs: RepoSpec[]) {
|
|
|
340
383
|
* Clone a single registered repo if it isn't present yet. Returns whether the
|
|
341
384
|
* repo dir exists afterwards. Used by loop creation and any on-demand path.
|
|
342
385
|
*/
|
|
343
|
-
async function ensureRepoCloned(name: string): Promise<boolean> {
|
|
386
|
+
async function ensureRepoCloned(name: string, sshCommand?: string): Promise<boolean> {
|
|
344
387
|
const dir = workspaceRepoDir(name)
|
|
345
388
|
if (existsSyncBase(dir)) return true
|
|
346
389
|
const cfg = await loadConfig()
|
|
@@ -348,7 +391,8 @@ async function ensureRepoCloned(name: string): Promise<boolean> {
|
|
|
348
391
|
if (!spec?.git) return false
|
|
349
392
|
try {
|
|
350
393
|
await mkdir(workspaceReposDir(), { recursive: true })
|
|
351
|
-
|
|
394
|
+
const env = sshCommand ? { ...process.env, GIT_SSH_COMMAND: sshCommand } : process.env
|
|
395
|
+
await execFileP("git", ["clone", "--", spec.git, dir], { env })
|
|
352
396
|
console.log(`[loopat] cloned on demand ${spec.git} → ${dir}`)
|
|
353
397
|
return true
|
|
354
398
|
} catch (e: any) {
|
|
@@ -639,7 +683,9 @@ export async function importPersonalFromRepo(
|
|
|
639
683
|
// Clone into a tmp dir. ssh uses the deploy key (StrictHostKeyChecking=
|
|
640
684
|
// accept-new, no pre-populated known_hosts on first run); https auths via url.
|
|
641
685
|
const tmp = await mkdtemp(join(tmpdir(), `loopat-import-${userId}-`))
|
|
642
|
-
|
|
686
|
+
// Bootstrap: first clone of the personal repo uses the host deploy-key (no
|
|
687
|
+
// vault key exists yet). Every later op uses the user's vault key.
|
|
688
|
+
const cloneEnv = isHttps ? { ...process.env } : { ...process.env, GIT_SSH_COMMAND: bootstrapSshCommand(userId) }
|
|
643
689
|
try {
|
|
644
690
|
await execFileP("git", ["clone", "--", repoUrl, tmp], { env: cloneEnv })
|
|
645
691
|
} catch (e: any) {
|
|
@@ -714,9 +760,25 @@ export async function importPersonalFromRepo(
|
|
|
714
760
|
return { ok: true, autoInitialized: true, cryptKey: init.cryptKey }
|
|
715
761
|
}
|
|
716
762
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
763
|
+
/**
|
|
764
|
+
* The ssh command a server-side git op uses to act AS the user — its OWN key
|
|
765
|
+
* from the selected vault (`vaults/<vault>/mounts/home/.ssh/id`). If the key
|
|
766
|
+
* isn't there the op simply fails: we deliberately do NOT fall back to the host
|
|
767
|
+
* deploy-key, so a loop never borrows the host's access. Authorization tracks
|
|
768
|
+
* the personal repo, not the host (see behavior/02-personal-permissions.md).
|
|
769
|
+
*/
|
|
770
|
+
function sshCommandForUser(userId: string, vault: string = "default"): string {
|
|
771
|
+
const vaultKey = join(personalVaultDir(userId, vault), "mounts", "home", ".ssh", "id")
|
|
772
|
+
return `ssh -i ${vaultKey} -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null`
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Bootstrap ONLY: the first clone/decrypt of the personal repo itself, before
|
|
777
|
+
* any vault key exists. Uses the host-managed deploy-key — the one credential
|
|
778
|
+
* the host legitimately holds to unlock a personal repo.
|
|
779
|
+
*/
|
|
780
|
+
function bootstrapSshCommand(userId: string): string {
|
|
781
|
+
return `ssh -i ${hostDeployKeyPath(userId)} -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null`
|
|
720
782
|
}
|
|
721
783
|
|
|
722
784
|
async function swapPersonalDir(
|
|
@@ -1273,13 +1335,16 @@ export async function syncUiNotes(user: string): Promise<PersonalPushResult> {
|
|
|
1273
1335
|
const branch = await remoteDefaultBranch(dir)
|
|
1274
1336
|
const c = await commitLocalChanges(dir, "loopat: edit notes")
|
|
1275
1337
|
if (!c.ok) return { ok: false, error: c.error }
|
|
1276
|
-
const
|
|
1338
|
+
const userSsh = sshCommandForUser(user)
|
|
1339
|
+
const reb = await rebaseOntoOrigin(dir, branch, userSsh)
|
|
1277
1340
|
if (!reb.ok) {
|
|
1278
1341
|
if ("conflict" in reb) return { ok: false, error: "conflict with remote", conflict: true, files: reb.files }
|
|
1279
1342
|
return { ok: false, error: reb.error }
|
|
1280
1343
|
}
|
|
1281
1344
|
try {
|
|
1282
|
-
await execFileP("git", ["-C", dir, "push", "origin", `HEAD:${branch}`]
|
|
1345
|
+
await execFileP("git", ["-C", dir, "push", "origin", `HEAD:${branch}`], {
|
|
1346
|
+
env: { ...process.env, GIT_SSH_COMMAND: userSsh },
|
|
1347
|
+
})
|
|
1283
1348
|
} catch (e: any) {
|
|
1284
1349
|
const stderr = (e?.stderr ?? "").toString().trim()
|
|
1285
1350
|
return { ok: false, error: `push failed: ${stderr || e?.message || e}`, needsPull: true }
|
|
@@ -1300,7 +1365,7 @@ export async function ffUpdateUiNotes(
|
|
|
1300
1365
|
const branch = await remoteDefaultBranch(dir)
|
|
1301
1366
|
try {
|
|
1302
1367
|
await execFileP("git", ["-C", dir, "fetch", "origin"], {
|
|
1303
|
-
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }, timeout: 30_000,
|
|
1368
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0", GIT_SSH_COMMAND: sshCommandForUser(user) }, timeout: 30_000,
|
|
1304
1369
|
})
|
|
1305
1370
|
} catch (e: any) {
|
|
1306
1371
|
return { ok: false, error: `fetch failed: ${e?.stderr ?? e?.message ?? e}` }
|
|
@@ -1332,7 +1397,7 @@ export async function notesBehind(user: string): Promise<number> {
|
|
|
1332
1397
|
const branch = await remoteDefaultBranch(dir)
|
|
1333
1398
|
try {
|
|
1334
1399
|
await execFileP("git", ["-C", dir, "fetch", "origin"], {
|
|
1335
|
-
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }, timeout: 30_000,
|
|
1400
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0", GIT_SSH_COMMAND: sshCommandForUser(user) }, timeout: 30_000,
|
|
1336
1401
|
})
|
|
1337
1402
|
} catch {
|
|
1338
1403
|
return 0
|
|
@@ -1371,7 +1436,7 @@ export type RepoSyncResult =
|
|
|
1371
1436
|
* failures are tolerated (offline / auth glitch) — status still reflects
|
|
1372
1437
|
* last-known remote state.
|
|
1373
1438
|
*/
|
|
1374
|
-
export async function inspectRepoSync(dir: string): Promise<RepoSyncStatus> {
|
|
1439
|
+
export async function inspectRepoSync(dir: string, user?: string): Promise<RepoSyncStatus> {
|
|
1375
1440
|
if (!existsSyncBase(dir) || !existsSyncBase(join(dir, ".git"))) {
|
|
1376
1441
|
return { isGitRepo: false, hasRemote: false, branch: "", ahead: 0, behind: 0, uncommitted: 0 }
|
|
1377
1442
|
}
|
|
@@ -1390,7 +1455,8 @@ export async function inspectRepoSync(dir: string): Promise<RepoSyncStatus> {
|
|
|
1390
1455
|
|
|
1391
1456
|
if (hasRemote) {
|
|
1392
1457
|
try {
|
|
1393
|
-
|
|
1458
|
+
const env = user ? { ...process.env, GIT_SSH_COMMAND: sshCommandForUser(user) } : process.env
|
|
1459
|
+
await execFileP("git", ["-C", dir, "fetch", "--quiet", "origin"], { env, timeout: 15_000 })
|
|
1394
1460
|
} catch {}
|
|
1395
1461
|
}
|
|
1396
1462
|
|
|
@@ -1420,7 +1486,7 @@ export async function inspectRepoSync(dir: string): Promise<RepoSyncStatus> {
|
|
|
1420
1486
|
* changes (we don't auto-stash workspace repos — caller decides) and on
|
|
1421
1487
|
* any non-ff condition.
|
|
1422
1488
|
*/
|
|
1423
|
-
export async function pullRepoFromRemote(dir: string): Promise<RepoSyncResult> {
|
|
1489
|
+
export async function pullRepoFromRemote(dir: string, user?: string): Promise<RepoSyncResult> {
|
|
1424
1490
|
if (!existsSyncBase(join(dir, ".git"))) {
|
|
1425
1491
|
return { ok: false, error: "not a git repo" }
|
|
1426
1492
|
}
|
|
@@ -1449,7 +1515,8 @@ export async function pullRepoFromRemote(dir: string): Promise<RepoSyncResult> {
|
|
|
1449
1515
|
}
|
|
1450
1516
|
|
|
1451
1517
|
try {
|
|
1452
|
-
|
|
1518
|
+
const env = user ? { ...process.env, GIT_SSH_COMMAND: sshCommandForUser(user) } : process.env
|
|
1519
|
+
await execFileP("git", ["-C", dir, "fetch", "origin"], { env, timeout: 30_000 })
|
|
1453
1520
|
} catch (e: any) {
|
|
1454
1521
|
return { ok: false, error: `fetch failed: ${e?.stderr ?? e?.message ?? e}` }
|
|
1455
1522
|
}
|
|
@@ -1469,7 +1536,7 @@ export async function pullRepoFromRemote(dir: string): Promise<RepoSyncResult> {
|
|
|
1469
1536
|
* non-ff by default, which is exactly the abort-on-conflict behavior we
|
|
1470
1537
|
* want. Caller pulls first if rejected.
|
|
1471
1538
|
*/
|
|
1472
|
-
export async function pushRepoToRemote(dir: string): Promise<RepoSyncResult> {
|
|
1539
|
+
export async function pushRepoToRemote(dir: string, user?: string): Promise<RepoSyncResult> {
|
|
1473
1540
|
if (!existsSyncBase(join(dir, ".git"))) {
|
|
1474
1541
|
return { ok: false, error: "not a git repo" }
|
|
1475
1542
|
}
|
|
@@ -1489,7 +1556,8 @@ export async function pushRepoToRemote(dir: string): Promise<RepoSyncResult> {
|
|
|
1489
1556
|
if (!branch) return { ok: false, error: "HEAD is detached" }
|
|
1490
1557
|
|
|
1491
1558
|
try {
|
|
1492
|
-
|
|
1559
|
+
const env = user ? { ...process.env, GIT_SSH_COMMAND: sshCommandForUser(user) } : process.env
|
|
1560
|
+
await execFileP("git", ["-C", dir, "push", "origin", `HEAD:${branch}`], { env })
|
|
1493
1561
|
} catch (e: any) {
|
|
1494
1562
|
const stderr = (e?.stderr ?? "").toString().trim()
|
|
1495
1563
|
return { ok: false, error: `push failed: ${stderr || e?.message || e}` }
|
|
@@ -1691,14 +1759,15 @@ async function remoteDefaultBranch(dir: string): Promise<string> {
|
|
|
1691
1759
|
return "main"
|
|
1692
1760
|
}
|
|
1693
1761
|
|
|
1694
|
-
async function remoteStartPoint(repo: string): Promise<string | null> {
|
|
1762
|
+
async function remoteStartPoint(repo: string, sshCommand?: string): Promise<string | null> {
|
|
1695
1763
|
try {
|
|
1696
1764
|
await execFileP("git", ["-C", repo, "remote", "get-url", "origin"])
|
|
1697
1765
|
} catch {
|
|
1698
1766
|
return null
|
|
1699
1767
|
}
|
|
1700
1768
|
try {
|
|
1701
|
-
|
|
1769
|
+
const env = sshCommand ? { ...process.env, GIT_SSH_COMMAND: sshCommand } : process.env
|
|
1770
|
+
await execFileP("git", ["-C", repo, "fetch", "--quiet", "origin"], { env, timeout: 15_000 })
|
|
1702
1771
|
} catch {}
|
|
1703
1772
|
const branch = await remoteDefaultBranch(repo)
|
|
1704
1773
|
try {
|
|
@@ -1794,8 +1863,10 @@ export async function createLoop(opts: {
|
|
|
1794
1863
|
|
|
1795
1864
|
// workdir = git worktree add (if repo selected) OR plain mkdir
|
|
1796
1865
|
if (opts.repo) {
|
|
1866
|
+
// clone + fetch as the user (their vault key), not the host's ssh.
|
|
1867
|
+
const userSsh = sshCommandForUser(opts.createdBy, opts.vault ?? "default")
|
|
1797
1868
|
// clone-on-demand: pull the repo down only now that a loop actually needs it
|
|
1798
|
-
if (!(await ensureRepoCloned(opts.repo))) {
|
|
1869
|
+
if (!(await ensureRepoCloned(opts.repo, userSsh))) {
|
|
1799
1870
|
throw new Error(`repo "${opts.repo}" not found / clone failed`)
|
|
1800
1871
|
}
|
|
1801
1872
|
const repoPath = workspaceRepoDir(opts.repo)
|
|
@@ -1804,7 +1875,7 @@ export async function createLoop(opts: {
|
|
|
1804
1875
|
// ① pull (docs/context-flow.md): base the workdir branch on origin/main
|
|
1805
1876
|
// (best-effort fetch) so it starts from latest consensus; fall back to
|
|
1806
1877
|
// local HEAD when there's no remote / no origin/main.
|
|
1807
|
-
const start = await remoteStartPoint(repoPath)
|
|
1878
|
+
const start = await remoteStartPoint(repoPath, userSsh)
|
|
1808
1879
|
const wtArgs = ["-C", repoPath, "worktree", "add", "-b", branch, loopWorkdir(id)]
|
|
1809
1880
|
if (start) wtArgs.push(start)
|
|
1810
1881
|
await execFileP("git", wtArgs)
|
|
@@ -1819,6 +1890,11 @@ export async function createLoop(opts: {
|
|
|
1819
1890
|
await mkdir(loopWorkdir(id), { recursive: true })
|
|
1820
1891
|
}
|
|
1821
1892
|
|
|
1893
|
+
// Point the context repos at the personal-declared kn/notes remotes and
|
|
1894
|
+
// connect with the user's vault key (personal repo is self-describing).
|
|
1895
|
+
await ensureUserContext(opts.createdBy, opts.vault ?? "default").catch(
|
|
1896
|
+
(e: any) => console.warn(`[loopat] ensureUserContext(${opts.createdBy}): ${e?.message ?? e}`),
|
|
1897
|
+
)
|
|
1822
1898
|
await ensureContextMounts(id, effectiveDriver(meta))
|
|
1823
1899
|
await writeFile(loopMetaPath(id), JSON.stringify(meta, null, 2))
|
|
1824
1900
|
return meta
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{C as e,x as t,y as n}from"./index-
|
|
1
|
+
import{C as e,x as t,y as n}from"./index-BfKJexXk.js";import{n as r,r as i,t as a}from"./w3c-keyname-BOAvb0qz.js";var o=e(t(),1),s=[],c=[];(()=>{let e=`lc,34,7n,7,7b,19,,,,2,,2,,,20,b,1c,l,g,,2t,7,2,6,2,2,,4,z,,u,r,2j,b,1m,9,9,,o,4,,9,,3,,5,17,3,3b,f,,w,1j,,,,4,8,4,,3,7,a,2,t,,1m,,,,2,4,8,,9,,a,2,q,,2,2,1l,,4,2,4,2,2,3,3,,u,2,3,,b,2,1l,,4,5,,2,4,,k,2,m,6,,,1m,,,2,,4,8,,7,3,a,2,u,,1n,,,,c,,9,,14,,3,,1l,3,5,3,,4,7,2,b,2,t,,1m,,2,,2,,3,,5,2,7,2,b,2,s,2,1l,2,,,2,4,8,,9,,a,2,t,,20,,4,,2,3,,,8,,29,,2,7,c,8,2q,,2,9,b,6,22,2,r,,,,,,1j,e,,5,,2,5,b,,10,9,,2u,4,,6,,2,2,2,p,2,4,3,g,4,d,,2,2,6,,f,,jj,3,qa,3,t,3,t,2,u,2,1s,2,,7,8,,2,b,9,,19,3,3b,2,y,,3a,3,4,2,9,,6,3,63,2,2,,1m,,,7,,,,,2,8,6,a,2,,1c,h,1r,4,1c,7,,,5,,14,9,c,2,w,4,2,2,,3,1k,,,2,3,,,3,1m,8,2,2,48,3,,d,,7,4,,6,,3,2,5i,1m,,5,ek,,5f,x,2da,3,3x,,2o,w,fe,6,2x,2,n9w,4,,a,w,2,28,2,7k,,3,,4,,p,2,5,,47,2,q,i,d,,12,8,p,b,1a,3,1c,,2,4,2,2,13,,1v,6,2,2,2,2,c,,8,,1b,,1f,,,3,2,2,5,2,,,16,2,8,,6m,,2,,4,,fn4,,kh,g,g,g,a6,2,gt,,6a,,45,5,1ae,3,,2,5,4,14,3,4,,4l,2,fx,4,ar,2,49,b,4w,,1i,f,1k,3,1d,4,2,2,1x,3,10,5,,8,1q,,c,2,1g,9,a,4,2,,2n,3,2,,,2,6,,4g,,3,8,l,2,1l,2,,,,,m,,e,7,3,5,5f,8,2,3,,,n,,29,,2,6,,,2,,,2,,2,6j,,2,4,6,2,,2,r,2,2d,8,2,,,2,2y,,,,2,6,,,2t,3,2,4,,5,77,9,,2,6t,,a,2,,,4,,40,4,2,2,4,,w,a,14,6,2,4,8,,9,6,2,3,1a,d,,2,ba,7,,6,,,2a,m,2,7,,2,,2,3e,6,3,,,2,,7,,,20,2,3,,,,9n,2,f0b,5,1n,7,t4,,1r,4,29,,f5k,2,43q,,,3,4,5,8,8,2,7,u,4,44,3,1iz,1j,4,1e,8,,e,,m,5,,f,11s,7,,h,2,7,,2,,5,79,7,c5,4,15s,7,31,7,240,5,gx7k,2o,3k,6o`.split(`,`).map(e=>e?parseInt(e,36):1);for(let t=0,n=0;t<e.length;t++)(t%2?c:s).push(n+=e[t])})();function l(e){if(e<768)return!1;for(let t=0,n=s.length;;){let r=t+n>>1;if(e<s[r])n=r;else if(e>=c[r])t=r+1;else return!0;if(t==n)return!1}}function u(e){return e>=127462&&e<=127487}var d=8205;function f(e,t,n=!0,r=!0){return(n?p:m)(e,t,r)}function p(e,t,n){if(t==e.length)return t;t&&g(e.charCodeAt(t))&&_(e.charCodeAt(t-1))&&t--;let r=h(e,t);for(t+=v(r);t<e.length;){let i=h(e,t);if(r==d||i==d||n&&l(i))t+=v(i),r=i;else if(u(i)){let n=0,r=t-2;for(;r>=0&&u(h(e,r));)n++,r-=2;if(n%2==0)break;t+=2}else break}return t}function m(e,t,n){for(;t>0;){let r=p(e,t-2,n);if(r<t)return r;t--}return 0}function h(e,t){let n=e.charCodeAt(t);if(!_(n)||t+1==e.length)return n;let r=e.charCodeAt(t+1);return g(r)?(n-55296<<10)+(r-56320)+65536:n}function g(e){return e>=56320&&e<57344}function _(e){return e>=55296&&e<56320}function v(e){return e<65536?1:2}var y=class e{lineAt(e){if(e<0||e>this.length)throw RangeError(`Invalid position ${e} in document of length ${this.length}`);return this.lineInner(e,!1,1,0)}line(e){if(e<1||e>this.lines)throw RangeError(`Invalid line number ${e} in ${this.lines}-line document`);return this.lineInner(e,!0,1,0)}replace(e,t,n){[e,t]=w(this,e,t);let r=[];return this.decompose(0,e,r,2),n.length&&n.decompose(0,n.length,r,3),this.decompose(t,this.length,r,1),x.from(r,this.length-(t-e)+n.length)}append(e){return this.replace(this.length,this.length,e)}slice(e,t=this.length){[e,t]=w(this,e,t);let n=[];return this.decompose(e,t,n,0),x.from(n,t-e)}eq(e){if(e==this)return!0;if(e.length!=this.length||e.lines!=this.lines)return!1;let t=this.scanIdentical(e,1),n=this.length-this.scanIdentical(e,-1),r=new C(this),i=new C(e);for(let e=t,a=t;;){if(r.next(e),i.next(e),e=0,r.lineBreak!=i.lineBreak||r.done!=i.done||r.value!=i.value)return!1;if(a+=r.value.length,r.done||a>=n)return!0}}iter(e=1){return new C(this,e)}iterRange(e,t=this.length){return new ne(this,e,t)}iterLines(e,t){let n;if(e==null)n=this.iter();else{t??=this.lines+1;let r=this.line(e).from;n=this.iterRange(r,Math.max(r,t==this.lines+1?this.length:t<=1?0:this.line(t-1).to))}return new re(n)}toString(){return this.sliceString(0)}toJSON(){let e=[];return this.flatten(e),e}constructor(){}static of(t){if(t.length==0)throw RangeError(`A document must have at least one line`);return t.length==1&&!t[0]?e.empty:t.length<=32?new b(t):x.from(b.split(t,[]))}},b=class e extends y{constructor(e,t=ee(e)){super(),this.text=e,this.length=t}get lines(){return this.text.length}get children(){return null}lineInner(e,t,n,r){for(let i=0;;i++){let a=this.text[i],o=r+a.length;if((t?n:o)>=e)return new ie(r,o,n,a);r=o+1,n++}}decompose(t,n,r,i){let a=t<=0&&n>=this.length?this:new e(S(this.text,t,n),Math.min(n,this.length)-Math.max(0,t));if(i&1){let t=r.pop(),n=te(a.text,t.text.slice(),0,a.length);if(n.length<=32)r.push(new e(n,t.length+a.length));else{let t=n.length>>1;r.push(new e(n.slice(0,t)),new e(n.slice(t)))}}else r.push(a)}replace(t,n,r){if(!(r instanceof e))return super.replace(t,n,r);[t,n]=w(this,t,n);let i=te(this.text,te(r.text,S(this.text,0,t)),n),a=this.length+r.length-(n-t);return i.length<=32?new e(i,a):x.from(e.split(i,[]),a)}sliceString(e,t=this.length,n=`
|
|
2
2
|
`){[e,t]=w(this,e,t);let r=``;for(let i=0,a=0;i<=t&&a<this.text.length;a++){let o=this.text[a],s=i+o.length;i>e&&a&&(r+=n),e<s&&t>i&&(r+=o.slice(Math.max(0,e-i),t-i)),i=s+1}return r}flatten(e){for(let t of this.text)e.push(t)}scanIdentical(){return 0}static split(t,n){let r=[],i=-1;for(let a of t)r.push(a),i+=a.length+1,r.length==32&&(n.push(new e(r,i)),r=[],i=-1);return i>-1&&n.push(new e(r,i)),n}},x=class e extends y{constructor(e,t){super(),this.children=e,this.length=t,this.lines=0;for(let t of e)this.lines+=t.lines}lineInner(e,t,n,r){for(let i=0;;i++){let a=this.children[i],o=r+a.length,s=n+a.lines-1;if((t?s:o)>=e)return a.lineInner(e,t,n,r);r=o+1,n=s+1}}decompose(e,t,n,r){for(let i=0,a=0;a<=t&&i<this.children.length;i++){let o=this.children[i],s=a+o.length;if(e<=s&&t>=a){let i=r&(a<=e|(s>=t?2:0));a>=e&&s<=t&&!i?n.push(o):o.decompose(e-a,t-a,n,i)}a=s+1}}replace(t,n,r){if([t,n]=w(this,t,n),r.lines<this.lines)for(let i=0,a=0;i<this.children.length;i++){let o=this.children[i],s=a+o.length;if(t>=a&&n<=s){let c=o.replace(t-a,n-a,r),l=this.lines-o.lines+c.lines;if(c.lines<l>>4&&c.lines>l>>6){let a=this.children.slice();return a[i]=c,new e(a,this.length-(n-t)+r.length)}return super.replace(a,s,c)}a=s+1}return super.replace(t,n,r)}sliceString(e,t=this.length,n=`
|
|
3
3
|
`){[e,t]=w(this,e,t);let r=``;for(let i=0,a=0;i<this.children.length&&a<=t;i++){let o=this.children[i],s=a+o.length;a>e&&i&&(r+=n),e<s&&t>a&&(r+=o.sliceString(e-a,t-a,n)),a=s+1}return r}flatten(e){for(let t of this.children)t.flatten(e)}scanIdentical(t,n){if(!(t instanceof e))return 0;let r=0,[i,a,o,s]=n>0?[0,0,this.children.length,t.children.length]:[this.children.length-1,t.children.length-1,-1,-1];for(;;i+=n,a+=n){if(i==o||a==s)return r;let e=this.children[i],c=t.children[a];if(e!=c)return r+e.scanIdentical(c,n);r+=e.length+1}}static from(t,n=t.reduce((e,t)=>e+t.length+1,-1)){let r=0;for(let e of t)r+=e.lines;if(r<32){let e=[];for(let n of t)n.flatten(e);return new b(e,n)}let i=Math.max(32,r>>5),a=i<<1,o=i>>1,s=[],c=0,l=-1,u=[];function d(t){let n;if(t.lines>a&&t instanceof e)for(let e of t.children)d(e);else t.lines>o&&(c>o||!c)?(f(),s.push(t)):t instanceof b&&c&&(n=u[u.length-1])instanceof b&&t.lines+n.lines<=32?(c+=t.lines,l+=t.length+1,u[u.length-1]=new b(n.text.concat(t.text),n.length+1+t.length)):(c+t.lines>i&&f(),c+=t.lines,l+=t.length+1,u.push(t))}function f(){c!=0&&(s.push(u.length==1?u[0]:e.from(u,l)),l=-1,c=u.length=0)}for(let e of t)d(e);return f(),s.length==1?s[0]:new e(s,n)}};y.empty=new b([``],0);function ee(e){let t=-1;for(let n of e)t+=n.length+1;return t}function te(e,t,n=0,r=1e9){for(let i=0,a=0,o=!0;a<e.length&&i<=r;a++){let s=e[a],c=i+s.length;c>=n&&(c>r&&(s=s.slice(0,r-i)),i<n&&(s=s.slice(n-i)),o?(t[t.length-1]+=s,o=!1):t.push(s)),i=c+1}return t}function S(e,t,n){return te(e,[``],t,n)}var C=class{constructor(e,t=1){this.dir=t,this.done=!1,this.lineBreak=!1,this.value=``,this.nodes=[e],this.offsets=[t>0?1:(e instanceof b?e.text.length:e.children.length)<<1]}nextInner(e,t){for(this.done=this.lineBreak=!1;;){let n=this.nodes.length-1,r=this.nodes[n],i=this.offsets[n],a=i>>1,o=r instanceof b?r.text.length:r.children.length;if(a==(t>0?o:0)){if(n==0)return this.done=!0,this.value=``,this;t>0&&this.offsets[n-1]++,this.nodes.pop(),this.offsets.pop()}else if((i&1)==(t>0?0:1)){if(this.offsets[n]+=t,e==0)return this.lineBreak=!0,this.value=`
|
|
4
4
|
`,this;e--}else if(r instanceof b){let i=r.text[a+(t<0?-1:0)];if(this.offsets[n]+=t,i.length>Math.max(0,e))return this.value=e==0?i:t>0?i.slice(e):i.slice(0,i.length-e),this;e-=i.length}else{let i=r.children[a+(t<0?-1:0)];e>i.length?(e-=i.length,this.offsets[n]+=t):(t<0&&this.offsets[n]--,this.nodes.push(i),this.offsets.push(t>0?1:(i instanceof b?i.text.length:i.children.length)<<1))}}}next(e=0){return e<0&&(this.nextInner(-e,-this.dir),e=this.value.length),this.nextInner(e,this.dir)}},ne=class{constructor(e,t,n){this.value=``,this.done=!1,this.cursor=new C(e,t>n?-1:1),this.pos=t>n?e.length:0,this.from=Math.min(t,n),this.to=Math.max(t,n)}nextInner(e,t){if(t<0?this.pos<=this.from:this.pos>=this.to)return this.value=``,this.done=!0,this;e+=Math.max(0,t<0?this.pos-this.to:this.from-this.pos);let n=t<0?this.pos-this.from:this.to-this.pos;e>n&&(e=n),n-=e;let{value:r}=this.cursor.next(e);return this.pos+=(r.length+e)*t,this.value=r.length<=n?r:t<0?r.slice(r.length-n):r.slice(0,n),this.done=!this.value,this}next(e=0){return e<0?e=Math.max(e,this.from-this.pos):e>0&&(e=Math.min(e,this.to-this.pos)),this.nextInner(e,this.cursor.dir)}get lineBreak(){return this.cursor.lineBreak&&this.value!=``}},re=class{constructor(e){this.inner=e,this.afterBreak=!0,this.value=``,this.done=!1}next(e=0){let{done:t,lineBreak:n,value:r}=this.inner.next(e);return t&&this.afterBreak?(this.value=``,this.afterBreak=!1):t?(this.done=!0,this.value=``):n?this.afterBreak?this.value=``:(this.afterBreak=!0,this.next()):(this.value=r,this.afterBreak=!1),this}get lineBreak(){return!1}};typeof Symbol<`u`&&(y.prototype[Symbol.iterator]=function(){return this.iter()},C.prototype[Symbol.iterator]=ne.prototype[Symbol.iterator]=re.prototype[Symbol.iterator]=function(){return this});var ie=class{constructor(e,t,n,r){this.from=e,this.to=t,this.number=n,this.text=r}get length(){return this.to-this.from}};function w(e,t,n){return t=Math.max(0,Math.min(e.length,t)),[t,Math.max(t,Math.min(e.length,n))]}function T(e,t,n=!0,r=!0){return f(e,t,n,r)}function ae(e){return e>=56320&&e<57344}function oe(e){return e>=55296&&e<56320}function se(e,t){let n=e.charCodeAt(t);if(!oe(n)||t+1==e.length)return n;let r=e.charCodeAt(t+1);return ae(r)?(n-55296<<10)+(r-56320)+65536:n}function ce(e){return e<=65535?String.fromCharCode(e):(e-=65536,String.fromCharCode((e>>10)+55296,(e&1023)+56320))}function le(e){return e<65536?1:2}var ue=/\r\n?|\n/,de=(function(e){return e[e.Simple=0]=`Simple`,e[e.TrackDel=1]=`TrackDel`,e[e.TrackBefore=2]=`TrackBefore`,e[e.TrackAfter=3]=`TrackAfter`,e})(de||={}),fe=class e{constructor(e){this.sections=e}get length(){let e=0;for(let t=0;t<this.sections.length;t+=2)e+=this.sections[t];return e}get newLength(){let e=0;for(let t=0;t<this.sections.length;t+=2){let n=this.sections[t+1];e+=n<0?this.sections[t]:n}return e}get empty(){return this.sections.length==0||this.sections.length==2&&this.sections[1]<0}iterGaps(e){for(let t=0,n=0,r=0;t<this.sections.length;){let i=this.sections[t++],a=this.sections[t++];a<0?(e(n,r,i),r+=i):r+=a,n+=i}}iterChangedRanges(e,t=!1){ge(this,e,t)}get invertedDesc(){let t=[];for(let e=0;e<this.sections.length;){let n=this.sections[e++],r=this.sections[e++];r<0?t.push(n,r):t.push(r,n)}return new e(t)}composeDesc(e){return this.empty?e:e.empty?this:ve(this,e)}mapDesc(e,t=!1){return e.empty?this:_e(this,e,t)}mapPos(e,t=-1,n=de.Simple){let r=0,i=0;for(let a=0;a<this.sections.length;){let o=this.sections[a++],s=this.sections[a++],c=r+o;if(s<0){if(c>e)return i+(e-r);i+=o}else{if(n!=de.Simple&&c>=e&&(n==de.TrackDel&&r<e&&c>e||n==de.TrackBefore&&r<e||n==de.TrackAfter&&c>e))return null;if(c>e||c==e&&t<0&&!o)return e==r||t<0?i:i+s;i+=s}r=c}if(e>r)throw RangeError(`Position ${e} is out of range for changeset of length ${r}`);return i}touchesRange(e,t=e){for(let n=0,r=0;n<this.sections.length&&r<=t;){let i=this.sections[n++],a=this.sections[n++],o=r+i;if(a>=0&&r<=t&&o>=e)return r<e&&o>t?`cover`:!0;r=o}return!1}toString(){let e=``;for(let t=0;t<this.sections.length;){let n=this.sections[t++],r=this.sections[t++];e+=(e?` `:``)+n+(r>=0?`:`+r:``)}return e}toJSON(){return this.sections}static fromJSON(t){if(!Array.isArray(t)||t.length%2||t.some(e=>typeof e!=`number`))throw RangeError(`Invalid JSON representation of ChangeDesc`);return new e(t)}static create(t){return new e(t)}},pe=class e extends fe{constructor(e,t){super(e),this.inserted=t}apply(e){if(this.length!=e.length)throw RangeError(`Applying change set to a document with the wrong length`);return ge(this,(t,n,r,i,a)=>e=e.replace(r,r+(n-t),a),!1),e}mapDesc(e,t=!1){return _e(this,e,t,!0)}invert(t){let n=this.sections.slice(),r=[];for(let e=0,i=0;e<n.length;e+=2){let a=n[e],o=n[e+1];if(o>=0){n[e]=o,n[e+1]=a;let s=e>>1;for(;r.length<s;)r.push(y.empty);r.push(a?t.slice(i,i+a):y.empty)}i+=a}return new e(n,r)}compose(e){return this.empty?e:e.empty?this:ve(this,e,!0)}map(e,t=!1){return e.empty?this:_e(this,e,t,!0)}iterChanges(e,t=!1){ge(this,e,t)}get desc(){return fe.create(this.sections)}filter(t){let n=[],r=[],i=[],a=new ye(this);done:for(let e=0,o=0;;){let s=e==t.length?1e9:t[e++];for(;o<s||o==s&&a.len==0;){if(a.done)break done;let e=Math.min(a.len,s-o);me(i,e,-1);let t=a.ins==-1?-1:a.off==0?a.ins:0;me(n,e,t),t>0&&he(r,n,a.text),a.forward(e),o+=e}let c=t[e++];for(;o<c;){if(a.done)break done;let e=Math.min(a.len,c-o);me(n,e,-1),me(i,e,a.ins==-1?-1:a.off==0?a.ins:0),a.forward(e),o+=e}}return{changes:new e(n,r),filtered:fe.create(i)}}toJSON(){let e=[];for(let t=0;t<this.sections.length;t+=2){let n=this.sections[t],r=this.sections[t+1];r<0?e.push(n):r==0?e.push([n]):e.push([n].concat(this.inserted[t>>1].toJSON()))}return e}static of(t,n,r){let i=[],a=[],o=0,s=null;function c(t=!1){if(!t&&!i.length)return;o<n&&me(i,n-o,-1);let r=new e(i,a);s=s?s.compose(r.map(s)):r,i=[],a=[],o=0}function l(t){if(Array.isArray(t))for(let e of t)l(e);else if(t instanceof e){if(t.length!=n)throw RangeError(`Mismatched change set length (got ${t.length}, expected ${n})`);c(),s=s?s.compose(t.map(s)):t}else{let{from:e,to:s=e,insert:l}=t;if(e>s||e<0||s>n)throw RangeError(`Invalid change range ${e} to ${s} (in doc of length ${n})`);let u=l?typeof l==`string`?y.of(l.split(r||ue)):l:y.empty,d=u.length;if(e==s&&d==0)return;e<o&&c(),e>o&&me(i,e-o,-1),me(i,s-e,d),he(a,i,u),o=s}}return l(t),c(!s),s}static empty(t){return new e(t?[t,-1]:[],[])}static fromJSON(t){if(!Array.isArray(t))throw RangeError(`Invalid JSON representation of ChangeSet`);let n=[],r=[];for(let e=0;e<t.length;e++){let i=t[e];if(typeof i==`number`)n.push(i,-1);else if(!Array.isArray(i)||typeof i[0]!=`number`||i.some((e,t)=>t&&typeof e!=`string`))throw RangeError(`Invalid JSON representation of ChangeSet`);else if(i.length==1)n.push(i[0],0);else{for(;r.length<e;)r.push(y.empty);r[e]=y.of(i.slice(1)),n.push(i[0],r[e].length)}}return new e(n,r)}static createSet(t,n){return new e(t,n)}};function me(e,t,n,r=!1){if(t==0&&n<=0)return;let i=e.length-2;i>=0&&n<=0&&n==e[i+1]?e[i]+=t:i>=0&&t==0&&e[i]==0?e[i+1]+=n:r?(e[i]+=t,e[i+1]+=n):e.push(t,n)}function he(e,t,n){if(n.length==0)return;let r=t.length-2>>1;if(r<e.length)e[e.length-1]=e[e.length-1].append(n);else{for(;e.length<r;)e.push(y.empty);e.push(n)}}function ge(e,t,n){let r=e.inserted;for(let i=0,a=0,o=0;o<e.sections.length;){let s=e.sections[o++],c=e.sections[o++];if(c<0)i+=s,a+=s;else{let l=i,u=a,d=y.empty;for(;l+=s,u+=c,c&&r&&(d=d.append(r[o-2>>1])),!(n||o==e.sections.length||e.sections[o+1]<0);)s=e.sections[o++],c=e.sections[o++];t(i,l,a,u,d),i=l,a=u}}}function _e(e,t,n,r=!1){let i=[],a=r?[]:null,o=new ye(e),s=new ye(t);for(let e=-1;;)if(o.done&&s.len||s.done&&o.len)throw Error(`Mismatched change set lengths`);else if(o.ins==-1&&s.ins==-1){let e=Math.min(o.len,s.len);me(i,e,-1),o.forward(e),s.forward(e)}else if(s.ins>=0&&(o.ins<0||e==o.i||o.off==0&&(s.len<o.len||s.len==o.len&&!n))){let t=s.len;for(me(i,s.ins,-1);t;){let n=Math.min(o.len,t);o.ins>=0&&e<o.i&&o.len<=n&&(me(i,0,o.ins),a&&he(a,i,o.text),e=o.i),o.forward(n),t-=n}s.next()}else if(o.ins>=0){let t=0,n=o.len;for(;n;)if(s.ins==-1){let e=Math.min(n,s.len);t+=e,n-=e,s.forward(e)}else if(s.ins==0&&s.len<n)n-=s.len,s.next();else break;me(i,t,e<o.i?o.ins:0),a&&e<o.i&&he(a,i,o.text),e=o.i,o.forward(o.len-n)}else if(o.done&&s.done)return a?pe.createSet(i,a):fe.create(i);else throw Error(`Mismatched change set lengths`)}function ve(e,t,n=!1){let r=[],i=n?[]:null,a=new ye(e),o=new ye(t);for(let e=!1;;)if(a.done&&o.done)return i?pe.createSet(r,i):fe.create(r);else if(a.ins==0)me(r,a.len,0,e),a.next();else if(o.len==0&&!o.done)me(r,0,o.ins,e),i&&he(i,r,o.text),o.next();else if(a.done||o.done)throw Error(`Mismatched change set lengths`);else{let t=Math.min(a.len2,o.len),n=r.length;if(a.ins==-1){let n=o.ins==-1?-1:o.off?0:o.ins;me(r,t,n,e),i&&n&&he(i,r,o.text)}else o.ins==-1?(me(r,a.off?0:a.len,t,e),i&&he(i,r,a.textBit(t))):(me(r,a.off?0:a.len,o.off?0:o.ins,e),i&&!o.off&&he(i,r,o.text));e=(a.ins>t||o.ins>=0&&o.len>t)&&(e||r.length>n),a.forward2(t),o.forward(t)}}var ye=class{constructor(e){this.set=e,this.i=0,this.next()}next(){let{sections:e}=this.set;this.i<e.length?(this.len=e[this.i++],this.ins=e[this.i++]):(this.len=0,this.ins=-2),this.off=0}get done(){return this.ins==-2}get len2(){return this.ins<0?this.len:this.ins}get text(){let{inserted:e}=this.set,t=this.i-2>>1;return t>=e.length?y.empty:e[t]}textBit(e){let{inserted:t}=this.set,n=this.i-2>>1;return n>=t.length&&!e?y.empty:t[n].slice(this.off,e==null?void 0:this.off+e)}forward(e){e==this.len?this.next():(this.len-=e,this.off+=e)}forward2(e){this.ins==-1?this.forward(e):e==this.ins?this.next():(this.ins-=e,this.off+=e)}},be=class e{constructor(e,t,n){this.from=e,this.to=t,this.flags=n}get anchor(){return this.flags&32?this.to:this.from}get head(){return this.flags&32?this.from:this.to}get empty(){return this.from==this.to}get assoc(){return this.flags&8?-1:this.flags&16?1:0}get bidiLevel(){let e=this.flags&7;return e==7?null:e}get goalColumn(){let e=this.flags>>6;return e==16777215?void 0:e}map(t,n=-1){let r,i;return this.empty?r=i=t.mapPos(this.from,n):(r=t.mapPos(this.from,1),i=t.mapPos(this.to,-1)),r==this.from&&i==this.to?this:new e(r,i,this.flags)}extend(e,t=e,n=0){if(e<=this.anchor&&t>=this.anchor)return E.range(e,t,void 0,void 0,n);let r=Math.abs(e-this.anchor)>Math.abs(t-this.anchor)?e:t;return E.range(this.anchor,r,void 0,void 0,n)}eq(e,t=!1){return this.anchor==e.anchor&&this.head==e.head&&this.goalColumn==e.goalColumn&&(!t||!this.empty||this.assoc==e.assoc)}toJSON(){return{anchor:this.anchor,head:this.head}}static fromJSON(e){if(!e||typeof e.anchor!=`number`||typeof e.head!=`number`)throw RangeError(`Invalid JSON representation for SelectionRange`);return E.range(e.anchor,e.head)}static create(t,n,r){return new e(t,n,r)}},E=class e{constructor(e,t){this.ranges=e,this.mainIndex=t}map(t,n=-1){return t.empty?this:e.create(this.ranges.map(e=>e.map(t,n)),this.mainIndex)}eq(e,t=!1){if(this.ranges.length!=e.ranges.length||this.mainIndex!=e.mainIndex)return!1;for(let n=0;n<this.ranges.length;n++)if(!this.ranges[n].eq(e.ranges[n],t))return!1;return!0}get main(){return this.ranges[this.mainIndex]}asSingle(){return this.ranges.length==1?this:new e([this.main],0)}addRange(t,n=!0){return e.create([t].concat(this.ranges),n?0:this.mainIndex+1)}replaceRange(t,n=this.mainIndex){let r=this.ranges.slice();return r[n]=t,e.create(r,this.mainIndex)}toJSON(){return{ranges:this.ranges.map(e=>e.toJSON()),main:this.mainIndex}}static fromJSON(t){if(!t||!Array.isArray(t.ranges)||typeof t.main!=`number`||t.main>=t.ranges.length)throw RangeError(`Invalid JSON representation for EditorSelection`);return new e(t.ranges.map(e=>be.fromJSON(e)),t.main)}static single(t,n=t){return new e([e.range(t,n)],0)}static create(t,n=0){if(t.length==0)throw RangeError(`A selection needs at least one range`);for(let r=0,i=0;i<t.length;i++){let a=t[i];if(a.empty?a.from<=r:a.from<r)return e.normalized(t.slice(),n);r=a.to}return new e(t,n)}static cursor(e,t=0,n,r){return be.create(e,e,(t==0?0:t<0?8:16)|(n==null?7:Math.min(6,n))|(r??16777215)<<6)}static range(e,t,n,r,i){let a=(n??16777215)<<6|(r==null?7:Math.min(6,r));return!i&&e!=t&&(i=t<e?1:-1),t<e?be.create(t,e,48|a):be.create(e,t,(i?i<0?8:16:0)|a)}static normalized(t,n=0){let r=t[n];t.sort((e,t)=>e.from-t.from),n=t.indexOf(r);for(let r=1;r<t.length;r++){let i=t[r],a=t[r-1];if(i.empty?i.from<=a.to:i.from<a.to){let o=a.from,s=Math.max(i.to,a.to);r<=n&&n--,t.splice(--r,2,i.anchor>i.head?e.range(s,o):e.range(o,s))}}return new e(t,n)}};function xe(e,t){for(let n of e.ranges)if(n.to>t)throw RangeError(`Selection points outside of document`)}var Se=0,D=class e{constructor(e,t,n,r,i){this.combine=e,this.compareInput=t,this.compare=n,this.isStatic=r,this.id=Se++,this.default=e([]),this.extensions=typeof i==`function`?i(this):i}get reader(){return this}static define(t={}){return new e(t.combine||(e=>e),t.compareInput||((e,t)=>e===t),t.compare||(t.combine?(e,t)=>e===t:Ce),!!t.static,t.enables)}of(e){return new we([],this,0,e)}compute(e,t){if(this.isStatic)throw Error(`Can't compute a static facet`);return new we(e,this,1,t)}computeN(e,t){if(this.isStatic)throw Error(`Can't compute a static facet`);return new we(e,this,2,t)}from(e,t){return t||=e=>e,this.compute([e],n=>t(n.field(e)))}};function Ce(e,t){return e==t||e.length==t.length&&e.every((e,n)=>e===t[n])}var we=class{constructor(e,t,n,r){this.dependencies=e,this.facet=t,this.type=n,this.value=r,this.id=Se++}dynamicSlot(e){let t=this.value,n=this.facet.compareInput,r=this.id,i=e[r]>>1,a=this.type==2,o=!1,s=!1,c=[];for(let t of this.dependencies)t==`doc`?o=!0:t==`selection`?s=!0:(e[t.id]??1)&1||c.push(e[t.id]);return{create(e){return e.values[i]=t(e),1},update(e,r){if(o&&r.docChanged||s&&(r.docChanged||r.selection)||Ee(e,c)){let r=t(e);if(a?!Te(r,e.values[i],n):!n(r,e.values[i]))return e.values[i]=r,1}return 0},reconfigure:(e,o)=>{let s,c=o.config.address[r];if(c!=null){let r=ze(o,c);if(this.dependencies.every(t=>t instanceof D?o.facet(t)===e.facet(t):t instanceof ke?o.field(t,!1)==e.field(t,!1):!0)||(a?Te(s=t(e),r,n):n(s=t(e),r)))return e.values[i]=r,0}else s=t(e);return e.values[i]=s,1}}}};function Te(e,t,n){if(e.length!=t.length)return!1;for(let r=0;r<e.length;r++)if(!n(e[r],t[r]))return!1;return!0}function Ee(e,t){let n=!1;for(let r of t)Re(e,r)&1&&(n=!0);return n}function De(e,t,n){let r=n.map(t=>e[t.id]),i=n.map(e=>e.type),a=r.filter(e=>!(e&1)),o=e[t.id]>>1;function s(e){let n=[];for(let t=0;t<r.length;t++){let a=ze(e,r[t]);if(i[t]==2)for(let e of a)n.push(e);else n.push(a)}return t.combine(n)}return{create(e){for(let t of r)Re(e,t);return e.values[o]=s(e),1},update(e,n){if(!Ee(e,a))return 0;let r=s(e);return t.compare(r,e.values[o])?0:(e.values[o]=r,1)},reconfigure(e,i){let a=Ee(e,r),c=i.config.facets[t.id],l=i.facet(t);if(c&&!a&&Ce(n,c))return e.values[o]=l,0;let u=s(e);return t.compare(u,l)?(e.values[o]=l,0):(e.values[o]=u,1)}}}var Oe=D.define({static:!0}),ke=class e{constructor(e,t,n,r,i){this.id=e,this.createF=t,this.updateF=n,this.compareF=r,this.spec=i,this.provides=void 0}static define(t){let n=new e(Se++,t.create,t.update,t.compare||((e,t)=>e===t),t);return t.provide&&(n.provides=t.provide(n)),n}create(e){return(e.facet(Oe).find(e=>e.field==this)?.create||this.createF)(e)}slot(e){let t=e[this.id]>>1;return{create:e=>(e.values[t]=this.create(e),1),update:(e,n)=>{let r=e.values[t],i=this.updateF(r,n);return this.compareF(r,i)?0:(e.values[t]=i,1)},reconfigure:(e,n)=>{let r=e.facet(Oe),i=n.facet(Oe),a;return(a=r.find(e=>e.field==this))&&a!=i.find(e=>e.field==this)?(e.values[t]=a.create(e),1):n.config.address[this.id]==null?(e.values[t]=this.create(e),1):(e.values[t]=n.field(this),0)}}}init(e){return[this,Oe.of({field:this,create:e})]}get extension(){return this}},Ae={lowest:4,low:3,default:2,high:1,highest:0};function je(e){return t=>new Ne(t,e)}var Me={highest:je(Ae.highest),high:je(Ae.high),default:je(Ae.default),low:je(Ae.low),lowest:je(Ae.lowest)},Ne=class{constructor(e,t){this.inner=e,this.prec=t}},Pe=class e{of(e){return new Fe(this,e)}reconfigure(t){return e.reconfigure.of({compartment:this,extension:t})}get(e){return e.config.compartments.get(this)}},Fe=class{constructor(e,t){this.compartment=e,this.inner=t}},Ie=class e{constructor(e,t,n,r,i,a){for(this.base=e,this.compartments=t,this.dynamicSlots=n,this.address=r,this.staticValues=i,this.facets=a,this.statusTemplate=[];this.statusTemplate.length<n.length;)this.statusTemplate.push(0)}staticFacet(e){let t=this.address[e.id];return t==null?e.default:this.staticValues[t>>1]}static resolve(t,n,r){let i=[],a=Object.create(null),o=new Map;for(let e of Le(t,n,o))e instanceof ke?i.push(e):(a[e.facet.id]||(a[e.facet.id]=[])).push(e);let s=Object.create(null),c=[],l=[];for(let e of i)s[e.id]=l.length<<1,l.push(t=>e.slot(t));let u=r?.config.facets;for(let e in a){let t=a[e],n=t[0].facet,i=u&&u[e]||[];if(t.every(e=>e.type==0))if(s[n.id]=c.length<<1|1,Ce(i,t))c.push(r.facet(n));else{let e=n.combine(t.map(e=>e.value));c.push(r&&n.compare(e,r.facet(n))?r.facet(n):e)}else{for(let e of t)e.type==0?(s[e.id]=c.length<<1|1,c.push(e.value)):(s[e.id]=l.length<<1,l.push(t=>e.dynamicSlot(t)));s[n.id]=l.length<<1,l.push(e=>De(e,n,t))}}return new e(t,o,l.map(e=>e(s)),s,c,a)}};function Le(e,t,n){let r=[[],[],[],[],[]],i=new Map;function a(e,o){let s=i.get(e);if(s!=null){if(s<=o)return;let t=r[s].indexOf(e);t>-1&&r[s].splice(t,1),e instanceof Fe&&n.delete(e.compartment)}if(i.set(e,o),Array.isArray(e))for(let t of e)a(t,o);else if(e instanceof Fe){if(n.has(e.compartment))throw RangeError(`Duplicate use of compartment in extensions`);let r=t.get(e.compartment)||e.inner;n.set(e.compartment,r),a(r,o)}else if(e instanceof Ne)a(e.inner,e.prec);else if(e instanceof ke)r[o].push(e),e.provides&&a(e.provides,o);else if(e instanceof we)r[o].push(e),e.facet.extensions&&a(e.facet.extensions,Ae.default);else{let t=e.extension;if(!t)throw Error(`Unrecognized extension value in extension set (${e}). This sometimes happens because multiple instances of @codemirror/state are loaded, breaking instanceof checks.`);a(t,o)}}return a(e,Ae.default),r.reduce((e,t)=>e.concat(t))}function Re(e,t){if(t&1)return 2;let n=t>>1,r=e.status[n];if(r==4)throw Error(`Cyclic dependency between fields and/or facets`);if(r&2)return r;e.status[n]=4;let i=e.computeSlot(e,e.config.dynamicSlots[n]);return e.status[n]=2|i}function ze(e,t){return t&1?e.config.staticValues[t>>1]:e.values[t>>1]}var Be=D.define(),Ve=D.define({combine:e=>e.some(e=>e),static:!0}),He=D.define({combine:e=>e.length?e[0]:void 0,static:!0}),Ue=D.define(),We=D.define(),Ge=D.define(),Ke=D.define({combine:e=>e.length?e[0]:!1}),qe=class{constructor(e,t){this.type=e,this.value=t}static define(){return new Je}},Je=class{of(e){return new qe(this,e)}},Ye=class{constructor(e){this.map=e}of(e){return new O(this,e)}},O=class e{constructor(e,t){this.type=e,this.value=t}map(t){let n=this.type.map(this.value,t);return n===void 0?void 0:n==this.value?this:new e(this.type,n)}is(e){return this.type==e}static define(e={}){return new Ye(e.map||(e=>e))}static mapEffects(e,t){if(!e.length)return e;let n=[];for(let r of e){let e=r.map(t);e&&n.push(e)}return n}};O.reconfigure=O.define(),O.appendConfig=O.define();var Xe=class e{constructor(t,n,r,i,a,o){this.startState=t,this.changes=n,this.selection=r,this.effects=i,this.annotations=a,this.scrollIntoView=o,this._doc=null,this._state=null,r&&xe(r,n.newLength),a.some(t=>t.type==e.time)||(this.annotations=a.concat(e.time.of(Date.now())))}static create(t,n,r,i,a,o){return new e(t,n,r,i,a,o)}get newDoc(){return this._doc||=this.changes.apply(this.startState.doc)}get newSelection(){return this.selection||this.startState.selection.map(this.changes)}get state(){return this._state||this.startState.applyTransaction(this),this._state}annotation(e){for(let t of this.annotations)if(t.type==e)return t.value}get docChanged(){return!this.changes.empty}get reconfigured(){return this.startState.config!=this.state.config}isUserEvent(t){let n=this.annotation(e.userEvent);return!!(n&&(n==t||n.length>t.length&&n.slice(0,t.length)==t&&n[t.length]==`.`))}};Xe.time=qe.define(),Xe.userEvent=qe.define(),Xe.addToHistory=qe.define(),Xe.remote=qe.define();function Ze(e,t){let n=[];for(let r=0,i=0;;){let a,o;if(r<e.length&&(i==t.length||t[i]>=e[r]))a=e[r++],o=e[r++];else if(i<t.length)a=t[i++],o=t[i++];else return n;!n.length||n[n.length-1]<a?n.push(a,o):n[n.length-1]<o&&(n[n.length-1]=o)}}function Qe(e,t,n){let r,i,a;return n?(r=t.changes,i=pe.empty(t.changes.length),a=e.changes.compose(t.changes)):(r=t.changes.map(e.changes),i=e.changes.mapDesc(t.changes,!0),a=e.changes.compose(r)),{changes:a,selection:t.selection?t.selection.map(i):e.selection?.map(r),effects:O.mapEffects(e.effects,r).concat(O.mapEffects(t.effects,i)),annotations:e.annotations.length?e.annotations.concat(t.annotations):t.annotations,scrollIntoView:e.scrollIntoView||t.scrollIntoView}}function $e(e,t,n){let r=t.selection,i=it(t.annotations);return t.userEvent&&(i=i.concat(Xe.userEvent.of(t.userEvent))),{changes:t.changes instanceof pe?t.changes:pe.of(t.changes||[],n,e.facet(He)),selection:r&&(r instanceof E?r:E.single(r.anchor,r.head)),effects:it(t.effects),annotations:i,scrollIntoView:!!t.scrollIntoView}}function et(e,t,n){let r=$e(e,t.length?t[0]:{},e.doc.length);t.length&&t[0].filter===!1&&(n=!1);for(let i=1;i<t.length;i++){t[i].filter===!1&&(n=!1);let a=!!t[i].sequential;r=Qe(r,$e(e,t[i],a?r.changes.newLength:e.doc.length),a)}let i=Xe.create(e,r.changes,r.selection,r.effects,r.annotations,r.scrollIntoView);return nt(n?tt(i):i)}function tt(e){let t=e.startState,n=!0;for(let r of t.facet(Ue)){let t=r(e);if(t===!1){n=!1;break}Array.isArray(t)&&(n=n===!0?t:Ze(n,t))}if(n!==!0){let r,i;if(n===!1)i=e.changes.invertedDesc,r=pe.empty(t.doc.length);else{let t=e.changes.filter(n);r=t.changes,i=t.filtered.mapDesc(t.changes).invertedDesc}e=Xe.create(t,r,e.selection&&e.selection.map(i),O.mapEffects(e.effects,i),e.annotations,e.scrollIntoView)}let r=t.facet(We);for(let n=r.length-1;n>=0;n--){let i=r[n](e);e=i instanceof Xe?i:Array.isArray(i)&&i.length==1&&i[0]instanceof Xe?i[0]:et(t,it(i),!1)}return e}function nt(e){let t=e.startState,n=t.facet(Ge),r=e;for(let i=n.length-1;i>=0;i--){let a=n[i](e);a&&Object.keys(a).length&&(r=Qe(r,$e(t,a,e.changes.newLength),!0))}return r==e?e:Xe.create(t,e.changes,e.selection,r.effects,r.annotations,r.scrollIntoView)}var rt=[];function it(e){return e==null?rt:Array.isArray(e)?e:[e]}var k=(function(e){return e[e.Word=0]=`Word`,e[e.Space=1]=`Space`,e[e.Other=2]=`Other`,e})(k||={}),at=/[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/,ot;try{ot=RegExp(`[\\p{Alphabetic}\\p{Number}_]`,`u`)}catch{}function st(e){if(ot)return ot.test(e);for(let t=0;t<e.length;t++){let n=e[t];if(/\w/.test(n)||n>``&&(n.toUpperCase()!=n.toLowerCase()||at.test(n)))return!0}return!1}function ct(e){return t=>{if(!/\S/.test(t))return k.Space;if(st(t))return k.Word;for(let n=0;n<e.length;n++)if(t.indexOf(e[n])>-1)return k.Word;return k.Other}}var A=class e{constructor(e,t,n,r,i,a){this.config=e,this.doc=t,this.selection=n,this.values=r,this.status=e.statusTemplate.slice(),this.computeSlot=i,a&&(a._state=this);for(let e=0;e<this.config.dynamicSlots.length;e++)Re(this,e<<1);this.computeSlot=null}field(e,t=!0){let n=this.config.address[e.id];if(n==null){if(t)throw RangeError(`Field is not present in this state`);return}return Re(this,n),ze(this,n)}update(...e){return et(this,e,!0)}applyTransaction(t){let n=this.config,{base:r,compartments:i}=n;for(let e of t.effects)e.is(Pe.reconfigure)?(n&&=(i=new Map,n.compartments.forEach((e,t)=>i.set(t,e)),null),i.set(e.value.compartment,e.value.extension)):e.is(O.reconfigure)?(n=null,r=e.value):e.is(O.appendConfig)&&(n=null,r=it(r).concat(e.value));let a;n?a=t.startState.values.slice():(n=Ie.resolve(r,i,this),a=new e(n,this.doc,this.selection,n.dynamicSlots.map(()=>null),(e,t)=>t.reconfigure(e,this),null).values);let o=t.startState.facet(Ve)?t.newSelection:t.newSelection.asSingle();new e(n,t.newDoc,o,a,(e,n)=>n.update(e,t),t)}replaceSelection(e){return typeof e==`string`&&(e=this.toText(e)),this.changeByRange(t=>({changes:{from:t.from,to:t.to,insert:e},range:E.cursor(t.from+e.length)}))}changeByRange(e){let t=this.selection,n=e(t.ranges[0]),r=this.changes(n.changes),i=[n.range],a=it(n.effects);for(let n=1;n<t.ranges.length;n++){let o=e(t.ranges[n]),s=this.changes(o.changes),c=s.map(r);for(let e=0;e<n;e++)i[e]=i[e].map(c);let l=r.mapDesc(s,!0);i.push(o.range.map(l)),r=r.compose(c),a=O.mapEffects(a,c).concat(O.mapEffects(it(o.effects),l))}return{changes:r,selection:E.create(i,t.mainIndex),effects:a}}changes(t=[]){return t instanceof pe?t:pe.of(t,this.doc.length,this.facet(e.lineSeparator))}toText(t){return y.of(t.split(this.facet(e.lineSeparator)||ue))}sliceDoc(e=0,t=this.doc.length){return this.doc.sliceString(e,t,this.lineBreak)}facet(e){let t=this.config.address[e.id];return t==null?e.default:(Re(this,t),ze(this,t))}toJSON(e){let t={doc:this.sliceDoc(),selection:this.selection.toJSON()};if(e)for(let n in e){let r=e[n];r instanceof ke&&this.config.address[r.id]!=null&&(t[n]=r.spec.toJSON(this.field(e[n]),this))}return t}static fromJSON(t,n={},r){if(!t||typeof t.doc!=`string`)throw RangeError(`Invalid JSON representation for EditorState`);let i=[];if(r){for(let e in r)if(Object.prototype.hasOwnProperty.call(t,e)){let n=r[e],a=t[e];i.push(n.init(e=>n.spec.fromJSON(a,e)))}}return e.create({doc:t.doc,selection:E.fromJSON(t.selection),extensions:n.extensions?i.concat([n.extensions]):i})}static create(t={}){let n=Ie.resolve(t.extensions||[],new Map),r=t.doc instanceof y?t.doc:y.of((t.doc||``).split(n.staticFacet(e.lineSeparator)||ue)),i=t.selection?t.selection instanceof E?t.selection:E.single(t.selection.anchor,t.selection.head):E.single(0);return xe(i,r.length),n.staticFacet(Ve)||(i=i.asSingle()),new e(n,r,i,n.dynamicSlots.map(()=>null),(e,t)=>t.create(e),null)}get tabSize(){return this.facet(e.tabSize)}get lineBreak(){return this.facet(e.lineSeparator)||`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{C as e,_ as t,b as n,v as r,x as i,y as a}from"./index-
|
|
1
|
+
import{C as e,_ as t,b as n,v as r,x as i,y as a}from"./index-BfKJexXk.js";import{CodeEditor as o}from"./CodeEditor-CPjl_-RF.js";var s=n(`text-wrap`,[[`path`,{d:`m16 16-3 3 3 3`,key:`117b85`}],[`path`,{d:`M3 12h14.5a1 1 0 0 1 0 7H13`,key:`18xa6z`}],[`path`,{d:`M3 19h6`,key:`1ygdsz`}],[`path`,{d:`M3 5h18`,key:`1u36vt`}]]),c=e(i(),1),l=a();function u(){try{if(localStorage.getItem(`loopat:editor:wordWrap`)===`0`)return!1}catch{}return!0}function d({loopId:e,path:n,onSelectionChange:i}){let[a,d]=(0,c.useState)(``),[f,p]=(0,c.useState)(``),[m,h]=(0,c.useState)(!1),[g,_]=(0,c.useState)(!1),[v,y]=(0,c.useState)(u);(0,c.useEffect)(()=>{if(!n){d(``),p(``);return}h(!0),t(e,n).then(e=>{let t=e?.content??``;d(t),p(t)}).finally(()=>h(!1))},[e,n]);let b=n&&f!==a,x=async()=>{if(!(!n||g)){_(!0);try{await r(e,n,f)&&d(f)}finally{_(!1)}}};return n?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(`div`,{className:`flex-1 min-h-0 relative`,onKeyDown:e=>{(e.metaKey||e.ctrlKey)&&e.key===`s`&&(e.preventDefault(),x())},children:m?(0,l.jsx)(`div`,{className:`h-full w-full flex items-center justify-center text-[12px] text-gray-400`,children:`loading…`}):(0,l.jsx)(o,{path:n,value:f,onChange:p,wordWrap:v,onSelectionChange:i})}),(0,l.jsxs)(`div`,{className:`border-t border-gray-200 px-3 py-1.5 text-[11px] text-gray-500 flex items-center gap-3`,children:[(0,l.jsx)(`span`,{className:`truncate`,children:n}),b&&(0,l.jsx)(`button`,{onClick:x,className:`text-orange-600 hover:underline`,title:`ctrl/⌘+S`,children:g?`saving…`:`unsaved · save`}),(0,l.jsx)(`span`,{className:`flex-1`}),(0,l.jsx)(`button`,{onClick:()=>{let e=!v;y(e);try{localStorage.setItem(`loopat:editor:wordWrap`,e?`1`:`0`)}catch{}},className:`flex items-center gap-1 hover:text-gray-700 transition-colors ${v?`text-gray-500`:`text-gray-300`}`,title:v?`word wrap: on`:`word wrap: off`,children:(0,l.jsx)(s,{size:13})}),(0,l.jsx)(`span`,{children:`utf-8 · LF`})]})]}):(0,l.jsx)(`div`,{className:`flex-1 min-h-0 flex items-center justify-center text-[13px] text-gray-500 px-8 text-center`,children:`没打开文件 · 在 ▤ workdir 里点一个`})}export{d as Editor};
|