Files
dotfiles/pi/files/agent/extensions/sub-bar/src/share.ts
T

76 lines
2.3 KiB
TypeScript

/**
* Display theme share helpers.
*/
import type { Settings } from "./settings-types.js";
import { mergeSettings } from "./settings-types.js";
const SHARE_SEPARATOR = ":";
const DISPLAY_SHARE_VERSION = 1;
export interface DisplaySharePayload {
v: number;
display: Settings["display"];
}
export interface DecodedDisplayShare {
name: string;
display: Settings["display"];
version: number;
isNewerVersion: boolean;
hasName: boolean;
}
function encodeDisplaySharePayload(display: Settings["display"]): string {
const payload: DisplaySharePayload = { v: DISPLAY_SHARE_VERSION, display };
return Buffer.from(JSON.stringify(payload)).toString("base64url");
}
export function buildDisplayShareString(name: string, display: Settings["display"]): string {
const encoded = encodeDisplaySharePayload(display);
const trimmedName = name.trim() || "custom";
return `${trimmedName}${SHARE_SEPARATOR}${encoded}`;
}
export function buildDisplayShareStringWithoutName(display: Settings["display"]): string {
return encodeDisplaySharePayload(display);
}
export function decodeDisplayShareString(input: string): DecodedDisplayShare | null {
const trimmed = input.trim();
if (!trimmed) return null;
let name = "custom";
let hasName = false;
let payload = trimmed;
const separatorIndex = trimmed.indexOf(SHARE_SEPARATOR);
if (separatorIndex >= 0) {
const candidateName = trimmed.slice(0, separatorIndex).trim();
payload = trimmed.slice(separatorIndex + 1).trim();
if (candidateName) {
name = candidateName;
hasName = true;
}
}
if (!payload) return null;
try {
const decoded = Buffer.from(payload, "base64url").toString("utf-8");
const parsed = JSON.parse(decoded) as unknown;
if (!parsed || typeof parsed !== "object") return null;
const displayCandidate = (parsed as DisplaySharePayload).display ?? parsed;
if (!displayCandidate || typeof displayCandidate !== "object" || Array.isArray(displayCandidate)) {
return null;
}
const merged = mergeSettings({ display: displayCandidate } as Partial<Settings>).display;
const version = typeof (parsed as DisplaySharePayload).v === "number" ? (parsed as DisplaySharePayload).v : 0;
return {
name,
display: merged,
version,
isNewerVersion: version > DISPLAY_SHARE_VERSION,
hasName,
};
} catch {
return null;
}
}