opencode-extended-sidebar 0.2.90 → 0.2.91
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/.oesignore +17 -17
- package/CHANGELOG.md +96 -95
- package/LICENSE +21 -21
- package/README.md +150 -235
- package/oes.json +13 -11
- package/package.json +100 -100
- package/scripts/install-git-hooks.mjs +29 -29
- package/src/pware.oc.core/constants/index.ts +18 -18
- package/src/pware.oc.core/constants/pware.oc.core.constants.eventKind.ts +29 -29
- package/src/pware.oc.core/constants/pware.oc.core.constants.eventName.ts +15 -13
- package/src/pware.oc.core/constants/pware.oc.core.constants.eventType.ts +241 -241
- package/src/pware.oc.core/constants/pware.oc.core.constants.myWork.ts +55 -55
- package/src/pware.oc.core/constants/pware.oc.core.constants.partType.ts +65 -65
- package/src/pware.oc.core/constants/pware.oc.core.constants.phase.ts +58 -58
- package/src/pware.oc.core/constants/pware.oc.core.constants.pulse.ts +63 -63
- package/src/pware.oc.core/constants/pware.oc.core.constants.rowKind.ts +34 -34
- package/src/pware.oc.core/constants/pware.oc.core.constants.status.ts +74 -74
- package/src/pware.oc.core/constants/pware.oc.core.constants.toolName.ts +69 -69
- package/src/pware.oc.core/git/index.ts +7 -7
- package/src/pware.oc.core/git/pware.oc.core.git.ts +237 -237
- package/src/pware.oc.core/git/pware.oc.core.gitignore.ts +87 -87
- package/src/pware.oc.core/index.ts +19 -18
- package/src/pware.oc.core/pware.oc.core.bus.ts +42 -42
- package/src/pware.oc.core/pware.oc.core.cache.ts +48 -48
- package/src/pware.oc.core/pware.oc.core.clipboard.ts +97 -97
- package/src/pware.oc.core/pware.oc.core.debug.ts +280 -280
- package/src/pware.oc.core/pware.oc.core.events.ts +79 -79
- package/src/pware.oc.core/pware.oc.core.glyph.ts +135 -135
- package/src/pware.oc.core/pware.oc.core.layout.ts +234 -234
- package/src/pware.oc.core/pware.oc.core.oes.ts +99 -89
- package/src/pware.oc.core/pware.oc.core.paths.ts +240 -240
- package/src/pware.oc.core/pware.oc.core.preview.ts +74 -74
- package/src/pware.oc.core/pware.oc.core.pulse.ts +726 -726
- package/src/pware.oc.core/pware.oc.core.runtimeConfig.ts +41 -0
- package/src/pware.oc.core/pware.oc.core.sqlite.ts +164 -175
- package/src/pware.oc.core/pware.oc.core.sqliteGateway.ts +89 -0
- package/src/pware.oc.core/pware.oc.core.status.ts +214 -214
- package/src/pware.oc.core/pware.oc.core.timing.ts +63 -63
- package/src/pware.oc.core/pware.oc.core.width.ts +215 -215
- package/src/pware.oc.omo/constants/index.ts +15 -15
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.backgroundTask.ts +18 -18
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.boulderStatus.ts +49 -49
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.docKind.ts +29 -29
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.eventName.ts +11 -11
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.planStatus.ts +60 -60
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.reviewStatus.ts +49 -49
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.startWork.ts +25 -25
- package/src/pware.oc.omo/constants/pware.oc.omo.constants.verdict.ts +38 -38
- package/src/pware.oc.omo/index.ts +7 -7
- package/src/pware.oc.omo/resolver/index.ts +19 -19
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.approval.ts +179 -179
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.approvalGroup.ts +116 -116
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.approvalState.ts +34 -34
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.boulder.ts +503 -503
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.config.ts +40 -40
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.doc.ts +269 -269
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.draftFile.ts +18 -18
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.notepadsFile.ts +18 -18
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.plan.ts +155 -155
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.planFile.ts +363 -361
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.proofFile.ts +18 -18
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.rulesFile.ts +11 -11
- package/src/pware.oc.omo/resolver/pware.oc.omo.resolver.runContinuationFile.ts +14 -14
- package/src/pware.oc.opencode/constants/index.ts +10 -10
- package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.eventName.ts +15 -15
- package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.fileTouch.ts +18 -18
- package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.questionKind.ts +45 -45
- package/src/pware.oc.opencode/constants/pware.oc.opencode.constants.sessionStatus.ts +58 -58
- package/src/pware.oc.opencode/index.ts +8 -8
- package/src/pware.oc.opencode/pware.oc.opencode.events.ts +182 -182
- package/src/pware.oc.opencode/pware.oc.opencode.files.ts +399 -399
- package/src/pware.oc.opencode/resolver/index.ts +223 -170
- package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.file.ts +150 -162
- package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.question.ts +149 -185
- package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.session.ts +248 -211
- package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.todo.ts +22 -26
- package/src/pware.oc.opencode/resolver/pware.oc.opencode.resolver.tool.ts +224 -236
- package/src/pware.oc.perf/index.ts +14 -14
- package/src/pware.oc.perf/pware.oc.perf.asciichart.d.ts +15 -15
- package/src/pware.oc.perf/pware.oc.perf.charts.ts +284 -284
- package/src/pware.oc.perf/pware.oc.perf.reader.ts +962 -927
- package/src/pware.oc.perf/pware.oc.perf.realtime.ts +88 -88
- package/src/pware.oc.perf/pware.oc.perf.realtimeBlock.ts +118 -118
- package/src/pware.oc.perf/pware.oc.perf.realtimeCpuRam.ts +79 -79
- package/src/pware.oc.perf/pware.oc.perf.realtimeSampler.ts +71 -71
- package/src/pware.oc.perf/pware.oc.perf.realtimeTimeline.ts +214 -214
- package/src/pware.oc.perf/pware.oc.perf.self.ts +179 -179
- package/src/pware.oc.perf/pware.oc.perf.view.tsx +463 -462
- package/src/pware.oc.runtime/index.ts +13 -13
- package/src/pware.oc.runtime/pware.oc.runtime.monitor.ts +142 -142
- package/src/pware.oc.runtime/pware.oc.runtime.mywork-enrich.ts +87 -79
- package/src/pware.oc.runtime/pware.oc.runtime.mywork.ts +282 -253
- package/src/pware.oc.runtime/pware.oc.runtime.omoRead.ts +37 -0
- package/src/pware.oc.runtime/pware.oc.runtime.questions.ts +83 -95
- package/src/pware.oc.runtime/pware.oc.runtime.snapshotClient.ts +130 -129
- package/src/pware.oc.runtime/pware.oc.runtime.source.ts +105 -105
- package/src/pware.oc.runtime/pware.oc.runtime.worker.ts +44 -43
- package/src/pware.oc.runtime/resolver/index.ts +268 -224
- package/src/pware.oc.runtime/resolver/pware.oc.runtime.resolver.delegate.ts +146 -146
- package/src/pware.oc.ui/index.ts +12 -12
- package/src/pware.oc.ui/pware.oc.ui.chrome.tsx +381 -339
- package/src/pware.oc.ui/pware.oc.ui.glyphs.tsx +144 -144
- package/src/pware.oc.ui/pware.oc.ui.host.tsx +111 -111
- package/src/pware.oc.ui/pware.oc.ui.live.tsx +82 -82
- package/src/pware.oc.ui/pware.oc.ui.menudialogs.tsx +843 -843
- package/src/pware.oc.ui/pware.oc.ui.sections.tsx +552 -526
- package/src/pware.oc.ui/pware.oc.ui.sidebar.tsx +1718 -1629
- package/src/pware.oc.ui.tsx +98 -98
|
@@ -1,87 +1,87 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ignore sources for the Files panel: the project `.gitignore` (only when
|
|
3
|
-
* `skipGitignore` is on) and `.oesignore` (always, when present). Both use the
|
|
4
|
-
* `ignore` package for full gitignore semantics.
|
|
5
|
-
*/
|
|
6
|
-
import fs from "node:fs"
|
|
7
|
-
import path from "node:path"
|
|
8
|
-
import ignore from "ignore"
|
|
9
|
-
import { fileStamp } from "../pware.oc.core.paths.js"
|
|
10
|
-
import { createStampCache } from "../pware.oc.core.cache.js"
|
|
11
|
-
|
|
12
|
-
export function gitignorePath(projectRoot?: string | null): string | null {
|
|
13
|
-
if (!projectRoot) return null
|
|
14
|
-
return path.join(projectRoot, ".gitignore")
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function oesignorePath(projectRoot?: string | null): string | null {
|
|
18
|
-
if (!projectRoot) return null
|
|
19
|
-
return path.join(projectRoot, ".oesignore")
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** Stamp both ignore files so the fingerprint reloads when either changes. */
|
|
23
|
-
export function gitignoreStamp(projectRoot?: string | null): string {
|
|
24
|
-
return [gitignorePath(projectRoot), oesignorePath(projectRoot)].map(fileStamp).join("|")
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function readIfExists(p: string | null): string | null {
|
|
28
|
-
if (!p) return null
|
|
29
|
-
try {
|
|
30
|
-
if (!fs.existsSync(p)) return null
|
|
31
|
-
return fs.readFileSync(p, "utf8")
|
|
32
|
-
} catch {
|
|
33
|
-
return null
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** Absolute/relative path folded to a project-relative posix path, or null. */
|
|
38
|
-
function toRelPath(posixFile: string, projectRoot: string): string | null {
|
|
39
|
-
const root = projectRoot.replace(/\\/g, "/").replace(/\/+$/, "")
|
|
40
|
-
const file = posixFile.replace(/\\/g, "/").replace(/\/+$/, "")
|
|
41
|
-
if (!root) return null
|
|
42
|
-
if (file.toLowerCase().startsWith(`${root.toLowerCase()}/`)) return file.slice(root.length + 1)
|
|
43
|
-
if (/^[a-z]:\//i.test(file) || file.startsWith("/")) return null
|
|
44
|
-
return file.replace(/^\.\//, "")
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function compile(patterns: string | null): ReturnType<typeof ignore> {
|
|
48
|
-
const ig = ignore()
|
|
49
|
-
if (patterns != null) ig.add(patterns)
|
|
50
|
-
return ig
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
type IgnoreSet = { gitignore: ReturnType<typeof ignore>; oesignore: ReturnType<typeof ignore> }
|
|
54
|
-
|
|
55
|
-
const ignoreCache = createStampCache<IgnoreSet>()
|
|
56
|
-
|
|
57
|
-
function setFor(projectRoot: string): IgnoreSet {
|
|
58
|
-
return ignoreCache.get(`${projectRoot}|${gitignoreStamp(projectRoot)}`, () => ({
|
|
59
|
-
gitignore: compile(readIfExists(gitignorePath(projectRoot))),
|
|
60
|
-
oesignore: compile(readIfExists(oesignorePath(projectRoot))),
|
|
61
|
-
}))
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function ignoredBy(
|
|
65
|
-
which: keyof IgnoreSet,
|
|
66
|
-
posixPath: string,
|
|
67
|
-
projectRoot?: string | null,
|
|
68
|
-
): boolean {
|
|
69
|
-
if (!projectRoot) return false
|
|
70
|
-
const rel = toRelPath(posixPath, projectRoot)
|
|
71
|
-
if (!rel) return false
|
|
72
|
-
try {
|
|
73
|
-
return setFor(projectRoot)[which].ignores(rel)
|
|
74
|
-
} catch {
|
|
75
|
-
return false
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** True when the path matches the project's root `.gitignore`. */
|
|
80
|
-
export function ignoredByGitignore(posixPath: string, projectRoot?: string | null): boolean {
|
|
81
|
-
return ignoredBy("gitignore", posixPath, projectRoot)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** True when the path matches the project's `.oesignore` (always on when present). */
|
|
85
|
-
export function ignoredByOesignore(posixPath: string, projectRoot?: string | null): boolean {
|
|
86
|
-
return ignoredBy("oesignore", posixPath, projectRoot)
|
|
87
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Ignore sources for the Files panel: the project `.gitignore` (only when
|
|
3
|
+
* `skipGitignore` is on) and `.oesignore` (always, when present). Both use the
|
|
4
|
+
* `ignore` package for full gitignore semantics.
|
|
5
|
+
*/
|
|
6
|
+
import fs from "node:fs"
|
|
7
|
+
import path from "node:path"
|
|
8
|
+
import ignore from "ignore"
|
|
9
|
+
import { fileStamp } from "../pware.oc.core.paths.js"
|
|
10
|
+
import { createStampCache } from "../pware.oc.core.cache.js"
|
|
11
|
+
|
|
12
|
+
export function gitignorePath(projectRoot?: string | null): string | null {
|
|
13
|
+
if (!projectRoot) return null
|
|
14
|
+
return path.join(projectRoot, ".gitignore")
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function oesignorePath(projectRoot?: string | null): string | null {
|
|
18
|
+
if (!projectRoot) return null
|
|
19
|
+
return path.join(projectRoot, ".oesignore")
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Stamp both ignore files so the fingerprint reloads when either changes. */
|
|
23
|
+
export function gitignoreStamp(projectRoot?: string | null): string {
|
|
24
|
+
return [gitignorePath(projectRoot), oesignorePath(projectRoot)].map(fileStamp).join("|")
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function readIfExists(p: string | null): string | null {
|
|
28
|
+
if (!p) return null
|
|
29
|
+
try {
|
|
30
|
+
if (!fs.existsSync(p)) return null
|
|
31
|
+
return fs.readFileSync(p, "utf8")
|
|
32
|
+
} catch {
|
|
33
|
+
return null
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Absolute/relative path folded to a project-relative posix path, or null. */
|
|
38
|
+
function toRelPath(posixFile: string, projectRoot: string): string | null {
|
|
39
|
+
const root = projectRoot.replace(/\\/g, "/").replace(/\/+$/, "")
|
|
40
|
+
const file = posixFile.replace(/\\/g, "/").replace(/\/+$/, "")
|
|
41
|
+
if (!root) return null
|
|
42
|
+
if (file.toLowerCase().startsWith(`${root.toLowerCase()}/`)) return file.slice(root.length + 1)
|
|
43
|
+
if (/^[a-z]:\//i.test(file) || file.startsWith("/")) return null
|
|
44
|
+
return file.replace(/^\.\//, "")
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function compile(patterns: string | null): ReturnType<typeof ignore> {
|
|
48
|
+
const ig = ignore()
|
|
49
|
+
if (patterns != null) ig.add(patterns)
|
|
50
|
+
return ig
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type IgnoreSet = { gitignore: ReturnType<typeof ignore>; oesignore: ReturnType<typeof ignore> }
|
|
54
|
+
|
|
55
|
+
const ignoreCache = createStampCache<IgnoreSet>()
|
|
56
|
+
|
|
57
|
+
function setFor(projectRoot: string): IgnoreSet {
|
|
58
|
+
return ignoreCache.get(`${projectRoot}|${gitignoreStamp(projectRoot)}`, () => ({
|
|
59
|
+
gitignore: compile(readIfExists(gitignorePath(projectRoot))),
|
|
60
|
+
oesignore: compile(readIfExists(oesignorePath(projectRoot))),
|
|
61
|
+
}))
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function ignoredBy(
|
|
65
|
+
which: keyof IgnoreSet,
|
|
66
|
+
posixPath: string,
|
|
67
|
+
projectRoot?: string | null,
|
|
68
|
+
): boolean {
|
|
69
|
+
if (!projectRoot) return false
|
|
70
|
+
const rel = toRelPath(posixPath, projectRoot)
|
|
71
|
+
if (!rel) return false
|
|
72
|
+
try {
|
|
73
|
+
return setFor(projectRoot)[which].ignores(rel)
|
|
74
|
+
} catch {
|
|
75
|
+
return false
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** True when the path matches the project's root `.gitignore`. */
|
|
80
|
+
export function ignoredByGitignore(posixPath: string, projectRoot?: string | null): boolean {
|
|
81
|
+
return ignoredBy("gitignore", posixPath, projectRoot)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** True when the path matches the project's `.oesignore` (always on when present). */
|
|
85
|
+
export function ignoredByOesignore(posixPath: string, projectRoot?: string | null): boolean {
|
|
86
|
+
return ignoredBy("oesignore", posixPath, projectRoot)
|
|
87
|
+
}
|
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* pware.oc.core
|
|
3
|
-
*
|
|
4
|
-
* Shared infrastructure + pure logic. Core never imports a domain or the UI.
|
|
5
|
-
*/
|
|
6
|
-
export * from "./pware.oc.core.cache.js"
|
|
7
|
-
export * from "./pware.oc.core.clipboard.js"
|
|
8
|
-
export * from "./pware.oc.core.debug.js"
|
|
9
|
-
export * from "./pware.oc.core.events.js"
|
|
10
|
-
export * from "./pware.oc.core.layout.js"
|
|
11
|
-
export * from "./pware.oc.core.oes.js"
|
|
12
|
-
export * from "./pware.oc.core.paths.js"
|
|
13
|
-
export * from "./pware.oc.core.preview.js"
|
|
14
|
-
export * from "./pware.oc.core.pulse.js"
|
|
15
|
-
export * from "./pware.oc.core.
|
|
16
|
-
export * from "./pware.oc.core.
|
|
17
|
-
export * from "./pware.oc.core.
|
|
18
|
-
export * from "./
|
|
1
|
+
/**
|
|
2
|
+
* pware.oc.core
|
|
3
|
+
*
|
|
4
|
+
* Shared infrastructure + pure logic. Core never imports a domain or the UI.
|
|
5
|
+
*/
|
|
6
|
+
export * from "./pware.oc.core.cache.js"
|
|
7
|
+
export * from "./pware.oc.core.clipboard.js"
|
|
8
|
+
export * from "./pware.oc.core.debug.js"
|
|
9
|
+
export * from "./pware.oc.core.events.js"
|
|
10
|
+
export * from "./pware.oc.core.layout.js"
|
|
11
|
+
export * from "./pware.oc.core.oes.js"
|
|
12
|
+
export * from "./pware.oc.core.paths.js"
|
|
13
|
+
export * from "./pware.oc.core.preview.js"
|
|
14
|
+
export * from "./pware.oc.core.pulse.js"
|
|
15
|
+
export * from "./pware.oc.core.runtimeConfig.js"
|
|
16
|
+
export * from "./pware.oc.core.sqlite.js"
|
|
17
|
+
export * from "./pware.oc.core.status.js"
|
|
18
|
+
export * from "./pware.oc.core.bus.js"
|
|
19
|
+
export * from "./git/index.js"
|
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
export type PwareEvent = {
|
|
2
|
-
type: string
|
|
3
|
-
ts: number
|
|
4
|
-
data?: unknown
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export type PwareEventListener = (evt: PwareEvent) => void
|
|
8
|
-
|
|
9
|
-
export type PwareEventBus = {
|
|
10
|
-
emit: (evt: PwareEvent) => void
|
|
11
|
-
on: (type: string, listener: PwareEventListener) => () => void
|
|
12
|
-
off: (type: string, listener: PwareEventListener) => void
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function createEventBus(): PwareEventBus {
|
|
16
|
-
const byType = new Map<string, Set<PwareEventListener>>()
|
|
17
|
-
|
|
18
|
-
const off = (type: string, listener: PwareEventListener): void => {
|
|
19
|
-
const set = byType.get(type)
|
|
20
|
-
if (!set) return
|
|
21
|
-
set.delete(listener)
|
|
22
|
-
if (set.size === 0) byType.delete(type)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const on = (type: string, listener: PwareEventListener): (() => void) => {
|
|
26
|
-
const set = byType.get(type)
|
|
27
|
-
if (set) {
|
|
28
|
-
set.add(listener)
|
|
29
|
-
} else {
|
|
30
|
-
byType.set(type, new Set([listener]))
|
|
31
|
-
}
|
|
32
|
-
return () => off(type, listener)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const emit = (evt: PwareEvent): void => {
|
|
36
|
-
const set = byType.get(evt.type)
|
|
37
|
-
if (!set || set.size === 0) return
|
|
38
|
-
for (const listener of set) listener(evt)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return { emit, on, off }
|
|
42
|
-
}
|
|
1
|
+
export type PwareEvent = {
|
|
2
|
+
type: string
|
|
3
|
+
ts: number
|
|
4
|
+
data?: unknown
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type PwareEventListener = (evt: PwareEvent) => void
|
|
8
|
+
|
|
9
|
+
export type PwareEventBus = {
|
|
10
|
+
emit: (evt: PwareEvent) => void
|
|
11
|
+
on: (type: string, listener: PwareEventListener) => () => void
|
|
12
|
+
off: (type: string, listener: PwareEventListener) => void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function createEventBus(): PwareEventBus {
|
|
16
|
+
const byType = new Map<string, Set<PwareEventListener>>()
|
|
17
|
+
|
|
18
|
+
const off = (type: string, listener: PwareEventListener): void => {
|
|
19
|
+
const set = byType.get(type)
|
|
20
|
+
if (!set) return
|
|
21
|
+
set.delete(listener)
|
|
22
|
+
if (set.size === 0) byType.delete(type)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const on = (type: string, listener: PwareEventListener): (() => void) => {
|
|
26
|
+
const set = byType.get(type)
|
|
27
|
+
if (set) {
|
|
28
|
+
set.add(listener)
|
|
29
|
+
} else {
|
|
30
|
+
byType.set(type, new Set([listener]))
|
|
31
|
+
}
|
|
32
|
+
return () => off(type, listener)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const emit = (evt: PwareEvent): void => {
|
|
36
|
+
const set = byType.get(evt.type)
|
|
37
|
+
if (!set || set.size === 0) return
|
|
38
|
+
for (const listener of set) listener(evt)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return { emit, on, off }
|
|
42
|
+
}
|
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Stamp-keyed in-memory cache. Optional TTL for filesystem snapshots.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export function createStampCache<T>(opts?: { ttlMs?: number }): {
|
|
6
|
-
get: (key: string, load: () => T) => T
|
|
7
|
-
peek: (key: string) => T | undefined
|
|
8
|
-
set: (key: string, value: T) => void
|
|
9
|
-
reset: () => void
|
|
10
|
-
} {
|
|
11
|
-
const ttl = opts?.ttlMs
|
|
12
|
-
let cacheKey = ""
|
|
13
|
-
let cached: T | undefined
|
|
14
|
-
let at = 0
|
|
15
|
-
return {
|
|
16
|
-
get(key, load) {
|
|
17
|
-
const now = Date.now()
|
|
18
|
-
if (
|
|
19
|
-
key &&
|
|
20
|
-
key === cacheKey &&
|
|
21
|
-
cached !== undefined &&
|
|
22
|
-
(ttl == null || now - at < ttl)
|
|
23
|
-
) {
|
|
24
|
-
return cached
|
|
25
|
-
}
|
|
26
|
-
const next = load()
|
|
27
|
-
cacheKey = key
|
|
28
|
-
cached = next
|
|
29
|
-
at = now
|
|
30
|
-
return next
|
|
31
|
-
},
|
|
32
|
-
peek(key) {
|
|
33
|
-
if (!key || key !== cacheKey || cached === undefined) return undefined
|
|
34
|
-
if (ttl != null && Date.now() - at >= ttl) return undefined
|
|
35
|
-
return cached
|
|
36
|
-
},
|
|
37
|
-
set(key, value) {
|
|
38
|
-
cacheKey = key
|
|
39
|
-
cached = value
|
|
40
|
-
at = Date.now()
|
|
41
|
-
},
|
|
42
|
-
reset() {
|
|
43
|
-
cacheKey = ""
|
|
44
|
-
cached = undefined
|
|
45
|
-
at = 0
|
|
46
|
-
},
|
|
47
|
-
}
|
|
48
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Stamp-keyed in-memory cache. Optional TTL for filesystem snapshots.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function createStampCache<T>(opts?: { ttlMs?: number }): {
|
|
6
|
+
get: (key: string, load: () => T) => T
|
|
7
|
+
peek: (key: string) => T | undefined
|
|
8
|
+
set: (key: string, value: T) => void
|
|
9
|
+
reset: () => void
|
|
10
|
+
} {
|
|
11
|
+
const ttl = opts?.ttlMs
|
|
12
|
+
let cacheKey = ""
|
|
13
|
+
let cached: T | undefined
|
|
14
|
+
let at = 0
|
|
15
|
+
return {
|
|
16
|
+
get(key, load) {
|
|
17
|
+
const now = Date.now()
|
|
18
|
+
if (
|
|
19
|
+
key &&
|
|
20
|
+
key === cacheKey &&
|
|
21
|
+
cached !== undefined &&
|
|
22
|
+
(ttl == null || now - at < ttl)
|
|
23
|
+
) {
|
|
24
|
+
return cached
|
|
25
|
+
}
|
|
26
|
+
const next = load()
|
|
27
|
+
cacheKey = key
|
|
28
|
+
cached = next
|
|
29
|
+
at = now
|
|
30
|
+
return next
|
|
31
|
+
},
|
|
32
|
+
peek(key) {
|
|
33
|
+
if (!key || key !== cacheKey || cached === undefined) return undefined
|
|
34
|
+
if (ttl != null && Date.now() - at >= ttl) return undefined
|
|
35
|
+
return cached
|
|
36
|
+
},
|
|
37
|
+
set(key, value) {
|
|
38
|
+
cacheKey = key
|
|
39
|
+
cached = value
|
|
40
|
+
at = Date.now()
|
|
41
|
+
},
|
|
42
|
+
reset() {
|
|
43
|
+
cacheKey = ""
|
|
44
|
+
cached = undefined
|
|
45
|
+
at = 0
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -1,97 +1,97 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copy text to the system clipboard without npm deps.
|
|
3
|
-
* Native tools are async so a click never freezes the TUI.
|
|
4
|
-
* Windows prefers clip.exe; PowerShell is last resort (slow to start).
|
|
5
|
-
* OSC 52 is always sent so SSH / remote terminals can still receive the copy.
|
|
6
|
-
*/
|
|
7
|
-
import { spawn } from "node:child_process"
|
|
8
|
-
import os from "node:os"
|
|
9
|
-
|
|
10
|
-
const COPY_TIMEOUT_MS = 1500
|
|
11
|
-
|
|
12
|
-
/** Terminal clipboard sequence (OSC 52). Exported for tests. */
|
|
13
|
-
export function osc52Payload(text: string): string {
|
|
14
|
-
return `\x1b]52;c;${Buffer.from(text, "utf8").toString("base64")}\x07`
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function writeOsc52(text: string): boolean {
|
|
18
|
-
try {
|
|
19
|
-
process.stdout.write(osc52Payload(text))
|
|
20
|
-
return true
|
|
21
|
-
} catch {
|
|
22
|
-
return false
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function pipe(command: string, args: string[], input: string | Buffer): Promise<boolean> {
|
|
27
|
-
return new Promise((resolve) => {
|
|
28
|
-
let child: ReturnType<typeof spawn>
|
|
29
|
-
try {
|
|
30
|
-
child = spawn(command, args, {
|
|
31
|
-
windowsHide: true,
|
|
32
|
-
stdio: ["pipe", "ignore", "ignore"],
|
|
33
|
-
})
|
|
34
|
-
} catch {
|
|
35
|
-
resolve(false)
|
|
36
|
-
return
|
|
37
|
-
}
|
|
38
|
-
let done = false
|
|
39
|
-
const finish = (ok: boolean) => {
|
|
40
|
-
if (done) return
|
|
41
|
-
done = true
|
|
42
|
-
clearTimeout(timer)
|
|
43
|
-
resolve(ok)
|
|
44
|
-
}
|
|
45
|
-
const timer = setTimeout(() => {
|
|
46
|
-
try {
|
|
47
|
-
child.kill()
|
|
48
|
-
} catch {
|
|
49
|
-
// already gone
|
|
50
|
-
}
|
|
51
|
-
finish(false)
|
|
52
|
-
}, COPY_TIMEOUT_MS)
|
|
53
|
-
child.on("error", () => finish(false))
|
|
54
|
-
child.on("close", (code) => finish(code === 0))
|
|
55
|
-
try {
|
|
56
|
-
if (!child.stdin) {
|
|
57
|
-
finish(false)
|
|
58
|
-
return
|
|
59
|
-
}
|
|
60
|
-
child.stdin.end(input)
|
|
61
|
-
} catch {
|
|
62
|
-
finish(false)
|
|
63
|
-
}
|
|
64
|
-
})
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** clip.exe expects UTF-16 LE with BOM for non-ASCII. */
|
|
68
|
-
function clipUtf16(text: string): Buffer {
|
|
69
|
-
return Buffer.concat([Buffer.from([0xff, 0xfe]), Buffer.from(text, "utf16le")])
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export async function copyText(text: string): Promise<boolean> {
|
|
73
|
-
if (!text) return false
|
|
74
|
-
const osc = writeOsc52(text)
|
|
75
|
-
const platform = os.platform()
|
|
76
|
-
if (platform === "win32") {
|
|
77
|
-
if (await pipe("clip", [], clipUtf16(text))) return true
|
|
78
|
-
if (
|
|
79
|
-
await pipe(
|
|
80
|
-
"powershell.exe",
|
|
81
|
-
["-NoProfile", "-NonInteractive", "-Command", "$input | Set-Clipboard"],
|
|
82
|
-
text,
|
|
83
|
-
)
|
|
84
|
-
) {
|
|
85
|
-
return true
|
|
86
|
-
}
|
|
87
|
-
return osc
|
|
88
|
-
}
|
|
89
|
-
if (platform === "darwin") {
|
|
90
|
-
if (await pipe("pbcopy", [], text)) return true
|
|
91
|
-
return osc
|
|
92
|
-
}
|
|
93
|
-
if (process.env.WAYLAND_DISPLAY && (await pipe("wl-copy", [], text))) return true
|
|
94
|
-
if (await pipe("xclip", ["-selection", "clipboard"], text)) return true
|
|
95
|
-
if (await pipe("xsel", ["--clipboard", "--input"], text)) return true
|
|
96
|
-
return osc
|
|
97
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Copy text to the system clipboard without npm deps.
|
|
3
|
+
* Native tools are async so a click never freezes the TUI.
|
|
4
|
+
* Windows prefers clip.exe; PowerShell is last resort (slow to start).
|
|
5
|
+
* OSC 52 is always sent so SSH / remote terminals can still receive the copy.
|
|
6
|
+
*/
|
|
7
|
+
import { spawn } from "node:child_process"
|
|
8
|
+
import os from "node:os"
|
|
9
|
+
|
|
10
|
+
const COPY_TIMEOUT_MS = 1500
|
|
11
|
+
|
|
12
|
+
/** Terminal clipboard sequence (OSC 52). Exported for tests. */
|
|
13
|
+
export function osc52Payload(text: string): string {
|
|
14
|
+
return `\x1b]52;c;${Buffer.from(text, "utf8").toString("base64")}\x07`
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function writeOsc52(text: string): boolean {
|
|
18
|
+
try {
|
|
19
|
+
process.stdout.write(osc52Payload(text))
|
|
20
|
+
return true
|
|
21
|
+
} catch {
|
|
22
|
+
return false
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function pipe(command: string, args: string[], input: string | Buffer): Promise<boolean> {
|
|
27
|
+
return new Promise((resolve) => {
|
|
28
|
+
let child: ReturnType<typeof spawn>
|
|
29
|
+
try {
|
|
30
|
+
child = spawn(command, args, {
|
|
31
|
+
windowsHide: true,
|
|
32
|
+
stdio: ["pipe", "ignore", "ignore"],
|
|
33
|
+
})
|
|
34
|
+
} catch {
|
|
35
|
+
resolve(false)
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
let done = false
|
|
39
|
+
const finish = (ok: boolean) => {
|
|
40
|
+
if (done) return
|
|
41
|
+
done = true
|
|
42
|
+
clearTimeout(timer)
|
|
43
|
+
resolve(ok)
|
|
44
|
+
}
|
|
45
|
+
const timer = setTimeout(() => {
|
|
46
|
+
try {
|
|
47
|
+
child.kill()
|
|
48
|
+
} catch {
|
|
49
|
+
// already gone
|
|
50
|
+
}
|
|
51
|
+
finish(false)
|
|
52
|
+
}, COPY_TIMEOUT_MS)
|
|
53
|
+
child.on("error", () => finish(false))
|
|
54
|
+
child.on("close", (code) => finish(code === 0))
|
|
55
|
+
try {
|
|
56
|
+
if (!child.stdin) {
|
|
57
|
+
finish(false)
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
child.stdin.end(input)
|
|
61
|
+
} catch {
|
|
62
|
+
finish(false)
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** clip.exe expects UTF-16 LE with BOM for non-ASCII. */
|
|
68
|
+
function clipUtf16(text: string): Buffer {
|
|
69
|
+
return Buffer.concat([Buffer.from([0xff, 0xfe]), Buffer.from(text, "utf16le")])
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function copyText(text: string): Promise<boolean> {
|
|
73
|
+
if (!text) return false
|
|
74
|
+
const osc = writeOsc52(text)
|
|
75
|
+
const platform = os.platform()
|
|
76
|
+
if (platform === "win32") {
|
|
77
|
+
if (await pipe("clip", [], clipUtf16(text))) return true
|
|
78
|
+
if (
|
|
79
|
+
await pipe(
|
|
80
|
+
"powershell.exe",
|
|
81
|
+
["-NoProfile", "-NonInteractive", "-Command", "$input | Set-Clipboard"],
|
|
82
|
+
text,
|
|
83
|
+
)
|
|
84
|
+
) {
|
|
85
|
+
return true
|
|
86
|
+
}
|
|
87
|
+
return osc
|
|
88
|
+
}
|
|
89
|
+
if (platform === "darwin") {
|
|
90
|
+
if (await pipe("pbcopy", [], text)) return true
|
|
91
|
+
return osc
|
|
92
|
+
}
|
|
93
|
+
if (process.env.WAYLAND_DISPLAY && (await pipe("wl-copy", [], text))) return true
|
|
94
|
+
if (await pipe("xclip", ["-selection", "clipboard"], text)) return true
|
|
95
|
+
if (await pipe("xsel", ["--clipboard", "--input"], text)) return true
|
|
96
|
+
return osc
|
|
97
|
+
}
|