loopat 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -10
- package/package.json +1 -1
- package/server/src/bootstrap.ts +1 -1
- package/server/src/index.ts +27 -3
- package/server/src/kanban.ts +17 -3
- package/server/src/loops.ts +86 -7
- package/web/dist/assets/{CodeEditor-BGODueTo.js → CodeEditor-CamHv98h.js} +1 -1
- package/web/dist/assets/{Editor-DMS25Vve.js → Editor-CTMAVfli.js} +1 -1
- package/web/dist/assets/{Markdown-CnHbW7WK.js → Markdown-DnH11crU.js} +1 -1
- package/web/dist/assets/{MilkdownEditor-nqo9_0v5.js → MilkdownEditor-lhueImS3.js} +1 -1
- package/web/dist/assets/{Terminal-CYWvxYam.js → Terminal-CT8pf5uA.js} +1 -1
- package/web/dist/assets/{index-DM5eO-Tv.js → index-jjZzoDb1.js} +64 -64
- package/web/dist/index.html +1 -1
package/README.md
CHANGED
|
@@ -85,19 +85,30 @@ sessions or teammates.
|
|
|
85
85
|
## Quick start
|
|
86
86
|
|
|
87
87
|
```sh
|
|
88
|
-
|
|
89
|
-
cd loopat && bun install
|
|
90
|
-
bun run dev
|
|
88
|
+
npx loopat
|
|
91
89
|
```
|
|
92
90
|
|
|
93
|
-
Open <http://localhost:
|
|
91
|
+
Open <http://localhost:10001>. The first run bootstraps `~/.loopat/`,
|
|
94
92
|
prints a checklist, and prompts you to set your API key in
|
|
95
93
|
`~/.loopat/config.json`. Restart — done.
|
|
96
94
|
|
|
97
|
-
> **Needs:**
|
|
98
|
-
>
|
|
99
|
-
>
|
|
100
|
-
> the
|
|
95
|
+
> **Needs:** [Node][node] to launch — the [Bun][bun] runtime is fetched
|
|
96
|
+
> automatically, so you don't install it yourself. The terminal / chat
|
|
97
|
+
> sandbox additionally needs a Linux host with [podman][podman]. Change
|
|
98
|
+
> the port with `PORT=8080 npx loopat`. macOS / Windows is via Docker
|
|
99
|
+
> (see below).
|
|
100
|
+
|
|
101
|
+
### From source (for development)
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
git clone https://github.com/simpx/loopat.git
|
|
105
|
+
cd loopat && bun install
|
|
106
|
+
bun run dev
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
> Needs [bun][bun] + [bubblewrap][bwrap] + [mise][mise] on the host. For
|
|
110
|
+
> team setups with shared knowledge/notes git repos and full bootstrap
|
|
111
|
+
> details, see the [installation guide](docs/install.md).
|
|
101
112
|
|
|
102
113
|
### Setup guides
|
|
103
114
|
|
|
@@ -113,6 +124,8 @@ Loopat splits configuration along role lines — read whichever applies:
|
|
|
113
124
|
[bwrap]: https://github.com/containers/bubblewrap
|
|
114
125
|
[mise]: https://mise.jdx.dev/
|
|
115
126
|
[bun]: https://bun.sh/
|
|
127
|
+
[node]: https://nodejs.org/
|
|
128
|
+
[podman]: https://podman.io/
|
|
116
129
|
|
|
117
130
|
## Deployment
|
|
118
131
|
|
|
@@ -122,7 +135,7 @@ Loopat splits configuration along role lines — read whichever applies:
|
|
|
122
135
|
docker compose up -d
|
|
123
136
|
```
|
|
124
137
|
|
|
125
|
-
Open <http://localhost:
|
|
138
|
+
Open <http://localhost:20001> (note: **20001**, not 10001 — the host
|
|
126
139
|
port is remapped to avoid collision with a local dev server; see
|
|
127
140
|
[`docker-compose.yml`](docker-compose.yml)). Workspace persists in
|
|
128
141
|
the `loopat-data` volume. Needs `SYS_ADMIN` + unconfined AppArmor for
|
|
@@ -132,7 +145,7 @@ bwrap mount namespaces.
|
|
|
132
145
|
|
|
133
146
|
```sh
|
|
134
147
|
bun run build # installs deps + builds frontend → web/dist/
|
|
135
|
-
PORT=
|
|
148
|
+
PORT=10001 bun run server/src/index.ts
|
|
136
149
|
```
|
|
137
150
|
|
|
138
151
|
Single Hono process serves API + static SPA + websocket on one port.
|
package/package.json
CHANGED
package/server/src/bootstrap.ts
CHANGED
|
@@ -106,7 +106,7 @@ export async function printBootstrapBanner(cfg: WorkspaceConfig) {
|
|
|
106
106
|
console.log(bar)
|
|
107
107
|
const blockers = checks.filter((c) => !c.ok)
|
|
108
108
|
if (blockers.length === 0) {
|
|
109
|
-
console.log(` ready. open http://localhost:${process.env.PORT ??
|
|
109
|
+
console.log(` ready. open http://localhost:${process.env.PORT ?? 10001}\n`)
|
|
110
110
|
} else {
|
|
111
111
|
console.log(` ${blockers.length} thing(s) to fix before chat will work — see hints above.\n`)
|
|
112
112
|
}
|
package/server/src/index.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { createBunWebSocket } from "hono/bun"
|
|
|
4
4
|
import { existsSync } from "node:fs"
|
|
5
5
|
import { execSync, execFile } from "node:child_process"
|
|
6
6
|
import { promisify } from "node:util"
|
|
7
|
-
import { listLoops, createLoop, getLoop, loopExists, patchLoopMeta, backfillAllMounts, ensureWorkspaceDirs, provisionUserPersonal, importPersonalFromRepo, setupPersonalViaProvider, listPersonalReposViaProvider, authenticateViaProvider, providerTokenHelp, isPersonalFresh, ensureUiNotesWorktree, syncUiNotes, inspectPersonalDirty, syncPersonalToRemote, deletePersonalVault, pullPersonalFromRemote, pushPersonalToRemote, ensureContextMounts, effectiveDriver, isDriver, distillLoop, inspectRepoSync, pullRepoFromRemote, pushRepoToRemote } from "./loops"
|
|
7
|
+
import { listLoops, createLoop, getLoop, loopExists, patchLoopMeta, backfillAllMounts, ensureWorkspaceDirs, provisionUserPersonal, importPersonalFromRepo, setupPersonalViaProvider, listPersonalReposViaProvider, authenticateViaProvider, providerTokenHelp, isPersonalFresh, ensureUiNotesWorktree, syncUiNotes, ffUpdateUiNotes, notesBehind, inspectPersonalDirty, syncPersonalToRemote, deletePersonalVault, pullPersonalFromRemote, pushPersonalToRemote, ensureContextMounts, effectiveDriver, isDriver, distillLoop, inspectRepoSync, pullRepoFromRemote, pushRepoToRemote } from "./loops"
|
|
8
8
|
import { getEphemeralHostPort } from "./podman"
|
|
9
9
|
import { getOnboardingStatus, startOnboardingLoop, markOnboardingDone } from "./onboarding"
|
|
10
10
|
import { startMcpAuth, completeMcpAuth, probeOAuthSupport, evictOAuthProbe, parseBearerEnvName, type OAuthSupport } from "./mcp-oauth"
|
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
loopsDir,
|
|
54
54
|
} from "./paths"
|
|
55
55
|
import { loadConfig, loadPersonalConfig, savePersonalConfig, saveWorkspaceConfig, loadTokenUsage, getActiveProvider, readPersonalDiskRaw, savePersonalDisk, describeApiKeyRef, writeVaultEnv, deleteVaultEnv, type ProviderConfig, type ModelEntry } from "./config"
|
|
56
|
-
import { listBoards, createBoard, renameBoard, listKanbanColumns, addCard, toggleCard, deleteCard, moveCard, updateCardMeta, updateCardBlock, reorderCards, createColumn, deleteColumn, readKanbanConfig, saveColumnOrder, setColumnColor, renameColumn, assignDriverForCard, createLoopFromCard, linkLoopToCard } from "./kanban"
|
|
56
|
+
import { listBoards, createBoard, renameBoard, listKanbanColumns, addCard, toggleCard, deleteCard, moveCard, updateCardMeta, updateCardBlock, reorderCards, createColumn, deleteColumn, readKanbanConfig, saveColumnOrder, setColumnColor, renameColumn, assignDriverForCard, createLoopFromCard, linkLoopToCard, kanbanUserCtx } from "./kanban"
|
|
57
57
|
import { printBootstrapBanner } from "./bootstrap"
|
|
58
58
|
import {
|
|
59
59
|
createUser,
|
|
@@ -2233,6 +2233,22 @@ app.post("/api/notes/save", requireAuth, async (c) => {
|
|
|
2233
2233
|
return c.json({ ok: true, message: r.message })
|
|
2234
2234
|
})
|
|
2235
2235
|
|
|
2236
|
+
// How many commits the user's notes are behind origin (drives the "remote
|
|
2237
|
+
// updated" hint). Polled lightly by the client; no push.
|
|
2238
|
+
app.get("/api/notes/behind", requireAuth, async (c) => {
|
|
2239
|
+
const userId = c.get("userId") as string
|
|
2240
|
+
return c.json({ behind: await notesBehind(userId) })
|
|
2241
|
+
})
|
|
2242
|
+
|
|
2243
|
+
// Refresh = ff-pull origin/main into the user's notes worktree. Diverged (local
|
|
2244
|
+
// unsaved edits) is not an error — the client just keeps its draft.
|
|
2245
|
+
app.post("/api/notes/refresh", requireAuth, async (c) => {
|
|
2246
|
+
const userId = c.get("userId") as string
|
|
2247
|
+
const r = await ffUpdateUiNotes(userId)
|
|
2248
|
+
if (!r.ok) return c.json({ ok: false, diverged: r.diverged ?? false, error: r.error }, r.diverged ? 200 : 400)
|
|
2249
|
+
return c.json({ ok: true })
|
|
2250
|
+
})
|
|
2251
|
+
|
|
2236
2252
|
app.delete("/api/workspace/file", requireAuth, async (c) => {
|
|
2237
2253
|
const userId = c.get("userId") as string
|
|
2238
2254
|
const vault = c.req.query("vault") ?? ""
|
|
@@ -2306,6 +2322,14 @@ app.post("/api/workspace/repo/:name/pull", requireAuth, async (c) => {
|
|
|
2306
2322
|
// ── topics ──
|
|
2307
2323
|
|
|
2308
2324
|
// ── kanban: focus boards (one directory per board, one .md file per column) ──
|
|
2325
|
+
// Kanban edits go through the editing user's notes worktree (a per-user UI
|
|
2326
|
+
// loop): carry the user via AsyncLocalStorage + ensure the worktree exists.
|
|
2327
|
+
// Changes reach origin through the explicit notes save (/api/notes/save).
|
|
2328
|
+
app.use("/api/kanban/*", requireAuth, async (c, next) => {
|
|
2329
|
+
const userId = c.get("userId") as string
|
|
2330
|
+
await ensureUiNotesWorktree(userId)
|
|
2331
|
+
return kanbanUserCtx.run(userId, () => next())
|
|
2332
|
+
})
|
|
2309
2333
|
|
|
2310
2334
|
// Board management
|
|
2311
2335
|
app.get("/api/kanban/boards", requireAuth, async (c) => {
|
|
@@ -3121,7 +3145,7 @@ app.get("*", async (c, next) => {
|
|
|
3121
3145
|
})
|
|
3122
3146
|
})
|
|
3123
3147
|
|
|
3124
|
-
const port = Number(process.env.PORT ??
|
|
3148
|
+
const port = Number(process.env.PORT ?? 10001)
|
|
3125
3149
|
const hostname = process.env.HOST ?? "127.0.0.1"
|
|
3126
3150
|
|
|
3127
3151
|
// Fast, serve-critical init only — keep this short so the port opens quickly.
|
package/server/src/kanban.ts
CHANGED
|
@@ -3,16 +3,30 @@ import { join, basename } from "node:path"
|
|
|
3
3
|
import { existsSync } from "node:fs"
|
|
4
4
|
import { execFile } from "node:child_process"
|
|
5
5
|
import { promisify } from "node:util"
|
|
6
|
-
import {
|
|
6
|
+
import { AsyncLocalStorage } from "node:async_hooks"
|
|
7
|
+
import { uiNotesDir } from "./paths"
|
|
7
8
|
import { createLoop, patchLoopMeta, type LoopMeta } from "./loops"
|
|
8
9
|
|
|
9
10
|
const execFileP = promisify(execFile)
|
|
10
11
|
|
|
12
|
+
// Kanban writes land in the editing user's notes UI-loop worktree (uiNotesDir) —
|
|
13
|
+
// the same per-user worktree as notes files. The user is carried per-request via
|
|
14
|
+
// AsyncLocalStorage, so the existing function signatures stay untouched. Changes
|
|
15
|
+
// reach origin through the explicit notes save (syncUiNotes), like any edit.
|
|
16
|
+
export const kanbanUserCtx = new AsyncLocalStorage<string>()
|
|
17
|
+
function userNotesDir(): string {
|
|
18
|
+
const u = kanbanUserCtx.getStore()
|
|
19
|
+
if (!u) throw new Error("kanban: missing user context")
|
|
20
|
+
return uiNotesDir(u)
|
|
21
|
+
}
|
|
22
|
+
|
|
11
23
|
// Auto-commit kanban writes so loop-worktree pushes don't silently wipe
|
|
12
24
|
// them via the post-receive reset --hard hook. Scoped to focus/ to avoid
|
|
13
25
|
// racing with vaultWrite's own commits on other paths under notes/.
|
|
14
26
|
async function commitFocus(msg: string): Promise<void> {
|
|
15
|
-
|
|
27
|
+
// Commit into the user's worktree so refresh (ff-only) isn't blocked by a
|
|
28
|
+
// dirty tree. Pushed to origin later by the explicit notes save.
|
|
29
|
+
const dir = userNotesDir()
|
|
16
30
|
if (!existsSync(join(dir, ".git"))) return
|
|
17
31
|
const env = {
|
|
18
32
|
...process.env,
|
|
@@ -51,7 +65,7 @@ export type KanbanColumn = {
|
|
|
51
65
|
// ── board path helpers ──
|
|
52
66
|
|
|
53
67
|
function boardsRoot(): string {
|
|
54
|
-
return join(
|
|
68
|
+
return join(userNotesDir(), "focus", "boards")
|
|
55
69
|
}
|
|
56
70
|
|
|
57
71
|
function boardDir(board: string): string {
|
package/server/src/loops.ts
CHANGED
|
@@ -1270,15 +1270,16 @@ export async function ensureUiNotesWorktree(user: string): Promise<void> {
|
|
|
1270
1270
|
export async function syncUiNotes(user: string): Promise<PersonalPushResult> {
|
|
1271
1271
|
const dir = uiNotesDir(user)
|
|
1272
1272
|
await ensureUiNotesWorktree(user)
|
|
1273
|
+
const branch = await remoteDefaultBranch(dir)
|
|
1273
1274
|
const c = await commitLocalChanges(dir, "loopat: edit notes")
|
|
1274
1275
|
if (!c.ok) return { ok: false, error: c.error }
|
|
1275
|
-
const reb = await rebaseOntoOrigin(dir,
|
|
1276
|
+
const reb = await rebaseOntoOrigin(dir, branch)
|
|
1276
1277
|
if (!reb.ok) {
|
|
1277
1278
|
if ("conflict" in reb) return { ok: false, error: "conflict with remote", conflict: true, files: reb.files }
|
|
1278
1279
|
return { ok: false, error: reb.error }
|
|
1279
1280
|
}
|
|
1280
1281
|
try {
|
|
1281
|
-
await execFileP("git", ["-C", dir, "push", "origin",
|
|
1282
|
+
await execFileP("git", ["-C", dir, "push", "origin", `HEAD:${branch}`])
|
|
1282
1283
|
} catch (e: any) {
|
|
1283
1284
|
const stderr = (e?.stderr ?? "").toString().trim()
|
|
1284
1285
|
return { ok: false, error: `push failed: ${stderr || e?.message || e}`, needsPull: true }
|
|
@@ -1286,6 +1287,64 @@ export async function syncUiNotes(user: string): Promise<PersonalPushResult> {
|
|
|
1286
1287
|
return { ok: true, message: c.committed ? "saved & pushed" : "pushed" }
|
|
1287
1288
|
}
|
|
1288
1289
|
|
|
1290
|
+
/**
|
|
1291
|
+
* Refresh = the pull half of the notes UI loop: fetch + ff-only merge
|
|
1292
|
+
* origin/main into the user's worktree. Skips silently if the worktree has
|
|
1293
|
+
* diverged (committed-but-unpushed local edits) — those reconcile on save.
|
|
1294
|
+
*/
|
|
1295
|
+
export async function ffUpdateUiNotes(
|
|
1296
|
+
user: string,
|
|
1297
|
+
): Promise<{ ok: true } | { ok: false; diverged?: boolean; error: string }> {
|
|
1298
|
+
const dir = uiNotesDir(user)
|
|
1299
|
+
await ensureUiNotesWorktree(user)
|
|
1300
|
+
const branch = await remoteDefaultBranch(dir)
|
|
1301
|
+
try {
|
|
1302
|
+
await execFileP("git", ["-C", dir, "fetch", "origin"], {
|
|
1303
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }, timeout: 30_000,
|
|
1304
|
+
})
|
|
1305
|
+
} catch (e: any) {
|
|
1306
|
+
return { ok: false, error: `fetch failed: ${e?.stderr ?? e?.message ?? e}` }
|
|
1307
|
+
}
|
|
1308
|
+
// No upstream yet → nothing to pull.
|
|
1309
|
+
try {
|
|
1310
|
+
await execFileP("git", ["-C", dir, "rev-parse", "--verify", "--quiet", `origin/${branch}`])
|
|
1311
|
+
} catch {
|
|
1312
|
+
return { ok: true }
|
|
1313
|
+
}
|
|
1314
|
+
try {
|
|
1315
|
+
await execFileP("git", ["-C", dir, "merge", "--ff-only", `origin/${branch}`], {
|
|
1316
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
|
|
1317
|
+
})
|
|
1318
|
+
return { ok: true }
|
|
1319
|
+
} catch {
|
|
1320
|
+
// Not ff (local unpushed commits). Leave it; the next save rebases.
|
|
1321
|
+
return { ok: false, diverged: true, error: "diverged — save your edits first" }
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
/**
|
|
1326
|
+
* How many commits the user's notes worktree is behind origin/main (after a
|
|
1327
|
+
* fetch). Drives the "remote updated" hint. 0 = up to date.
|
|
1328
|
+
*/
|
|
1329
|
+
export async function notesBehind(user: string): Promise<number> {
|
|
1330
|
+
const dir = uiNotesDir(user)
|
|
1331
|
+
await ensureUiNotesWorktree(user)
|
|
1332
|
+
const branch = await remoteDefaultBranch(dir)
|
|
1333
|
+
try {
|
|
1334
|
+
await execFileP("git", ["-C", dir, "fetch", "origin"], {
|
|
1335
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }, timeout: 30_000,
|
|
1336
|
+
})
|
|
1337
|
+
} catch {
|
|
1338
|
+
return 0
|
|
1339
|
+
}
|
|
1340
|
+
try {
|
|
1341
|
+
const { stdout } = await execFileP("git", ["-C", dir, "rev-list", "--count", `HEAD..origin/${branch}`])
|
|
1342
|
+
return parseInt(stdout.trim(), 10) || 0
|
|
1343
|
+
} catch {
|
|
1344
|
+
return 0
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1289
1348
|
// ── Generic repo sync (knowledge / notes / repos) ─────────────────────
|
|
1290
1349
|
//
|
|
1291
1350
|
// Distinct from personal sync above: these workspace-level repos use the
|
|
@@ -1599,10 +1658,13 @@ async function ensureContextWorktree(repo: string, path: string, branchName: str
|
|
|
1599
1658
|
|
|
1600
1659
|
// Stale state (old symlink, empty dir, leftover from manual cleanup) → wipe + create.
|
|
1601
1660
|
try { await rm(path, { recursive: true, force: true }) } catch {}
|
|
1602
|
-
|
|
1603
|
-
//
|
|
1661
|
+
try { await execFileP("git", ["-C", repo, "worktree", "prune"]) } catch {}
|
|
1662
|
+
// ① pull (docs/context-flow.md): open the worktree from origin's default
|
|
1663
|
+
// branch so the loop starts from latest consensus, not a stale local HEAD.
|
|
1604
1664
|
const start = await remoteStartPoint(repo)
|
|
1605
|
-
|
|
1665
|
+
// -B (not -b): reset the branch if it lingers from a removed worktree, so a
|
|
1666
|
+
// rebuild always re-opens cleanly from the start point.
|
|
1667
|
+
const args = ["-C", repo, "worktree", "add", "-B", branchName, path]
|
|
1606
1668
|
if (start) args.push(start)
|
|
1607
1669
|
await execFileP("git", args)
|
|
1608
1670
|
}
|
|
@@ -1613,6 +1675,22 @@ async function ensureContextWorktree(repo: string, path: string, branchName: str
|
|
|
1613
1675
|
* loop opens from the latest shared state. Returns null to fall back to local
|
|
1614
1676
|
* HEAD (solo / offline / no remote / no origin/main yet).
|
|
1615
1677
|
*/
|
|
1678
|
+
/** The remote's default branch (origin/HEAD) — e.g. main or master. Falls back
|
|
1679
|
+
* to "main". loopat must NOT assume "main": team repos are often on "master". */
|
|
1680
|
+
async function remoteDefaultBranch(dir: string): Promise<string> {
|
|
1681
|
+
try {
|
|
1682
|
+
const { stdout } = await execFileP("git", ["-C", dir, "symbolic-ref", "--short", "refs/remotes/origin/HEAD"])
|
|
1683
|
+
const b = stdout.trim().replace(/^origin\//, "")
|
|
1684
|
+
if (b) return b
|
|
1685
|
+
} catch {}
|
|
1686
|
+
try {
|
|
1687
|
+
const { stdout } = await execFileP("git", ["-C", dir, "ls-remote", "--symref", "origin", "HEAD"])
|
|
1688
|
+
const m = stdout.match(/ref:\s+refs\/heads\/(\S+)\s+HEAD/)
|
|
1689
|
+
if (m?.[1]) return m[1]
|
|
1690
|
+
} catch {}
|
|
1691
|
+
return "main"
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1616
1694
|
async function remoteStartPoint(repo: string): Promise<string | null> {
|
|
1617
1695
|
try {
|
|
1618
1696
|
await execFileP("git", ["-C", repo, "remote", "get-url", "origin"])
|
|
@@ -1622,9 +1700,10 @@ async function remoteStartPoint(repo: string): Promise<string | null> {
|
|
|
1622
1700
|
try {
|
|
1623
1701
|
await execFileP("git", ["-C", repo, "fetch", "--quiet", "origin"], { timeout: 15_000 })
|
|
1624
1702
|
} catch {}
|
|
1703
|
+
const branch = await remoteDefaultBranch(repo)
|
|
1625
1704
|
try {
|
|
1626
|
-
await execFileP("git", ["-C", repo, "rev-parse", "--verify", "--quiet",
|
|
1627
|
-
return
|
|
1705
|
+
await execFileP("git", ["-C", repo, "rev-parse", "--verify", "--quiet", `origin/${branch}^{commit}`])
|
|
1706
|
+
return `origin/${branch}`
|
|
1628
1707
|
} catch {
|
|
1629
1708
|
return null
|
|
1630
1709
|
}
|
|
@@ -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-jjZzoDb1.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-jjZzoDb1.js";import{CodeEditor as o}from"./CodeEditor-CamHv98h.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};
|