eleven / ocho Public

Clone
git clone https://amadan.net/eleven/ocho

Plain git — no account needed to clone.

Download

Download this file

1// The shell's shared mutable state — the seven bindings every part of the chat
2// page reads (docs/shell-modules.md § The shellstate seam). This module OWNS
3// them; everyone else imports them under these same names. ES-module live
4// bindings make reads and property mutations (threadKeys[id] = k) work
5// unchanged — only reassigning a WHOLE binding needs its setter, which is all
6// the setters are: assignment, no logic. Shared *behaviour* does not belong
7// here; helpers keep moving into their own modules.
8
9export const embed = window.__lchatEmbed || null; // native-webview embed config, or null in a browser
10// Feature gates the server stamps into the page as meta tags ("0" = off).
11const gateMeta = (name) => ((document.querySelector(`meta[name="${name}"]`) || {}).content || "") !== "0";
12export const ENABLE_AI = gateMeta("enable-ai");
13export const ENABLE_BOTS = gateMeta("enable-bots");
14export const ENABLE_APPS = gateMeta("enable-apps");
15export const ENABLE_MEMBER_APPS = gateMeta("enable-member-apps");
16// The places-UI experiment gate — OFF by default, and deliberately NOT a
17// server gate: it hides the multi-place app surfaces (the focus-and-explain
18// Apps sheet, the + picker's places doorway, the space menu's directory)
19// while the machinery underneath stays live for experiments. Flip it
20// per-device from the console: localStorage.lchat_places_ui = "1" (remove or
21// set anything else to turn back off, then reload). With it off, tapping an
22// app in the + picker just opens/creates its personal copy, and a chat's
23// ⋮ → Apps only adds shared copies to THAT chat. See docs/space-apps.md
24// § The pullback.
25export const PLACES_UI = (() => {
26 try {
27 return localStorage.getItem("lchat_places_ui") === "1";
28 } catch {
29 return false;
30 }
31})();
32export let session = null; // { token, name } — set at boot (embed or localStorage)
33export let roster = []; // [{user_id, label, public_key}]
34export let myUserId = "";
35export let threads = []; // server Thread objects (id, kind, name_blob, members, wrapped)
36export let threadKeys = {}; // thread id -> CryptoKey
37export let activeId = null; // the open thread's id, or null
38export let ws = null; // the live WebSocket, or null between connects
39export let nicknames = {}; // uid -> the name I call them (per-viewer names, #577)
40export let threadName = {}; // thread id -> decrypted display name
41export let instanceKey = null; // the all-members AES key (profile photos), or null until granted
42export let instanceKeyB64 = null;
43export let threadKeyB64 = {}; // thread id -> raw key (base64url), to wrap for new members
44export let unread = {}; // thread id -> bool
45export let hidden = {}; // thread id -> bool: closed (hidden from the list), synced from the server
46export let dmClosed = {}; // thread id -> bool: DM ended by either party (server-enforced)
47export let pinned = {}; // thread id -> bool: pinned to the top of the list, synced from the server
48export let muted = {}; // thread id -> bool: muted (no notifications), synced from the server
49export let sealedBranding = null; // decrypted {name, description, admin_name, admin_email, avatar_alt, avatar_mime}
50export let brandingB = null; // the short branding key (lowercase base32)
51export let threadApp = {}; // thread id -> mini-app name, for a kind=="self" app-thread
52export let tiers = []; // /api/tiers rows, personalised to this user
53export let tierKeys = {}; // tier id -> CryptoKey
54export let tierKeyB64 = {}; // tier id -> raw base64url (kept to wrap for others)
55export let pendingJoinThread = null; // the DM a fresh invite created — the welcome conversation offers it at the end
56export const ACTIVE_KEY = "lchat_active"; // last-open thread id, so a refresh lands back in it
57export const MAX_UPLOAD = 25 * 1024 * 1024; // matches the default upload cap (main.go)
58
59export function setSession(v) { session = v; }
60export function setRoster(v) { roster = v; }
61export function setMyUserId(v) { myUserId = v; }
62export function setThreads(v) { threads = v; }
63export function setThreadKeys(v) { threadKeys = v; }
64export function setActiveId(v) { activeId = v; }
65export function setWs(v) { ws = v; }
66export function setTiers(v) { tiers = v; }
67export function setPendingJoinThread(v) { pendingJoinThread = v; }
68export function setSealedBranding(v) { sealedBranding = v; }
69export function setBrandingB(v) { brandingB = v; }
70export function setNicknames(v) { nicknames = v; }
71export function setThreadName(v) { threadName = v; }
72export function setInstanceKey(key, b64) { instanceKey = key; instanceKeyB64 = b64; }
73