@shivaedev/work-board 0.3.0 → 0.4.1
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/CHANGELOG.md +36 -0
- package/README.md +70 -11
- package/dist/attention/overviewHtml.d.ts +3 -0
- package/dist/attention/overviewHtml.d.ts.map +1 -0
- package/dist/attention/overviewHtml.js +41 -0
- package/dist/attention/overviewHtml.js.map +1 -0
- package/dist/attention/requests.d.ts +24 -0
- package/dist/attention/requests.d.ts.map +1 -0
- package/dist/attention/requests.js +40 -0
- package/dist/attention/requests.js.map +1 -0
- package/dist/attention/style.d.ts +2 -0
- package/dist/attention/style.d.ts.map +1 -0
- package/dist/attention/style.js +10 -0
- package/dist/attention/style.js.map +1 -0
- package/dist/board.d.ts.map +1 -1
- package/dist/board.js +6 -1
- package/dist/board.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/files/changes.d.ts.map +1 -1
- package/dist/files/changes.js +4 -9
- package/dist/files/changes.js.map +1 -1
- package/dist/files/home.d.ts.map +1 -1
- package/dist/files/home.js +4 -0
- package/dist/files/home.js.map +1 -1
- package/dist/files/list.d.ts +7 -0
- package/dist/files/list.d.ts.map +1 -1
- package/dist/files/list.js +23 -13
- package/dist/files/list.js.map +1 -1
- package/dist/files/markdownPath.d.ts +3 -0
- package/dist/files/markdownPath.d.ts.map +1 -0
- package/dist/files/markdownPath.js +31 -0
- package/dist/files/markdownPath.js.map +1 -0
- package/dist/files/watchDirectories.d.ts +9 -0
- package/dist/files/watchDirectories.d.ts.map +1 -0
- package/dist/files/watchDirectories.js +33 -0
- package/dist/files/watchDirectories.js.map +1 -0
- package/dist/history/baseline.d.ts +19 -0
- package/dist/history/baseline.d.ts.map +1 -0
- package/dist/history/baseline.js +40 -0
- package/dist/history/baseline.js.map +1 -0
- package/dist/history/compare.d.ts +14 -0
- package/dist/history/compare.d.ts.map +1 -0
- package/dist/history/compare.js +47 -0
- package/dist/history/compare.js.map +1 -0
- package/dist/history/documents.d.ts +10 -0
- package/dist/history/documents.d.ts.map +1 -0
- package/dist/history/documents.js +15 -0
- package/dist/history/documents.js.map +1 -0
- package/dist/history/fields.d.ts +3 -0
- package/dist/history/fields.d.ts.map +1 -0
- package/dist/history/fields.js +27 -0
- package/dist/history/fields.js.map +1 -0
- package/dist/history/html.d.ts +4 -0
- package/dist/history/html.d.ts.map +1 -0
- package/dist/history/html.js +22 -0
- package/dist/history/html.js.map +1 -0
- package/dist/history/limits.d.ts +3 -0
- package/dist/history/limits.d.ts.map +1 -0
- package/dist/history/limits.js +3 -0
- package/dist/history/limits.js.map +1 -0
- package/dist/history/response.d.ts +4 -0
- package/dist/history/response.d.ts.map +1 -0
- package/dist/history/response.js +20 -0
- package/dist/history/response.js.map +1 -0
- package/dist/history/schema.d.ts +24 -0
- package/dist/history/schema.d.ts.map +1 -0
- package/dist/history/schema.js +19 -0
- package/dist/history/schema.js.map +1 -0
- package/dist/history/script.d.ts +2 -0
- package/dist/history/script.d.ts.map +1 -0
- package/dist/history/script.js +125 -0
- package/dist/history/script.js.map +1 -0
- package/dist/http/assets.d.ts.map +1 -1
- package/dist/http/assets.js +2 -0
- package/dist/http/assets.js.map +1 -1
- package/dist/http/history.d.ts +7 -0
- package/dist/http/history.d.ts.map +1 -0
- package/dist/http/history.js +45 -0
- package/dist/http/history.js.map +1 -0
- package/dist/http/overview.d.ts +5 -0
- package/dist/http/overview.d.ts.map +1 -0
- package/dist/http/overview.js +13 -0
- package/dist/http/overview.js.map +1 -0
- package/dist/http/page.d.ts +2 -2
- package/dist/http/page.d.ts.map +1 -1
- package/dist/http/page.js +5 -6
- package/dist/http/page.js.map +1 -1
- package/dist/metadata/attentionProblems.d.ts +4 -0
- package/dist/metadata/attentionProblems.d.ts.map +1 -0
- package/dist/metadata/attentionProblems.js +16 -0
- package/dist/metadata/attentionProblems.js.map +1 -0
- package/dist/metadata/backlinks.d.ts.map +1 -1
- package/dist/metadata/backlinks.js +5 -0
- package/dist/metadata/backlinks.js.map +1 -1
- package/dist/metadata/fields.d.ts +8 -0
- package/dist/metadata/fields.d.ts.map +1 -1
- package/dist/metadata/problems.d.ts.map +1 -1
- package/dist/metadata/problems.js +3 -1
- package/dist/metadata/problems.js.map +1 -1
- package/dist/metadata/relationshipLabel.d.ts.map +1 -1
- package/dist/metadata/relationshipLabel.js +1 -0
- package/dist/metadata/relationshipLabel.js.map +1 -1
- package/dist/metadata/schema.d.ts +10 -0
- package/dist/metadata/schema.d.ts.map +1 -1
- package/dist/metadata/schema.js +9 -0
- package/dist/metadata/schema.js.map +1 -1
- package/dist/page/attentionDetails.d.ts +4 -0
- package/dist/page/attentionDetails.d.ts.map +1 -0
- package/dist/page/attentionDetails.js +13 -0
- package/dist/page/attentionDetails.js.map +1 -0
- package/dist/page/client.d.ts +1 -1
- package/dist/page/client.d.ts.map +1 -1
- package/dist/page/client.js +1 -0
- package/dist/page/client.js.map +1 -1
- package/dist/page/metadataDetails.d.ts.map +1 -1
- package/dist/page/metadataDetails.js +2 -0
- package/dist/page/metadataDetails.js.map +1 -1
- package/dist/page/navigationScript.js +1 -1
- package/dist/page/shell.d.ts +1 -1
- package/dist/page/shell.d.ts.map +1 -1
- package/dist/page/shell.js +2 -2
- package/dist/page/shell.js.map +1 -1
- package/dist/page/stateScript.d.ts.map +1 -1
- package/dist/page/stateScript.js +6 -0
- package/dist/page/stateScript.js.map +1 -1
- package/dist/page/style.d.ts.map +1 -1
- package/dist/page/style.js +2 -1
- package/dist/page/style.js.map +1 -1
- package/dist/search/attentionEntries.d.ts +4 -0
- package/dist/search/attentionEntries.d.ts.map +1 -0
- package/dist/search/attentionEntries.js +20 -0
- package/dist/search/attentionEntries.js.map +1 -0
- package/dist/search/metadataEntries.d.ts.map +1 -1
- package/dist/search/metadataEntries.js +2 -0
- package/dist/search/metadataEntries.js.map +1 -1
- package/dist/search/snapshot.d.ts +2 -2
- package/dist/search/snapshot.d.ts.map +1 -1
- package/dist/search/snapshot.js +4 -5
- package/dist/search/snapshot.js.map +1 -1
- package/package.json +5 -4
- package/src/attention/overviewHtml.ts +44 -0
- package/src/attention/requests.ts +64 -0
- package/src/attention/style.ts +9 -0
- package/src/board.ts +7 -2
- package/src/cli.ts +1 -1
- package/src/files/changes.ts +4 -16
- package/src/files/home.ts +4 -0
- package/src/files/list.ts +25 -14
- package/src/files/markdownPath.ts +31 -0
- package/src/files/watchDirectories.ts +60 -0
- package/src/history/baseline.ts +43 -0
- package/src/history/compare.ts +54 -0
- package/src/history/documents.ts +21 -0
- package/src/history/fields.ts +32 -0
- package/src/history/html.ts +23 -0
- package/src/history/limits.ts +2 -0
- package/src/history/response.ts +23 -0
- package/src/history/schema.ts +36 -0
- package/src/history/script.ts +124 -0
- package/src/http/assets.ts +2 -0
- package/src/http/history.ts +50 -0
- package/src/http/overview.ts +19 -0
- package/src/http/page.ts +6 -6
- package/src/metadata/attentionProblems.ts +18 -0
- package/src/metadata/backlinks.ts +7 -0
- package/src/metadata/problems.ts +5 -1
- package/src/metadata/relationshipLabel.ts +1 -0
- package/src/metadata/schema.ts +9 -0
- package/src/page/attentionDetails.ts +18 -0
- package/src/page/client.ts +1 -0
- package/src/page/metadataDetails.ts +2 -0
- package/src/page/navigationScript.ts +1 -1
- package/src/page/shell.ts +11 -3
- package/src/page/stateScript.ts +6 -0
- package/src/page/style.ts +2 -1
- package/src/search/attentionEntries.ts +22 -0
- package/src/search/metadataEntries.ts +2 -0
- package/src/search/snapshot.ts +4 -5
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Effect, FileSystem, Option, Path } from "effect";
|
|
2
|
+
import { isMarkdown, within } from "./list.js";
|
|
3
|
+
export const markdownPath = Effect.fn("WorkBoard.markdownPath")(function* (root, realRoot, relative) {
|
|
4
|
+
if (!isMarkdown(relative) || relative.split("/").some((segment) => segment === "" || segment === "..")) {
|
|
5
|
+
return undefined;
|
|
6
|
+
}
|
|
7
|
+
const fs = yield* FileSystem.FileSystem;
|
|
8
|
+
const path = yield* Path.Path;
|
|
9
|
+
const segments = relative.split("/");
|
|
10
|
+
const ancestors = new Set([realRoot]);
|
|
11
|
+
let boundary = realRoot;
|
|
12
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
13
|
+
const real = yield* Effect.option(fs.realPath(path.join(root, ...segments.slice(0, index + 1))));
|
|
14
|
+
if (Option.isNone(real)) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
if (index === segments.length - 1) {
|
|
18
|
+
return within(boundary, path.sep, real.value) ? real.value : undefined;
|
|
19
|
+
}
|
|
20
|
+
const info = yield* Effect.option(fs.stat(real.value));
|
|
21
|
+
if (Option.isNone(info) || info.value.type !== "Directory" || ancestors.has(real.value) || within(real.value, path.sep, realRoot)) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
ancestors.add(real.value);
|
|
25
|
+
if (!within(boundary, path.sep, real.value)) {
|
|
26
|
+
boundary = real.value;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return undefined;
|
|
30
|
+
});
|
|
31
|
+
//# sourceMappingURL=markdownPath.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdownPath.js","sourceRoot":"","sources":["../../src/files/markdownPath.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAE/C,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,EAAE,CAAC,wBAAwB,CAAC,CAAC,QAAQ,CAAC,EAAE,IAAY,EAAE,QAAgB,EAAE,QAAgB;IAC1H,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,EAAE,IAAI,OAAO,KAAK,IAAI,CAAC,EAAE,CAAC;QACxG,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC;IACxC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;IAC9B,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrC,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;IACtC,IAAI,QAAQ,GAAG,QAAQ,CAAC;IACxB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACzD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACjG,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,OAAO,SAAS,CAAC;QAClB,CAAC;QACD,IAAI,KAAK,KAAK,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACnC,OAAO,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;QACxE,CAAC;QACD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QACvD,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,CAAC;YACnI,OAAO,SAAS,CAAC;QAClB,CAAC;QACD,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7C,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC;QACvB,CAAC;IACF,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Effect, FileSystem, Path } from "effect";
|
|
2
|
+
declare const WatchEnded_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
3
|
+
readonly _tag: "WatchEnded";
|
|
4
|
+
} & Readonly<A>;
|
|
5
|
+
declare class WatchEnded extends WatchEnded_base {
|
|
6
|
+
}
|
|
7
|
+
export declare const watchDirectories: (root: string, realRoot: string, stale: Effect.Effect<void, never, never>, setWatching: (watching: boolean) => Effect.Effect<unknown>, publish: (paths: readonly string[]) => Effect.Effect<unknown>) => Effect.Effect<void, import("effect/PlatformError").PlatformError | WatchEnded, FileSystem.FileSystem | Path.Path>;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=watchDirectories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watchDirectories.d.ts","sourceRoot":"","sources":["../../src/files/watchDirectories.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,MAAM,EAAE,UAAU,EAAU,IAAI,EAAU,MAAM,QAAQ,CAAC;;;;AAGlF,cAAM,UAAW,SAAQ,eAA8B;CAAG;AAK1D,eAAO,MAAM,gBAAgB,qGAIJ,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,mBACzC,SAAS,MAAM,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,sHA8C5D,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Data, Deferred, Effect, FileSystem, Option, Path, Stream } from "effect";
|
|
2
|
+
import { isMarkdown, scanMarkdown } from "./list.js";
|
|
3
|
+
class WatchEnded extends Data.TaggedError("WatchEnded") {
|
|
4
|
+
}
|
|
5
|
+
const SETTLE = "100 millis";
|
|
6
|
+
const BATCH = 256;
|
|
7
|
+
export const watchDirectories = Effect.fn("WorkBoard.watchDirectories")(function* (root, realRoot, stale, setWatching, publish) {
|
|
8
|
+
const fs = yield* FileSystem.FileSystem;
|
|
9
|
+
const path = yield* Path.Path;
|
|
10
|
+
const cycle = Effect.scoped(Effect.gen(function* () {
|
|
11
|
+
const { roots } = yield* scanMarkdown(root, realRoot);
|
|
12
|
+
const topology = yield* Deferred.make();
|
|
13
|
+
// Node's recursive watcher can miss replacement of directory symlinks.
|
|
14
|
+
const parents = yield* Effect.forEach(roots.filter((folder) => folder.relative !== ""), (folder) => Effect.map(fs.realPath(path.dirname(path.join(root, folder.relative))), (real) => ({ real, relative: path.dirname(folder.relative) })));
|
|
15
|
+
const watched = [...roots.map((folder) => ({ ...folder, recursive: true })), ...parents.map((folder) => ({ ...folder, recursive: false }))];
|
|
16
|
+
const streams = watched.map((folder) => fs.watch(folder.real, { recursive: folder.recursive }).pipe(Stream.map((event) => {
|
|
17
|
+
const local = path.isAbsolute(event.path) ? path.relative(folder.real, event.path) : event.path;
|
|
18
|
+
return path.join(folder.relative, local).split(path.sep).join("/");
|
|
19
|
+
})));
|
|
20
|
+
const watch = Stream.mergeAll(streams, { concurrency: "unbounded" }).pipe(Stream.tap(() => stale), Stream.tap((relative) => Effect.gen(function* () {
|
|
21
|
+
const info = yield* Effect.option(fs.stat(path.join(root, relative)));
|
|
22
|
+
const removedLink = roots.some((folder) => folder.relative === relative || folder.relative.startsWith(`${relative}/`));
|
|
23
|
+
if (Option.match(info, { onNone: () => removedLink, onSome: (value) => value.type === "Directory" })) {
|
|
24
|
+
yield* Effect.forkScoped(Effect.delay(Deferred.succeed(topology, undefined), SETTLE));
|
|
25
|
+
}
|
|
26
|
+
})), Stream.filter(isMarkdown), Stream.interruptWhen(Deferred.await(topology)), Stream.groupedWithin(BATCH, SETTLE), Stream.runForEach((paths) => publish([...new Set(paths)])), Effect.andThen(Effect.flatMap(Deferred.isDone(topology), (changed) => (changed ? Effect.void : Effect.fail(new WatchEnded())))));
|
|
27
|
+
yield* Effect.forkScoped(Effect.delay(setWatching(true), SETTLE));
|
|
28
|
+
yield* watch;
|
|
29
|
+
yield* setWatching(false);
|
|
30
|
+
}));
|
|
31
|
+
yield* Effect.forever(cycle);
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=watchDirectories.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watchDirectories.js","sourceRoot":"","sources":["../../src/files/watchDirectories.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Snapshot } from "#search/snapshot.ts";
|
|
2
|
+
export declare function readBaseline(raw: string | null, now: number): {
|
|
3
|
+
baseline: undefined;
|
|
4
|
+
discard: boolean;
|
|
5
|
+
reason: string;
|
|
6
|
+
} | {
|
|
7
|
+
baseline: {
|
|
8
|
+
readonly documents: readonly {
|
|
9
|
+
readonly file: string;
|
|
10
|
+
readonly source: string;
|
|
11
|
+
}[];
|
|
12
|
+
readonly recordedAt: string;
|
|
13
|
+
readonly version: 1;
|
|
14
|
+
};
|
|
15
|
+
discard: boolean;
|
|
16
|
+
reason: string;
|
|
17
|
+
};
|
|
18
|
+
export declare function observeBaseline(snapshot: Snapshot, now: number): string | undefined;
|
|
19
|
+
//# sourceMappingURL=baseline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"baseline.d.ts","sourceRoot":"","sources":["../../src/history/baseline.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AASpD,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM;;;;;;;;;;;;;;;EAmB3D;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAWnF"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Option, Schema } from "effect";
|
|
2
|
+
import { MAX_AGE, MAX_BYTES } from "./limits.js";
|
|
3
|
+
import { Baseline } from "./schema.js";
|
|
4
|
+
const JsonBaseline = Schema.fromJsonString(Baseline);
|
|
5
|
+
function byteLength(text) {
|
|
6
|
+
return new TextEncoder().encode(text).length;
|
|
7
|
+
}
|
|
8
|
+
export function readBaseline(raw, now) {
|
|
9
|
+
if (raw === null) {
|
|
10
|
+
return { baseline: undefined, discard: false, reason: "No previous snapshot. Start remembering changes to compare future source observations." };
|
|
11
|
+
}
|
|
12
|
+
if (byteLength(raw) > MAX_BYTES) {
|
|
13
|
+
return { baseline: undefined, discard: true, reason: "Remembered snapshot exceeds 2 MiB; history is unavailable." };
|
|
14
|
+
}
|
|
15
|
+
const decoded = Schema.decodeUnknownOption(JsonBaseline, { onExcessProperty: "error" })(raw);
|
|
16
|
+
if (Option.isNone(decoded)) {
|
|
17
|
+
return { baseline: undefined, discard: true, reason: "Remembered snapshot is invalid or unsupported; history is unavailable." };
|
|
18
|
+
}
|
|
19
|
+
const elapsed = now - Date.parse(decoded.value.recordedAt);
|
|
20
|
+
if (elapsed < 0) {
|
|
21
|
+
return { baseline: undefined, discard: true, reason: "Remembered observation is in the future; history is unavailable." };
|
|
22
|
+
}
|
|
23
|
+
if (elapsed >= MAX_AGE) {
|
|
24
|
+
return { baseline: undefined, discard: true, reason: "Remembered snapshot expired after 30 days; start again to establish a new baseline." };
|
|
25
|
+
}
|
|
26
|
+
return { baseline: decoded.value, discard: false, reason: "" };
|
|
27
|
+
}
|
|
28
|
+
export function observeBaseline(snapshot, now) {
|
|
29
|
+
if (snapshot.unavailable.length > 0) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
const baseline = {
|
|
33
|
+
documents: snapshot.documents.map((document) => ({ file: document.file, source: (document.parsed.raw ?? "") + document.parsed.body })),
|
|
34
|
+
recordedAt: new Date(now).toISOString(),
|
|
35
|
+
version: 1,
|
|
36
|
+
};
|
|
37
|
+
const raw = Schema.encodeSync(JsonBaseline)(baseline);
|
|
38
|
+
return byteLength(raw) <= MAX_BYTES ? raw : undefined;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=baseline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"baseline.js","sourceRoot":"","sources":["../../src/history/baseline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,MAAM,YAAY,GAAG,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;AACrD,SAAS,UAAU,CAAC,IAAY;IAC/B,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAkB,EAAE,GAAW;IAC3D,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QAClB,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,wFAAwF,EAAE,CAAC;IAClJ,CAAC;IACD,IAAI,UAAU,CAAC,GAAG,CAAC,GAAG,SAAS,EAAE,CAAC;QACjC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,4DAA4D,EAAE,CAAC;IACrH,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,mBAAmB,CAAC,YAAY,EAAE,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;IAC7F,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,wEAAwE,EAAE,CAAC;IACjI,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC3D,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QACjB,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,kEAAkE,EAAE,CAAC;IAC3H,CAAC;IACD,IAAI,OAAO,IAAI,OAAO,EAAE,CAAC;QACxB,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,qFAAqF,EAAE,CAAC;IAC9I,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,QAAkB,EAAE,GAAW;IAC9D,IAAI,QAAQ,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrC,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,MAAM,QAAQ,GAAa;QAC1B,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QACtI,UAAU,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE;QACvC,OAAO,EAAE,CAAC;KACV,CAAC;IACF,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,CAAC;IACtD,OAAO,UAAU,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type HistoryDocument } from "./documents.ts";
|
|
2
|
+
import type { Baseline } from "./schema.ts";
|
|
3
|
+
export interface SourceChange {
|
|
4
|
+
readonly after?: HistoryDocument;
|
|
5
|
+
readonly before?: HistoryDocument;
|
|
6
|
+
readonly kind: "added" | "removed" | "changed";
|
|
7
|
+
readonly reasons: readonly string[];
|
|
8
|
+
}
|
|
9
|
+
export declare function compareHistory(baseline: Baseline, current: Baseline): {
|
|
10
|
+
changes: SourceChange[];
|
|
11
|
+
issues: string[];
|
|
12
|
+
newIds: ReadonlyMap<string, HistoryDocument | undefined>;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=compare.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../src/history/compare.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAuC,MAAM,gBAAgB,CAAC;AAE3F,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;IAC/C,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ;;;;EA0CnE"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { historyDocuments, historyIdentities } from "./documents.js";
|
|
2
|
+
import { historyFields } from "./fields.js";
|
|
3
|
+
export function compareHistory(baseline, current) {
|
|
4
|
+
const before = historyDocuments(baseline);
|
|
5
|
+
const after = historyDocuments(current);
|
|
6
|
+
const oldIds = historyIdentities(before);
|
|
7
|
+
const newIds = historyIdentities(after);
|
|
8
|
+
const paired = new Set();
|
|
9
|
+
const remaining = new Set(after);
|
|
10
|
+
const changes = [];
|
|
11
|
+
function pair(old, next) {
|
|
12
|
+
paired.add(old);
|
|
13
|
+
remaining.delete(next);
|
|
14
|
+
if (old.source !== next.source || old.file !== next.file) {
|
|
15
|
+
changes.push({ after: next, before: old, kind: "changed", reasons: historyFields(old, next) });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
for (const [id, old] of oldIds) {
|
|
19
|
+
const next = newIds.get(id);
|
|
20
|
+
if (old && next) {
|
|
21
|
+
pair(old, next);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const byFile = new Map([...remaining].map((document) => [document.file, document]));
|
|
25
|
+
for (const old of before) {
|
|
26
|
+
if (paired.has(old)) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const next = byFile.get(old.file);
|
|
30
|
+
if (next) {
|
|
31
|
+
pair(old, next);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
changes.push({ before: old, kind: "removed", reasons: ["Source absent from the complete current observation"] });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
for (const next of remaining) {
|
|
38
|
+
changes.push({ after: next, kind: "added", reasons: ["Source added since the remembered observation"] });
|
|
39
|
+
}
|
|
40
|
+
const issues = [
|
|
41
|
+
...[...oldIds].filter(([, document]) => !document).map(([id]) => `Duplicate ID ${id} in remembered sources; no identity winner selected.`),
|
|
42
|
+
...[...newIds].filter(([, document]) => !document).map(([id]) => `Duplicate ID ${id} in current sources; no identity winner selected.`),
|
|
43
|
+
];
|
|
44
|
+
changes.sort((a, b) => (a.after?.file ?? a.before?.file ?? "").localeCompare(b.after?.file ?? b.before?.file ?? "", "en"));
|
|
45
|
+
return { changes, issues, newIds };
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=compare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.js","sourceRoot":"","sources":["../../src/history/compare.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ParsedMetadata } from "#metadata/parse.ts";
|
|
2
|
+
import type { Baseline } from "./schema.ts";
|
|
3
|
+
export interface HistoryDocument {
|
|
4
|
+
readonly file: string;
|
|
5
|
+
readonly parsed: ParsedMetadata;
|
|
6
|
+
readonly source: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function historyDocuments(baseline: Baseline): readonly HistoryDocument[];
|
|
9
|
+
export declare function historyIdentities(documents: readonly HistoryDocument[]): ReadonlyMap<string, HistoryDocument | undefined>;
|
|
10
|
+
//# sourceMappingURL=documents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"documents.d.ts","sourceRoot":"","sources":["../../src/history/documents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACxE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB;AACD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,eAAe,EAAE,CAE/E;AACD,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,SAAS,eAAe,EAAE,GAAG,WAAW,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CAAC,CASzH"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { metadataParse } from "#metadata/parse.ts";
|
|
2
|
+
export function historyDocuments(baseline) {
|
|
3
|
+
return baseline.documents.map((document) => ({ ...document, parsed: metadataParse(document.source) }));
|
|
4
|
+
}
|
|
5
|
+
export function historyIdentities(documents) {
|
|
6
|
+
const ids = new Map();
|
|
7
|
+
for (const document of documents) {
|
|
8
|
+
const id = document.parsed.fields.id;
|
|
9
|
+
if (id) {
|
|
10
|
+
ids.set(id, ids.has(id) ? undefined : document);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return ids;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=documents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"documents.js","sourceRoot":"","sources":["../../src/history/documents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAuB,MAAM,oBAAoB,CAAC;AAQxE,MAAM,UAAU,gBAAgB,CAAC,QAAkB;IAClD,OAAO,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,QAAQ,EAAE,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;AACxG,CAAC;AACD,MAAM,UAAU,iBAAiB,CAAC,SAAqC;IACtE,MAAM,GAAG,GAAG,IAAI,GAAG,EAAuC,CAAC;IAC3D,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QAClC,MAAM,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QACrC,IAAI,EAAE,EAAE,CAAC;YACR,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QACjD,CAAC;IACF,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../../src/history/fields.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAetD,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,eAAe,GAAG,SAAS,MAAM,EAAE,CAehG"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const labels = [
|
|
2
|
+
["attention", "Recorded attention requests changed"],
|
|
3
|
+
["criteria", "Criteria changed"],
|
|
4
|
+
["evidence", "Recorded evidence changed (not verified acceptance)"],
|
|
5
|
+
["id", "Item ID changed"],
|
|
6
|
+
["items", "Declared board membership changed"],
|
|
7
|
+
["kind", "Item kind changed"],
|
|
8
|
+
["nextAction", "Next action changed"],
|
|
9
|
+
["owner", "Recorded owner changed (not authorship)"],
|
|
10
|
+
["relationships", "Explicit relationships changed"],
|
|
11
|
+
["status", "Recorded status changed (not acceptance)"],
|
|
12
|
+
];
|
|
13
|
+
export function historyFields(before, after) {
|
|
14
|
+
const changes = labels.flatMap(([field, label]) => JSON.stringify(before.parsed.fields[field]) === JSON.stringify(after.parsed.fields[field]) ? [] : [label]);
|
|
15
|
+
if (before.parsed.body !== after.parsed.body) {
|
|
16
|
+
const decision = before.parsed.fields.kind === "decision" || after.parsed.fields.kind === "decision";
|
|
17
|
+
changes.push(decision ? "Decision Markdown changed" : "Markdown body changed");
|
|
18
|
+
}
|
|
19
|
+
if (changes.length === 0 && before.source !== after.source) {
|
|
20
|
+
changes.push("Other frontmatter content or source formatting changed; no further interpretation");
|
|
21
|
+
}
|
|
22
|
+
if (before.file !== after.file) {
|
|
23
|
+
changes.unshift("Source path changed for the same unique item ID");
|
|
24
|
+
}
|
|
25
|
+
return changes;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=fields.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fields.js","sourceRoot":"","sources":["../../src/history/fields.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,GAAqD;IAChE,CAAC,WAAW,EAAE,qCAAqC,CAAC;IACpD,CAAC,UAAU,EAAE,kBAAkB,CAAC;IAChC,CAAC,UAAU,EAAE,qDAAqD,CAAC;IACnE,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACzB,CAAC,OAAO,EAAE,mCAAmC,CAAC;IAC9C,CAAC,MAAM,EAAE,mBAAmB,CAAC;IAC7B,CAAC,YAAY,EAAE,qBAAqB,CAAC;IACrC,CAAC,OAAO,EAAE,yCAAyC,CAAC;IACpD,CAAC,eAAe,EAAE,gCAAgC,CAAC;IACnD,CAAC,QAAQ,EAAE,0CAA0C,CAAC;CACtD,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,MAAuB,EAAE,KAAsB;IAC5E,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,EAAE,CACjD,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CACzG,CAAC;IACF,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAC9C,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,UAAU,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,UAAU,CAAC;QACrG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC;IAChF,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;QAC5D,OAAO,CAAC,IAAI,CAAC,mFAAmF,CAAC,CAAC;IACnG,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC;QAChC,OAAO,CAAC,OAAO,CAAC,iDAAiD,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,OAAO,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/history/html.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAU5C,wBAAgB,WAAW,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,GAAG,MAAM,CAMvE;AACD,wBAAgB,WAAW,IAAI,MAAM,CAEpC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { fileUrl } from "#files/url.ts";
|
|
2
|
+
import { escapeHtml } from "#page/escape.ts";
|
|
3
|
+
import { compareHistory } from "./compare.js";
|
|
4
|
+
function source(label, text, key) {
|
|
5
|
+
return `<details data-key="${escapeHtml(key)}:${label}" data-history-source="${label.toLowerCase()}"><summary>${label} source (plain text)</summary><pre class="history-source">${escapeHtml(text)}</pre></details>`;
|
|
6
|
+
}
|
|
7
|
+
function changeHtml(change) {
|
|
8
|
+
const file = change.after?.file ?? change.before?.file ?? "";
|
|
9
|
+
const key = change.before ? `before:${change.before.file}` : `added:${file}`;
|
|
10
|
+
return `<article class="history-change" data-key="${escapeHtml(key)}"><h2>${change.kind}: ${escapeHtml(file)}</h2>${change.before && change.after && change.before.file !== file ? `<p>Previously ${escapeHtml(change.before.file)}</p>` : ""}<ul>${change.reasons.map((reason) => `<li>${escapeHtml(reason)}</li>`).join("")}</ul>${change.after ? `<a href="${escapeHtml(fileUrl(change.after.file))}">Read current source</a>` : "<p>Removed source; no current document link.</p>"}${change.before ? source("Remembered", change.before.source, key) : ""}${change.after ? source("Current", change.after.source, key) : ""}</article>`;
|
|
11
|
+
}
|
|
12
|
+
export function historyHtml(before, current) {
|
|
13
|
+
const result = compareHistory(before, current);
|
|
14
|
+
const counts = ["added", "removed", "changed"]
|
|
15
|
+
.map((kind) => `${result.changes.filter((change) => change.kind === kind).length} ${kind}`)
|
|
16
|
+
.join(" · ");
|
|
17
|
+
return `<p>Compared with your observation at <time>${escapeHtml(before.recordedAt)}</time>.</p><p role="status">${counts}</p>${result.changes.length === 0 ? "<p>No source changes since that observation.</p>" : ""}${result.issues.length > 0 ? `<aside aria-label="Identity limitations"><ul>${result.issues.map((issue) => `<li>${escapeHtml(issue)}</li>`).join("")}</ul><p>Ambiguous identities are compared by source path only.</p></aside>` : ""}${result.changes.map(changeHtml).join("")}`;
|
|
18
|
+
}
|
|
19
|
+
export function changesHtml() {
|
|
20
|
+
return `<section class="history"><h1>Changes since seen</h1><p>Compare current Markdown with one observation you explicitly remember in this browser. This is an observed source comparison, not an event journal, Git history, authorship, or verified acceptance.</p><p>Remembered source can include old or deleted content. One snapshot per workspace, up to 2 MiB, expires after 30 days and is removed on your next workspace visit. Mark seen replaces it; Clear forgets it. No source writes or cloud storage.</p><div class="history-controls"><button id="history-mark" type="button" disabled>Start remembering changes</button><button id="history-clear" type="button" disabled>Clear remembered history</button><span id="history-storage" role="status"></span></div><noscript><p>Remembering and comparing changes requires JavaScript and browser storage. Markdown, Overview and Work remain readable.</p></noscript><div id="changes-content" aria-live="polite"><p>No comparison has been loaded.</p></div></section><style>.history-controls{display:flex;flex-wrap:wrap;gap:.6rem;align-items:center}.history-change{border-top:1px solid var(--border);padding:1rem 0}.history-source{white-space:pre-wrap;overflow-wrap:anywhere;max-height:32rem;overflow:auto}.history h2{overflow-wrap:anywhere}</style>`;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=html.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html.js","sourceRoot":"","sources":["../../src/history/html.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/history/limits.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,QAAkB,CAAC;AACzC,eAAO,MAAM,OAAO,QAA2B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"limits.js","sourceRoot":"","sources":["../../src/history/limits.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AACzC,MAAM,CAAC,MAAM,OAAO,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../src/history/response.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAGpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,aAAa,CAgBnG"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { escapeHtml } from "#page/escape.ts";
|
|
2
|
+
import { observeBaseline, readBaseline } from "./baseline.js";
|
|
3
|
+
import { historyHtml } from "./html.js";
|
|
4
|
+
export function historyResponse(raw, now, snapshot) {
|
|
5
|
+
const prior = readBaseline(raw, now);
|
|
6
|
+
const output = { discardBaseline: prior.discard, html: `<p>${escapeHtml(prior.reason)}</p>`, reason: prior.reason, snapshot: null };
|
|
7
|
+
if (snapshot === undefined) {
|
|
8
|
+
return output;
|
|
9
|
+
}
|
|
10
|
+
const currentRaw = observeBaseline(snapshot, now);
|
|
11
|
+
if (currentRaw === undefined) {
|
|
12
|
+
const reason = snapshot.unavailable.length > 0
|
|
13
|
+
? "Current workspace observation is incomplete; no comparison or removals are inferred. Your valid remembered snapshot is preserved."
|
|
14
|
+
: "Complete current snapshot exceeds 2 MiB; no comparison or partial history is retained.";
|
|
15
|
+
return { ...output, html: `<p role="status">${escapeHtml(reason)}</p>`, reason };
|
|
16
|
+
}
|
|
17
|
+
const current = readBaseline(currentRaw, now).baseline;
|
|
18
|
+
return { ...output, html: prior.baseline && current ? historyHtml(prior.baseline, current) : output.html, snapshot: currentRaw };
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=response.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response.js","sourceRoot":"","sources":["../../src/history/response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAGxC,MAAM,UAAU,eAAe,CAAC,GAAkB,EAAE,GAAW,EAAE,QAAmB;IACnF,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACrC,MAAM,MAAM,GAAkB,EAAE,eAAe,EAAE,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACnJ,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,MAAM,CAAC;IACf,CAAC;IACD,MAAM,UAAU,GAAG,eAAe,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAClD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC9B,MAAM,MAAM,GACX,QAAQ,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC;YAC9B,CAAC,CAAC,mIAAmI;YACrI,CAAC,CAAC,wFAAwF,CAAC;QAC7F,OAAO,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,oBAAoB,UAAU,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAClF,CAAC;IACD,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC;IACvD,OAAO,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;AAClI,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
export declare const Baseline: Schema.encodeKeys<Schema.Struct<{
|
|
3
|
+
readonly documents: Schema.$Array<Schema.Struct<{
|
|
4
|
+
readonly file: Schema.String;
|
|
5
|
+
readonly source: Schema.String;
|
|
6
|
+
}>>;
|
|
7
|
+
readonly recordedAt: Schema.String;
|
|
8
|
+
readonly version: Schema.Literal<1>;
|
|
9
|
+
}>, {
|
|
10
|
+
readonly recordedAt: "recorded_at";
|
|
11
|
+
}>;
|
|
12
|
+
export type Baseline = typeof Baseline.Type;
|
|
13
|
+
export declare const HistoryInput: Schema.Struct<{
|
|
14
|
+
readonly action: Schema.Literals<readonly ["check", "compare", "observe"]>;
|
|
15
|
+
readonly baseline: Schema.NullOr<Schema.String>;
|
|
16
|
+
}>;
|
|
17
|
+
export declare const HistoryOutput: Schema.Struct<{
|
|
18
|
+
readonly discardBaseline: Schema.Boolean;
|
|
19
|
+
readonly html: Schema.String;
|
|
20
|
+
readonly reason: Schema.String;
|
|
21
|
+
readonly snapshot: Schema.NullOr<Schema.String>;
|
|
22
|
+
}>;
|
|
23
|
+
export type HistoryOutput = typeof HistoryOutput.Type;
|
|
24
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/history/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAiBhC,eAAO,MAAM,QAAQ;;;;;;;;;EAQoC,CAAC;AAC1D,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAC;AAE5C,eAAO,MAAM,YAAY;;;EAAsH,CAAC;AAChJ,eAAO,MAAM,aAAa;;;;;EAKxB,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAC,IAAI,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { isMarkdown } from "#files/list.ts";
|
|
3
|
+
const File = Schema.String.check(Schema.makeFilter((value) => (isMarkdown(value) && !value.includes("\\") && !value.includes("\0") && value.split("/").every((part) => part.length > 0 && part !== ".."))
|
|
4
|
+
|| "Expected a relative readable Markdown path"));
|
|
5
|
+
const ObservedTime = Schema.String.check(Schema.makeFilter((value) => (Number.isFinite(Date.parse(value)) && /^\d{4}-\d{2}-\d{2}T.*(?:Z|[+-]\d{2}:\d{2})$/u.exec(value) !== null)
|
|
6
|
+
|| "Expected an ISO observation time with timezone"));
|
|
7
|
+
export const Baseline = Schema.Struct({
|
|
8
|
+
documents: Schema.Array(Schema.Struct({ file: File, source: Schema.String })).check(Schema.makeFilter((values) => new Set(values.map((value) => value.file)).size === values.length || "Duplicate source paths are not a complete observation")),
|
|
9
|
+
recordedAt: ObservedTime,
|
|
10
|
+
version: Schema.Literal(1),
|
|
11
|
+
}).pipe(Schema.encodeKeys({ recordedAt: "recorded_at" }));
|
|
12
|
+
export const HistoryInput = Schema.Struct({ action: Schema.Literals(["check", "compare", "observe"]), baseline: Schema.NullOr(Schema.String) });
|
|
13
|
+
export const HistoryOutput = Schema.Struct({
|
|
14
|
+
discardBaseline: Schema.Boolean,
|
|
15
|
+
html: Schema.String,
|
|
16
|
+
reason: Schema.String,
|
|
17
|
+
snapshot: Schema.NullOr(Schema.String),
|
|
18
|
+
});
|
|
19
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/history/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5C,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAC/B,MAAM,CAAC,UAAU,CAChB,CAAC,KAAK,EAAE,EAAE,CACT,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC;OACxI,4CAA4C,CAChD,CACD,CAAC;AACF,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CACvC,MAAM,CAAC,UAAU,CAChB,CAAC,KAAK,EAAE,EAAE,CACT,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,8CAA8C,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC;OACxG,gDAAgD,CACpD,CACD,CAAC;AACF,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC;IACrC,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,CAClF,MAAM,CAAC,UAAU,CAChB,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,MAAM,IAAI,uDAAuD,CACxI,CACD;IACD,UAAU,EAAE,YAAY;IACxB,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;CAC1B,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,UAAU,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;AAG1D,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAChJ,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC;IAC1C,eAAe,EAAE,MAAM,CAAC,OAAO;IAC/B,IAAI,EAAE,MAAM,CAAC,MAAM;IACnB,MAAM,EAAE,MAAM,CAAC,MAAM;IACrB,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;CACtC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"script.d.ts","sourceRoot":"","sources":["../../src/history/script.ts"],"names":[],"mappings":"AAAA,wBAAgB,aAAa,IAAI,MAAM,CA2HtC"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
export function historyScript() {
|
|
2
|
+
return `
|
|
3
|
+
import { swap, remember } from "/_board/swap.js";
|
|
4
|
+
import { captureReading, restoreReading } from "/_board/reading-state.js";
|
|
5
|
+
const key = "work-board:changes:" + document.documentElement.dataset.workspace;
|
|
6
|
+
let memory;
|
|
7
|
+
let storageNotice = "";
|
|
8
|
+
let unavailableHtml = "";
|
|
9
|
+
let generation = 0;
|
|
10
|
+
let controller;
|
|
11
|
+
let observing = null;
|
|
12
|
+
const baseline = () => {
|
|
13
|
+
if (memory !== undefined) return memory;
|
|
14
|
+
try { return localStorage.getItem(key); }
|
|
15
|
+
catch { memory = null; storageNotice = "Browser storage unavailable; history applies to this page only."; return memory; }
|
|
16
|
+
};
|
|
17
|
+
const forget = () => {
|
|
18
|
+
memory = null;
|
|
19
|
+
try { localStorage.removeItem(key); memory = undefined; }
|
|
20
|
+
catch { storageNotice = "Cleared for this page, but browser storage could not be cleared. Reload may restore older data."; }
|
|
21
|
+
};
|
|
22
|
+
const keep = (raw) => {
|
|
23
|
+
memory = raw;
|
|
24
|
+
try { localStorage.setItem(key, raw); memory = undefined; storageNotice = ""; }
|
|
25
|
+
catch {
|
|
26
|
+
storageNotice = "Snapshot remembered for this page only; browser storage is unavailable.";
|
|
27
|
+
try { localStorage.removeItem(key); }
|
|
28
|
+
catch { storageNotice += " Reload may restore older data."; }
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const controls = (available, busy = false) => {
|
|
32
|
+
const mark = document.getElementById("history-mark");
|
|
33
|
+
const clear = document.getElementById("history-clear");
|
|
34
|
+
if (!mark || !clear) return;
|
|
35
|
+
const exists = baseline() !== null;
|
|
36
|
+
mark.textContent = exists ? "Mark current workspace seen" : "Start remembering changes";
|
|
37
|
+
mark.disabled = busy || !available;
|
|
38
|
+
clear.disabled = busy || !exists;
|
|
39
|
+
document.getElementById("history-storage").textContent = storageNotice;
|
|
40
|
+
};
|
|
41
|
+
const render = (html) => {
|
|
42
|
+
const content = document.getElementById("changes-content");
|
|
43
|
+
if (!content) return;
|
|
44
|
+
const reading = captureReading();
|
|
45
|
+
const next = content.cloneNode(false);
|
|
46
|
+
next.innerHTML = html;
|
|
47
|
+
swap(content, next);
|
|
48
|
+
remember(content);
|
|
49
|
+
restoreReading({ ...reading, selection: null }, false);
|
|
50
|
+
};
|
|
51
|
+
const query = async (action) => {
|
|
52
|
+
const mine = ++generation;
|
|
53
|
+
controller?.abort();
|
|
54
|
+
controller = new AbortController();
|
|
55
|
+
const raw = baseline();
|
|
56
|
+
const response = await fetch("/_board/history", {
|
|
57
|
+
method: "POST", headers: { "content-type": "application/json" },
|
|
58
|
+
body: JSON.stringify({ action, baseline: raw }), signal: controller.signal, cache: "no-store",
|
|
59
|
+
});
|
|
60
|
+
if (!response.ok) throw new Error("History query failed");
|
|
61
|
+
const result = await response.json();
|
|
62
|
+
if (mine !== generation) return null;
|
|
63
|
+
if (result.discardBaseline && baseline() === raw) { forget(); unavailableHtml = result.html; }
|
|
64
|
+
return result;
|
|
65
|
+
};
|
|
66
|
+
const refresh = async () => {
|
|
67
|
+
if (observing !== null) return;
|
|
68
|
+
const visible = document.getElementById("doc")?.dataset.view === "changes";
|
|
69
|
+
try {
|
|
70
|
+
const result = await query(visible ? "compare" : "check");
|
|
71
|
+
if (!result) return;
|
|
72
|
+
if (visible && document.getElementById("doc")?.dataset.view === "changes") {
|
|
73
|
+
render(baseline() === null && unavailableHtml ? unavailableHtml : result.html);
|
|
74
|
+
controls(result.snapshot !== null);
|
|
75
|
+
}
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (error.name === "AbortError") return;
|
|
78
|
+
if (visible) {
|
|
79
|
+
render("<p>History is unavailable: the current observation could not be loaded. No comparison or removals are inferred.</p>");
|
|
80
|
+
controls(false);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
document.addEventListener("click", async (event) => {
|
|
85
|
+
if (event.target.closest?.("#history-clear")) {
|
|
86
|
+
observing = null;
|
|
87
|
+
++generation;
|
|
88
|
+
controller?.abort();
|
|
89
|
+
forget();
|
|
90
|
+
unavailableHtml = "";
|
|
91
|
+
await refresh();
|
|
92
|
+
}
|
|
93
|
+
if (event.target.closest?.("#history-mark")) {
|
|
94
|
+
if (observing !== null) return;
|
|
95
|
+
const mine = generation + 1;
|
|
96
|
+
observing = mine;
|
|
97
|
+
controls(false, true);
|
|
98
|
+
try {
|
|
99
|
+
const result = await query("observe");
|
|
100
|
+
if (!result) return;
|
|
101
|
+
if (result.snapshot === null) { render(result.html); controls(false); return; }
|
|
102
|
+
keep(result.snapshot);
|
|
103
|
+
unavailableHtml = "";
|
|
104
|
+
} catch (error) {
|
|
105
|
+
if (error.name !== "AbortError") { render("<p>Could not remember the current observation. Your previous baseline is preserved.</p>"); controls(false); }
|
|
106
|
+
return;
|
|
107
|
+
} finally { if (observing === mine) observing = null; }
|
|
108
|
+
await refresh();
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
document.addEventListener("board-page", refresh);
|
|
112
|
+
document.addEventListener("visibilitychange", () => { if (!document.hidden) refresh(); });
|
|
113
|
+
window.addEventListener("storage", (event) => {
|
|
114
|
+
if (event.key === key || event.key === null) {
|
|
115
|
+
observing = null;
|
|
116
|
+
++generation;
|
|
117
|
+
controller?.abort();
|
|
118
|
+
memory = undefined; storageNotice = ""; unavailableHtml = ""; refresh();
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
setInterval(() => { if (baseline() !== null) refresh(); }, 60000);
|
|
122
|
+
refresh();
|
|
123
|
+
`;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=script.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"script.js","sourceRoot":"","sources":["../../src/history/script.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,aAAa;IAC5B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyHP,CAAC;AACF,CAAC"}
|