feat(shell,api,keys): firefly v1 shell over the api with cookie auth, period, palette and keymap

This commit is contained in:
hh committed 2026-10-16 02:21:45 +00:00
1 parent ae4de7911b
commit c74dac3e00
37 files changed
+58163 -79

No files matched your search

+130
View File
@@ -0,0 +1,130 @@
import {
addDays,
isDynamic,
periodOf,
type Range,
shiftPeriod,
today,
type ViewRange,
} from "#lib/dates.js";
import { formatDate, formatRange } from "#lib/format.js";
const STORAGE_KEY = "ffui.period";
const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
export interface Preset {
label: string;
range: Range;
}
function stored(): Range | null {
try {
const value = JSON.parse(localStorage.getItem(STORAGE_KEY) ?? "null");
return ISO_DATE.test(value?.start) && ISO_DATE.test(value?.end)
? value
: null;
} catch {
return null;
}
}
export function rangeFromUrl(url: {
searchParams: { get: (name: string) => string | null };
}): Range | null {
const start = url.searchParams.get("start") ?? "";
const end = url.searchParams.get("end") ?? "";
return ISO_DATE.test(start) && ISO_DATE.test(end) && start <= end
? { end, start }
: null;
}
export function periodLabel(range: Range, view: ViewRange): string {
const aligned = periodOf(range.start, isDynamic(view) ? "1M" : view);
if (aligned.start === range.start && aligned.end === range.end) {
if (view === "1M" || isDynamic(view)) {
return formatDate(range.start, "month");
}
if (view === "1Y") {
return formatDate(range.start, "year");
}
}
return formatRange(range.start, range.end);
}
class Period {
view = $state<ViewRange>("1M");
start = $state(today());
end = $state(today());
first = $state<string | null>(null);
ready = $state(false);
range = $derived<Range>({ end: this.end, start: this.start });
label = $derived(periodLabel(this.range, this.view));
isCurrent = $derived(this.start <= today() && today() <= this.end);
init(view: ViewRange, url: Parameters<typeof rangeFromUrl>[0]) {
this.view = view;
const range = rangeFromUrl(url) ?? stored() ?? periodOf(today(), view);
this.start = range.start;
this.end = range.end;
this.ready = true;
}
set(range: Range) {
if (range.start === this.start && range.end === this.end) {
return;
}
this.start = range.start;
this.end = range.end;
localStorage.setItem(STORAGE_KEY, JSON.stringify(range));
}
shift(dir: 1 | -1) {
this.set(shiftPeriod(this.range, this.view, dir));
}
reset() {
this.set(periodOf(today(), this.view));
}
presets(): Preset[] {
const now = today();
const { view } = this;
const presets: Preset[] = [];
if (!isDynamic(view)) {
const prev = shiftPeriod(this.range, view, -1);
const next = shiftPeriod(this.range, view, 1);
presets.push({ label: periodLabel(prev, view), range: prev });
presets.push({ label: periodLabel(next, view), range: next });
}
const current = periodOf(now, view);
presets.push({
label: `Today (${periodLabel(current, view)})`,
range: current,
});
presets.push({
label: "Last seven days",
range: { end: now, start: addDays(now, -7) },
});
presets.push({
label: "Last thirty days",
range: { end: now, start: addDays(now, -30) },
});
presets.push({ label: "Month to date", range: periodOf(now, "MTD") });
presets.push({ label: "Year to date", range: periodOf(now, "YTD") });
const year = Number(now.slice(0, 4)) - 1;
presets.push({
label: `Previous year (${year})`,
range: { end: `${year}-12-31`, start: `${year}-01-01` },
});
if (this.first) {
presets.push({
label: "Everything",
range: { end: now, start: this.first },
});
}
return presets;
}
}
export const period = new Period();