mirror of
https://github.com/OpenSelena/omniget.git
synced 2026-11-04 08:10:56 +00:00
Adds OmniDisc, a self-hosted chat/voice/screen-share surface inside OmniGet, behind the `omnidisc.enabled` setting (off by default). The server it talks to lives in its own repo; the wire contract is the new `omnidisc-proto` crate. Client crates: - `omnidisc-proto`: snowflakes with 10-day buckets, 64-bit permission bitfield and its resolution order, gateway opcodes and dispatch events, streaming policy, REST bodies. Shared verbatim with the server. - `omnidisc-media`: the whole media path in Rust, because WKWebView has no getDisplayMedia and a webview cannot own the audio graph without stuttering. cpal capture/playback with a lock-free mic path, RNNoise, VAD, a mixer with per-participant gain and ducking, LiveKit as the SFU, ScreenCaptureKit capture feeding our own VideoToolbox session published pre-encoded, and a wgpu viewer in a pop-out window. Windows and Linux compile to typed "unsupported yet" stubs. - `omnidisc-mls`: MLS (RFC 9420) via openmls for direct messages, per-file XChaCha20-Poly1305 in 1 MiB chunks, and the voice room key derived from the group epoch — never minted by the server. App: 89 Tauri commands across auth, gateway, chat, voice, streaming, uploads, devices and MLS; session tokens in the OS keyring and never exposed to the webview; the `/omnidisc` route with a virtualized message list, composer, friends, profiles, guild settings, search, pins and per-device fingerprints. Also carries the settings and shell work that was already in progress in the tree: the grouped settings sidebar, the platform-aware chrome, and the i18n keys those introduced.
43 lines
1.7 KiB
JavaScript
43 lines
1.7 KiB
JavaScript
#!/usr/bin/env node
|
|
import { readFileSync, writeFileSync, readdirSync } from "node:fs";
|
|
import { join, dirname } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const dir = join(dirname(fileURLToPath(import.meta.url)), "..", "src", "lib", "i18n");
|
|
const checkOnly = process.argv.includes("--check");
|
|
const source = JSON.parse(readFileSync(join(dir, "en.json"), "utf8"));
|
|
|
|
function sync(src, dst, path, stats) {
|
|
const out = {};
|
|
for (const key of Object.keys(src)) {
|
|
const full = path ? `${path}.${key}` : key;
|
|
const s = src[key];
|
|
const d = dst?.[key];
|
|
if (s && typeof s === "object" && !Array.isArray(s)) {
|
|
out[key] = sync(s, d && typeof d === "object" ? d : {}, full, stats);
|
|
} else if (d === undefined) {
|
|
out[key] = s;
|
|
stats.added.push(full);
|
|
} else {
|
|
out[key] = d;
|
|
}
|
|
}
|
|
for (const key of Object.keys(dst ?? {})) {
|
|
if (!(key in src)) stats.removed.push(path ? `${path}.${key}` : key);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
let dirty = false;
|
|
for (const file of readdirSync(dir).filter((f) => f.endsWith(".json") && f !== "en.json").sort()) {
|
|
const target = JSON.parse(readFileSync(join(dir, file), "utf8"));
|
|
const stats = { added: [], removed: [] };
|
|
const merged = sync(source, target, "", stats);
|
|
const changed = stats.added.length > 0 || stats.removed.length > 0;
|
|
if (changed) dirty = true;
|
|
console.log(`${file}: +${stats.added.length} missing (filled with English) -${stats.removed.length} stale`);
|
|
if (stats.removed.length) console.log(` stale: ${stats.removed.slice(0, 10).join(", ")}${stats.removed.length > 10 ? ", …" : ""}`);
|
|
if (!checkOnly && changed) writeFileSync(join(dir, file), JSON.stringify(merged, null, 2) + "\n");
|
|
}
|
|
if (checkOnly && dirty) process.exit(1);
|