chore: rename project from idfoto to relicario

Sweeping rename across crates, CLI binary, WASM bindings, extension, docs,
and vault metadata paths. Git remote updated to relicario.git.

- crates/idfoto-{core,cli,wasm} -> crates/relicario-{core,cli,wasm}
- IdfotoError -> RelicarioError
- IDFOTO_IMAGE env var -> RELICARIO_IMAGE
- ~/.config/idfoto -> ~/.config/relicario
- .idfoto/ vault metadata dir -> .relicario/ (breaking; pre-release)
- Binary name idfoto -> relicario
- Extension wasm module idfoto_wasm -> relicario_wasm
- Storage key idfotoSettings -> relicarioSettings
- All doc filenames and content references updated

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
adlee-was-taken
2026-04-19 16:47:02 -04:00
parent 20ff1d9f47
commit 519a6f0e36
51 changed files with 949 additions and 949 deletions

View File

@@ -4,7 +4,7 @@
/// credentials in the vault. Supports bar and toast prompt styles.
import type { Request, Response } from '../shared/messages';
import type { IdfotoSettings } from '../shared/types';
import type { RelicarioSettings } from '../shared/types';
// --- State ---
@@ -89,8 +89,8 @@ async function onFormSubmit(pwField: HTMLInputElement): Promise<void> {
// Fetch settings for prompt style
const settingsResp = await sendMessage({ type: 'get_settings' });
const settings: IdfotoSettings = settingsResp.ok
? (settingsResp.data as { settings: IdfotoSettings }).settings
const settings: RelicarioSettings = settingsResp.ok
? (settingsResp.data as { settings: RelicarioSettings }).settings
: { captureEnabled: true, captureStyle: 'bar' };
showPrompt(settings.captureStyle, data.action, url, username, password, data.entryId);
@@ -99,7 +99,7 @@ async function onFormSubmit(pwField: HTMLInputElement): Promise<void> {
// --- Prompt UI ---
function removeExistingPrompt(): void {
const existing = document.getElementById('idfoto-capture-prompt');
const existing = document.getElementById('relicario-capture-prompt');
if (existing) existing.remove();
}
@@ -121,7 +121,7 @@ function showPrompt(
}
const container = document.createElement('div');
container.id = 'idfoto-capture-prompt';
container.id = 'relicario-capture-prompt';
// Common styles
const baseStyles = [
@@ -173,17 +173,17 @@ function showPrompt(
<span style="flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">
${actionLabel} login for <strong style="color:#58a6ff">${escapeForHtml(hostname)}</strong>${escapeForHtml(displayUser)}?
</span>
<button id="idfoto-save-btn" style="
<button id="relicario-save-btn" style="
background:#1f6feb; color:#fff; border:none; padding:5px 14px;
border-radius:3px; cursor:pointer; font-family:inherit; font-size:12px;
white-space:nowrap;
">${actionLabel}</button>
<button id="idfoto-never-btn" style="
<button id="relicario-never-btn" style="
background:transparent; color:#8b949e; border:1px solid #30363d;
padding:5px 10px; border-radius:3px; cursor:pointer;
font-family:inherit; font-size:12px; white-space:nowrap;
">Never</button>
<button id="idfoto-close-btn" style="
<button id="relicario-close-btn" style="
background:transparent; color:#8b949e; border:none;
cursor:pointer; font-size:16px; padding:2px 6px;
font-family:inherit; line-height:1;
@@ -212,7 +212,7 @@ function showPrompt(
};
// Save button
container.querySelector('#idfoto-save-btn')?.addEventListener('click', async () => {
container.querySelector('#relicario-save-btn')?.addEventListener('click', async () => {
clearAutoDismiss();
const now = new Date().toISOString();
@@ -246,22 +246,22 @@ function showPrompt(
// Show confirmation
const span = container.querySelector('span');
if (span) span.textContent = '\u2713 Saved';
const saveBtn = container.querySelector('#idfoto-save-btn') as HTMLElement | null;
const neverBtn = container.querySelector('#idfoto-never-btn') as HTMLElement | null;
const saveBtn = container.querySelector('#relicario-save-btn') as HTMLElement | null;
const neverBtn = container.querySelector('#relicario-never-btn') as HTMLElement | null;
if (saveBtn) saveBtn.style.display = 'none';
if (neverBtn) neverBtn.style.display = 'none';
setTimeout(() => removeExistingPrompt(), 1500);
});
// Never button
container.querySelector('#idfoto-never-btn')?.addEventListener('click', async () => {
container.querySelector('#relicario-never-btn')?.addEventListener('click', async () => {
clearAutoDismiss();
await sendMessage({ type: 'blacklist_site', hostname });
removeExistingPrompt();
});
// Close button
container.querySelector('#idfoto-close-btn')?.addEventListener('click', () => {
container.querySelector('#relicario-close-btn')?.addEventListener('click', () => {
clearAutoDismiss();
removeExistingPrompt();
});

View File

@@ -21,7 +21,7 @@ export function injectFieldIcons(
const icon = document.createElement('div');
icon.textContent = 'id';
icon.setAttribute('role', 'button');
icon.setAttribute('aria-label', 'idfoto autofill');
icon.setAttribute('aria-label', 'relicario autofill');
Object.assign(icon.style, {
position: 'absolute',
@@ -98,10 +98,10 @@ function showPicker(
candidates: Array<[string, ManifestEntry]>,
): void {
// Remove any existing picker.
document.querySelectorAll('.idfoto-picker').forEach(el => el.remove());
document.querySelectorAll('.relicario-picker').forEach(el => el.remove());
const picker = document.createElement('div');
picker.className = 'idfoto-picker';
picker.className = 'relicario-picker';
Object.assign(picker.style, {
position: 'absolute',
right: '0',

View File

@@ -1,7 +1,7 @@
/// Settings view — capture toggle, prompt style, and blacklist management.
import { sendMessage, navigate, escapeHtml } from '../popup';
import type { IdfotoSettings } from '../../shared/types';
import type { RelicarioSettings } from '../../shared/types';
export async function renderSettings(app: HTMLElement): Promise<void> {
app.innerHTML = '<div class="pad" style="text-align:center; padding-top:20px;"><span class="spinner"></span></div>';
@@ -12,8 +12,8 @@ export async function renderSettings(app: HTMLElement): Promise<void> {
sendMessage({ type: 'get_blacklist' }),
]);
const settings: IdfotoSettings = settingsResp.ok
? (settingsResp.data as { settings: IdfotoSettings }).settings
const settings: RelicarioSettings = settingsResp.ok
? (settingsResp.data as { settings: RelicarioSettings }).settings
: { captureEnabled: false, captureStyle: 'bar' };
const blacklist: string[] = blacklistResp.ok
@@ -24,7 +24,7 @@ export async function renderSettings(app: HTMLElement): Promise<void> {
? blacklist.map((h) => `
<div style="display:flex; align-items:center; justify-content:space-between; padding:4px 0; border-bottom:1px solid #21262d;">
<span style="font-size:12px; overflow:hidden; text-overflow:ellipsis;">${escapeHtml(h)}</span>
<button class="idfoto-remove-bl" data-hostname="${escapeHtml(h)}" style="
<button class="relicario-remove-bl" data-hostname="${escapeHtml(h)}" style="
background:transparent; color:#f85149; border:none; cursor:pointer;
font-size:11px; padding:2px 6px;
">remove</button>
@@ -86,7 +86,7 @@ export async function renderSettings(app: HTMLElement): Promise<void> {
});
// Blacklist remove buttons
document.querySelectorAll('.idfoto-remove-bl').forEach((btn) => {
document.querySelectorAll('.relicario-remove-bl').forEach((btn) => {
btn.addEventListener('click', async () => {
const hostname = (btn as HTMLElement).dataset.hostname;
if (hostname) {

View File

@@ -9,8 +9,8 @@ import { escapeHtml } from '../popup';
export function renderSetupWizard(app: HTMLElement): void {
app.innerHTML = `
<div class="pad" style="padding-top:24px;text-align:center;">
<img class="brand-logo" src="icons/idfoto-logo.svg" alt="">
<div class="brand" style="font-size:16px;margin-bottom:4px;">idfoto</div>
<img class="brand-logo" src="icons/relicario-logo.svg" alt="">
<div class="brand" style="font-size:16px;margin-bottom:4px;">relicario</div>
<p class="secondary" style="margin-bottom:20px;">two-factor vault</p>
<p class="muted" style="margin-bottom:16px;font-size:11px;line-height:1.6;">

View File

@@ -8,8 +8,8 @@ export function renderUnlock(app: HTMLElement): void {
app.innerHTML = `
<div class="pad" style="text-align:center; padding-top:40px;">
<img class="brand-logo" src="icons/idfoto-logo.svg" alt="">
<div class="brand">idfoto</div>
<img class="brand-logo" src="icons/relicario-logo.svg" alt="">
<div class="brand">relicario</div>
<p class="muted" style="margin:8px 0 24px;">two-factor vault</p>
<div class="form-group">
<input

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=360">
<link rel="stylesheet" href="styles.css">
<title>idfoto</title>
<title>relicario</title>
</head>
<body>
<div id="app"></div>

View File

@@ -1,4 +1,4 @@
/* idfoto extension — terminal dark theme */
/* relicario extension — terminal dark theme */
* {
margin: 0;

View File

@@ -1,4 +1,4 @@
/// Background script entry point for the idfoto browser extension.
/// Background script entry point for the relicario browser extension.
///
/// In Chrome this runs as a service worker (MV3). In Firefox this runs
/// as a persistent background script. WASM loading adapts automatically.
@@ -7,7 +7,7 @@
/// and routes all messages from the popup and content scripts.
import type { Request, Response } from '../shared/messages';
import type { Manifest, VaultConfig, SetupState, IdfotoSettings } from '../shared/types';
import type { Manifest, VaultConfig, SetupState, RelicarioSettings } from '../shared/types';
import { DEFAULT_SETTINGS } from '../shared/types';
import type { GitHost } from './git-host';
import { createGitHost } from './git-host';
@@ -30,9 +30,9 @@ const totpSecretCache: Map<string, string> = new Map();
// We detect the environment at runtime and use the appropriate loading strategy.
// @ts-ignore TS2307 — resolved by webpack alias / copy
import initDefault, { initSync } from '../../wasm/idfoto_wasm.js';
import initDefault, { initSync } from '../../wasm/relicario_wasm.js';
// @ts-ignore TS2307
import * as wasmBindings from '../../wasm/idfoto_wasm.js';
import * as wasmBindings from '../../wasm/relicario_wasm.js';
type WasmModule = typeof wasmBindings;
let wasm: WasmModule | null = null;
@@ -47,12 +47,12 @@ async function initWasm(): Promise<WasmModule> {
if (isServiceWorker) {
// Chrome: fetch WASM binary and instantiate synchronously
const wasmResponse = await fetch(chrome.runtime.getURL('idfoto_wasm_bg.wasm'));
const wasmResponse = await fetch(chrome.runtime.getURL('relicario_wasm_bg.wasm'));
const wasmBytes = await wasmResponse.arrayBuffer();
initSync({ module: new WebAssembly.Module(wasmBytes) });
} else {
// Firefox: background script — async init works
const wasmUrl = chrome.runtime.getURL('idfoto_wasm_bg.wasm');
const wasmUrl = chrome.runtime.getURL('relicario_wasm_bg.wasm');
await initDefault(wasmUrl);
}
@@ -86,13 +86,13 @@ async function loadSetupState(): Promise<SetupState> {
// --- Settings & blacklist helpers ---
async function loadSettings(): Promise<IdfotoSettings> {
const result = await chrome.storage.local.get('idfotoSettings');
return (result.idfotoSettings as IdfotoSettings) ?? { ...DEFAULT_SETTINGS };
async function loadSettings(): Promise<RelicarioSettings> {
const result = await chrome.storage.local.get('relicarioSettings');
return (result.relicarioSettings as RelicarioSettings) ?? { ...DEFAULT_SETTINGS };
}
async function saveSettings(settings: IdfotoSettings): Promise<void> {
await chrome.storage.local.set({ idfotoSettings: settings });
async function saveSettings(settings: RelicarioSettings): Promise<void> {
await chrome.storage.local.set({ relicarioSettings: settings });
}
async function loadBlacklist(): Promise<string[]> {

View File

@@ -30,8 +30,8 @@ export interface VaultMeta {
/// Read the vault salt and KDF params from the git repo.
export async function fetchVaultMeta(git: GitHost): Promise<VaultMeta> {
const saltBytes = await git.readFile('.idfoto/salt');
const paramsRaw = await git.readFile('.idfoto/params.json');
const saltBytes = await git.readFile('.relicario/salt');
const paramsRaw = await git.readFile('.relicario/params.json');
const paramsJson = new TextDecoder().decode(paramsRaw);
return { salt: saltBytes, paramsJson };
}

View File

@@ -1,4 +1,4 @@
/// Vault initialization wizard — 4-step flow for creating new idfoto vaults.
/// Vault initialization wizard — 4-step flow for creating new relicario vaults.
///
/// Step 1: Choose host type (Gitea / GitHub)
/// Step 2: Configure connection (URL, repo, token) + test
@@ -11,16 +11,16 @@ import type { VaultConfig } from '../shared/types';
// --- WASM module (loaded dynamically) ---
type WasmModule = typeof import('idfoto-wasm');
type WasmModule = typeof import('relicario-wasm');
let wasm: WasmModule | null = null;
async function loadWasm(): Promise<WasmModule> {
if (wasm) return wasm;
const mod = await import(
// @ts-ignore TS2307 — resolved at runtime, not by TS/webpack
/* webpackIgnore: true */ '../idfoto_wasm.js'
/* webpackIgnore: true */ '../relicario_wasm.js'
) as WasmModule & { default: (input?: string | URL) => Promise<void> };
await mod.default('../idfoto_wasm_bg.wasm');
await mod.default('../relicario_wasm_bg.wasm');
wasm = mod;
return mod;
}
@@ -109,8 +109,8 @@ function render(): void {
app.innerHTML = `
<div class="pad" style="padding-top:12px;">
<img class="brand-logo" src="icons/idfoto-logo.svg" alt="" style="margin-bottom:12px;">
<div class="brand" style="margin-bottom:4px;">idfoto vault setup</div>
<img class="brand-logo" src="icons/relicario-logo.svg" alt="" style="margin-bottom:12px;">
<div class="brand" style="margin-bottom:4px;">relicario vault setup</div>
${progressHtml}
${state.error ? `<div class="error">${escapeHtml(state.error)}</div>` : ''}
${stepHtml}
@@ -412,14 +412,14 @@ function attachStep3(): void {
const host = createGitHost(state.hostType, hostUrl, state.repoPath, state.apiToken);
await host.writeFile(
'.idfoto/salt',
'.relicario/salt',
salt,
'init: vault salt',
);
const paramsBytes = new TextEncoder().encode(paramsJson);
await host.writeFile(
'.idfoto/params.json',
'.relicario/params.json',
paramsBytes,
'init: KDF parameters',
);
@@ -427,7 +427,7 @@ function attachStep3(): void {
const devicesJson = '{"devices":[]}';
const devicesBytes = new TextEncoder().encode(devicesJson);
await host.writeFile(
'.idfoto/devices.json',
'.relicario/devices.json',
devicesBytes,
'init: device registry',
);

View File

@@ -23,7 +23,7 @@ export type Request =
| { type: 'check_credential'; url: string; username: string; password: string }
| { type: 'blacklist_site'; hostname: string }
| { type: 'get_settings' }
| { type: 'update_settings'; settings: Partial<import('./types').IdfotoSettings> }
| { type: 'update_settings'; settings: Partial<import('./types').RelicarioSettings> }
| { type: 'get_blacklist' }
| { type: 'remove_blacklist'; hostname: string };

View File

@@ -1,4 +1,4 @@
/// Full credential entry (matches Rust Entry struct in idfoto-core).
/// Full credential entry (matches Rust Entry struct in relicario-core).
export interface Entry {
name: string;
url?: string;
@@ -42,12 +42,12 @@ export interface SetupState {
}
/// User-configurable credential capture settings.
export interface IdfotoSettings {
export interface RelicarioSettings {
captureEnabled: boolean;
captureStyle: 'bar' | 'toast';
}
export const DEFAULT_SETTINGS: IdfotoSettings = {
export const DEFAULT_SETTINGS: RelicarioSettings = {
captureEnabled: false,
captureStyle: 'bar',
};

View File

@@ -1,9 +1,9 @@
/// Type declarations for the idfoto WASM module produced by wasm-pack.
/// Type declarations for the relicario WASM module produced by wasm-pack.
// Ambient module declarations for the WASM glue code.
// The module specifier must exactly match what's used in import statements.
declare module 'idfoto-wasm' {
declare module 'relicario-wasm' {
export default function init(input?: string | URL): Promise<void>;
export function derive_master_key(
passphrase: string,