Export template source from 11bc3ae35b0034c113daca4c35d1b341aa31ef70:examples/local-first/packages/language-reader-presentation

This commit is contained in:
Quixos Template Publisher
2000-01-01 00:00:00 +00:00
commit 131148e054
61 changed files with 10297 additions and 0 deletions
+224
View File
@@ -0,0 +1,224 @@
import { sameCacheConfiguration } from "./cache.js";
import { intersects, type Range } from "./canvas/types.js";
export type Feature = {
processor: string;
generation: string;
scope: string;
reveal: string;
configRevision: number;
enabled: boolean;
};
export type Job = { sourceRevision: string; featureRevision: number; start: number; end: number; status: string };
export function processorMatches(job: { key: string }, revision: string, previousRevisions: readonly string[] = []) {
return [revision, ...previousRevisions].some((revision) => job.key.startsWith(revision + ":"));
}
export function sameScope(
job: Job & { configuration: string },
sourceRevision: string,
configuration: string,
scope: Range,
) {
return (
job.sourceRevision === sourceRevision &&
sameCacheConfiguration(job.configuration, configuration) &&
job.start === scope.start &&
job.end === scope.end
);
}
type SemanticJob = {
configuration: string;
data: {
key: string;
kind: string;
status: string;
sourceRevision: string;
start: number;
end: number;
createdAt: string;
};
};
export type AnnotationRetryFeature<F> = {
feature: F;
configuration: string;
revision: string;
aliases?: readonly string[];
styles: readonly string[];
};
/** Nearby controls describe the newest attempt for a semantic scope, not its
* history. Shared renderer instances and verified processor aliases must not
* multiply controls. Sidebar computations already have their own controls. */
export function selectAnnotationRetryJobs<F, J extends SemanticJob>(
jobs: readonly J[],
features: readonly AnnotationRetryFeature<F>[],
sourceRevision: string,
visible: Range,
) {
const scopes: { configuration: string; start: number; end: number; revision: string }[] = [];
const result: { feature: F; job: J }[] = [];
if (visible.end <= visible.start) return result;
for (const job of [...jobs].sort((a, b) => b.data.createdAt.localeCompare(a.data.createdAt))) {
if (job.data.kind !== "annotation" || job.data.sourceRevision !== sourceRevision || !intersects(job.data, visible))
continue;
const matching = features.filter(
(feature) =>
feature.revision &&
processorMatches(job.data, feature.revision, feature.aliases) &&
sameCacheConfiguration(feature.configuration, job.configuration),
);
const feature = matching.find((feature) => feature.styles.some((style) => style !== "sidebar"));
if (!feature || matching.some((feature) => feature.styles.includes("sidebar"))) continue;
if (
scopes.some(
(scope) =>
scope.revision === feature.revision &&
scope.start === job.data.start &&
scope.end === job.data.end &&
sameCacheConfiguration(scope.configuration, job.configuration),
)
)
continue;
scopes.push({
configuration: job.configuration,
start: job.data.start,
end: job.data.end,
revision: feature.revision,
});
if (["failed", "skipped", "uncertain"].includes(job.data.status)) result.push({ feature: feature.feature, job });
}
return result;
}
export function annotationRetryStatus(status: string, error: string) {
if (error.includes("AGENT_SETUP_UNAVAILABLE")) return "Setup was unavailable. Try again when connected.";
if (status === "skipped") return "Not prepared. Try again when ready.";
if (status === "uncertain") return "Preparation could not be confirmed.";
return "Could not prepare annotations.";
}
/** Only an explicit control invokes this dispatcher. Guard double clicks until
* the public operation settles; neither selection nor a status update retries. */
export function explicitAnnotationRetry(
key: string,
inFlight: Set<string>,
dispatch: (settled: () => void) => boolean,
changed: () => void,
) {
if (inFlight.has(key)) return false;
inFlight.add(key);
changed();
const settled = () => {
inFlight.delete(key);
changed();
};
try {
if (dispatch(settled)) return true;
settled();
return false;
} catch (error) {
settled();
throw error;
}
}
/** A successful task replaces its whole semantic scope, including omitted
* annotations. Deduplicating individual items resurrects removed old output. */
export function selectSuccessfulAnnotationJobs<T extends SemanticJob>(
jobs: readonly T[],
matches: (job: T) => boolean,
) {
const scopes = new Map<string, string[]>();
return jobs
.filter((job) => job.data.kind === "annotation" && job.data.status === "ready" && matches(job))
.sort((a, b) => b.data.createdAt.localeCompare(a.data.createdAt))
.filter((job) => {
const key = JSON.stringify([job.data.sourceRevision, job.data.start, job.data.end]);
const configurations = scopes.get(key) ?? [];
if (configurations.some((configuration) => sameCacheConfiguration(configuration, job.configuration)))
return false;
configurations.push(job.configuration);
scopes.set(key, configurations);
return true;
});
}
/** Index pointers are authoritative even if an older successful attempt was
* explicitly restored. Keep attempt history for progress/errors, not as a
* second successful result for the same indexed key. */
export function mergeCachedAnnotationEntries<
T extends {
node: { _camino: { ref: { object: string } }; data: { key: string; kind: string; status: string } };
},
>(cached: readonly T[], history: readonly T[]) {
const keys = new Set<string>();
const entries = new Map<string, T>();
for (const entry of cached) {
if (keys.has(entry.node.data.key)) continue;
keys.add(entry.node.data.key);
entries.set(entry.node._camino.ref.object, entry);
}
for (const entry of history) {
if (entry.node.data.kind === "annotation" && entry.node.data.status === "ready" && keys.has(entry.node.data.key))
continue;
if (!entries.has(entry.node._camino.ref.object)) entries.set(entry.node._camino.ref.object, entry);
}
return [...entries.values()];
}
export type PreparationScope = { key: string; configuration: string; range: Range; local: boolean };
/** Independent scopes may share the two model slots. Nearby features take turns
* at equal distance; completed, failed and already-submitted work is excluded. */
export function preparationQueue<T extends PreparationScope>(
plans: T[],
visible: Range,
occupied: number,
submitted: ReadonlySet<string>,
cached: ReadonlySet<string>,
) {
const groups = new Map<string, T[]>(),
seen = new Set<string>();
for (const plan of plans) {
if (plan.range.end <= plan.range.start || seen.has(plan.key) || submitted.has(plan.key) || cached.has(plan.key))
continue;
seen.add(plan.key);
const group = groups.get(plan.configuration) ?? [];
group.push(plan);
groups.set(plan.configuration, group);
}
const waiting: T[] = [];
for (let index = 0; ; index++) {
let found = false;
for (const group of groups.values())
if (group[index]) {
waiting.push(group[index]);
found = true;
}
if (!found) break;
}
const distance = ({ range }: T) =>
range.end <= visible.start
? visible.start - range.end + 1
: range.start >= visible.end
? range.start - visible.end + 1
: 0;
waiting.sort((a, b) => distance(a) - distance(b));
let slots = Math.max(0, 2 - occupied);
const admit = waiting.filter((plan) => plan.local || slots-- > 0);
return { waiting, admit };
}
/** Intent scheduler for heavy hover work. It never owns the durable model job:
* leaving hover cancels intent; an admitted result remains useful in its cache.
* Local/section/cached reveal happens outside this scheduler, immediately.
*/
export class HoverIntent {
private timer: ReturnType<typeof setTimeout> | null = null;
private generation = 0;
constructor(private readonly delay = 420) {}
cancel() {
this.generation++;
if (this.timer !== null) clearTimeout(this.timer);
this.timer = null;
}
schedule(callback: () => void) {
this.cancel();
const generation = this.generation;
this.timer = setTimeout(() => {
this.timer = null;
if (generation === this.generation) callback();
}, this.delay);
}
}