feat(shell,api,keys): firefly v1 shell over the api with cookie auth, period, palette and keymap
This commit is contained in:
1 parent
ae4de7911b
commit
c74dac3e00
37 files changed
+58163
-79
No files matched your search
@@ -0,0 +1,150 @@
|
||||
export interface Range {
|
||||
end: string;
|
||||
start: string;
|
||||
}
|
||||
|
||||
export type ViewRange =
|
||||
| "1D"
|
||||
| "1W"
|
||||
| "last7"
|
||||
| "1M"
|
||||
| "last30"
|
||||
| "MTD"
|
||||
| "3M"
|
||||
| "last90"
|
||||
| "QTD"
|
||||
| "6M"
|
||||
| "1Y"
|
||||
| "YTD"
|
||||
| "last365";
|
||||
|
||||
const DYNAMIC = new Set<ViewRange>([
|
||||
"last7",
|
||||
"last30",
|
||||
"last90",
|
||||
"last365",
|
||||
"MTD",
|
||||
"QTD",
|
||||
"YTD",
|
||||
]);
|
||||
|
||||
const pad = (n: number) => String(n).padStart(2, "0");
|
||||
|
||||
export const iso = (d: Date) =>
|
||||
`${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
|
||||
|
||||
export function parse(value: string): Date {
|
||||
const [y, m, d] = value.slice(0, 10).split("-").map(Number);
|
||||
return new Date(y, m - 1, d);
|
||||
}
|
||||
|
||||
export const today = () => iso(new Date());
|
||||
|
||||
export function addDays(value: string, days: number): string {
|
||||
const d = parse(value);
|
||||
d.setDate(d.getDate() + days);
|
||||
return iso(d);
|
||||
}
|
||||
|
||||
export function addMonths(value: string, months: number): string {
|
||||
const d = parse(value);
|
||||
const day = d.getDate();
|
||||
d.setDate(1);
|
||||
d.setMonth(d.getMonth() + months);
|
||||
const last = new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate();
|
||||
d.setDate(Math.min(day, last));
|
||||
return iso(d);
|
||||
}
|
||||
|
||||
export const isDynamic = (view: ViewRange) => DYNAMIC.has(view);
|
||||
|
||||
function startOf(value: string, view: ViewRange): string {
|
||||
const d = parse(value);
|
||||
switch (view) {
|
||||
case "1D":
|
||||
return value;
|
||||
case "1W": {
|
||||
const shift = (d.getDay() + 6) % 7;
|
||||
return addDays(value, -shift);
|
||||
}
|
||||
case "3M":
|
||||
return iso(
|
||||
new Date(d.getFullYear(), Math.floor(d.getMonth() / 3) * 3, 1)
|
||||
);
|
||||
case "6M":
|
||||
return iso(new Date(d.getFullYear(), d.getMonth() < 6 ? 0 : 6, 1));
|
||||
case "1Y":
|
||||
return iso(new Date(d.getFullYear(), 0, 1));
|
||||
default:
|
||||
return iso(new Date(d.getFullYear(), d.getMonth(), 1));
|
||||
}
|
||||
}
|
||||
|
||||
const LENGTH_MONTHS: Partial<Record<ViewRange, number>> = {
|
||||
"1M": 1,
|
||||
"1Y": 12,
|
||||
"3M": 3,
|
||||
"6M": 6,
|
||||
};
|
||||
|
||||
export function periodOf(value: string, view: ViewRange): Range {
|
||||
const now = today();
|
||||
switch (view) {
|
||||
case "last7":
|
||||
return { end: now, start: addDays(now, -7) };
|
||||
case "last30":
|
||||
return { end: now, start: addDays(now, -30) };
|
||||
case "last90":
|
||||
return { end: now, start: addDays(now, -90) };
|
||||
case "last365":
|
||||
return { end: now, start: addDays(now, -365) };
|
||||
case "MTD":
|
||||
return { end: now, start: startOf(now, "1M") };
|
||||
case "QTD":
|
||||
return { end: now, start: startOf(now, "3M") };
|
||||
case "YTD":
|
||||
return { end: now, start: startOf(now, "1Y") };
|
||||
case "1D":
|
||||
return { end: value, start: value };
|
||||
case "1W": {
|
||||
const start = startOf(value, "1W");
|
||||
return { end: addDays(start, 6), start };
|
||||
}
|
||||
default: {
|
||||
const start = startOf(value, view);
|
||||
const months = LENGTH_MONTHS[view] ?? 1;
|
||||
return { end: addDays(addMonths(start, months), -1), start };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function shiftPeriod(range: Range, view: ViewRange, dir: 1 | -1): Range {
|
||||
const fixed = isDynamic(view) ? "1M" : view;
|
||||
const aligned = periodOf(range.start, fixed);
|
||||
if (aligned.start === range.start && aligned.end === range.end) {
|
||||
const anchor = dir === 1 ? addDays(range.end, 1) : addDays(range.start, -1);
|
||||
return periodOf(anchor, fixed);
|
||||
}
|
||||
const days = daysBetween(range.start, range.end) + 1;
|
||||
return {
|
||||
end: addDays(range.end, dir * days),
|
||||
start: addDays(range.start, dir * days),
|
||||
};
|
||||
}
|
||||
|
||||
export function daysBetween(a: string, b: string): number {
|
||||
return Math.round((parse(b).getTime() - parse(a).getTime()) / 86_400_000);
|
||||
}
|
||||
|
||||
export function eachDay(range: Range): string[] {
|
||||
const days: string[] = [];
|
||||
for (let d = range.start; d <= range.end; d = addDays(d, 1)) {
|
||||
days.push(d);
|
||||
}
|
||||
return days;
|
||||
}
|
||||
|
||||
export function contains(range: Range, value: string): boolean {
|
||||
const day = value.slice(0, 10);
|
||||
return day >= range.start && day <= range.end;
|
||||
}
|
||||
Reference in new issue
Block a user