eleven / messenger Public

web: newchat.js — restore the three bare references the extraction dropped (#973)

isAdmin() (now via verbs — the audit was fooled by a same-named local
const in another function's scope), the ENABLE_BOTS/ENABLE_APPS gates
(joined shellstate with gateMeta + ENABLE_AI), and ICON_WRENCH (icons.js
import). Paul hit the empty new-chat sheet on the canary; the boot smoke
now opens the picker and fails on an empty sheet.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell 5cf22b18583497e11efe773f661136a9b147b457 parent 310921d
3 files changed, +10 −8
  • web/static/newchat.js +3 −2
  • web/static/shell.js +2 −6
  • web/static/shellstate.js +5 −0
diff --git a/web/static/newchat.js b/web/static/newchat.js
index c834857..5f6ae9f 100644
--- a/web/static/newchat.js
+++ b/web/static/newchat.js
@@ -2,7 +2,8 @@
// Discover, and the apps catalogue, plus thread creation itself. Extracted
// verbatim from shell.js; behaviours ride the verbs seam, state shellstate.
import { verbs } from "/static/shellverbs.js";
-import { session, roster, myUserId, threads, threadKeys, activeId, nicknames, threadName, threadKeyB64, hidden, dmClosed, embed, setActiveId } from "/static/shellstate.js";
+import { ICON_WRENCH } from "/static/icons.js";
+import { session, roster, myUserId, threads, threadKeys, activeId, nicknames, threadName, threadKeyB64, hidden, dmClosed, embed, setActiveId, ENABLE_BOTS, ENABLE_APPS } from "/static/shellstate.js";
import { avatarFor, avatarEl, paintAvatar } from "/static/avatars.js";
import { decrypt, encrypt, generateKey, importKey, wrapThreadKey } from "/static/crypto.js";
import { appGlyphInto, personalApps } from "/static/apps.js";
@@ -102,7 +103,7 @@ export function showNewChat(startMode) {
// admins get its row in the Apps section all the same — the discoverable way
// in, beyond the /admin command. Null for everyone else. Deliberately exempt
// from the apps gate: Admin is how an admin switches apps back on.
- const adminApp = isAdmin() ? { name: "admin", title: "Admin", glyph: "🛠️", logo: ICON_WRENCH } : null;
+ const adminApp = verbs.isAdmin() ? { name: "admin", title: "Admin", glyph: "🛠️", logo: ICON_WRENCH } : null;
const adminMatches = (q) => !!adminApp && adminApp.title.toLowerCase().includes(q);
const addAppRow = (listEl, app) => {
const b = document.createElement("button");
diff --git a/web/static/shell.js b/web/static/shell.js
index 6956fcd..9dd13df 100644
--- a/web/static/shell.js
+++ b/web/static/shell.js
@@ -144,7 +144,7 @@ import { verbs } from "/static/shellverbs.js"; // the behaviour seam (docs/shell
import { scheduleMessageSearch, wireSearch, searchIdx } from "/static/searchui.js"; // message search over the local store (#586)
import { loadMemberApps, setMemberSeeder, setSurfaceRefresh, setMemberAdmin, recentLocalDeploy, pushTheme } from "/static/sandbox.js"; // member-built apps, run sandboxed (docs/member-apps.md)
import { THEME_META, THEME_ORDER, currentTheme, currentAppearance, setTheme, setAppearance, currentTextSize, setTextSize, onThemeApplied } from "/static/theme.js"; // applies the saved theme/text-size on import
-import { session, roster, myUserId, threads, threadKeys, activeId, ws, setSession, setRoster, setMyUserId, setThreads, setActiveId, setWs, nicknames, setNicknames, threadName, instanceKey, instanceKeyB64, setInstanceKey, threadKeyB64, unread, hidden, dmClosed, embed } from "/static/shellstate.js"; // the shared shell state (docs/shell-modules.md)
+import { session, roster, myUserId, threads, threadKeys, activeId, ws, setSession, setRoster, setMyUserId, setThreads, setActiveId, setWs, nicknames, setNicknames, threadName, instanceKey, instanceKeyB64, setInstanceKey, threadKeyB64, unread, hidden, dmClosed, embed, ENABLE_AI, ENABLE_BOTS, ENABLE_APPS } from "/static/shellstate.js"; // the shared shell state (docs/shell-modules.md)
import { openLightbox } from "/static/lightbox.js";
import { mutedPeople, blockedPeople, loadModPrefs, setPersonMuted, openReportSheet, loadBlocks, setPersonBlocked, onModerationChange } from "/static/moderation.js";
@@ -194,7 +194,7 @@ Object.assign(verbs, {
resolveRename, showList, paintHeaderAvatar,
loadTiers, setNickname, syncThreadKeysToSW,
inviteSomeone, resolveAppThread, billingDate, levelPrice, inviteAllowed,
- decryptRosterLabels, openThreadById, seedAppState, welcomePending,
+ decryptRosterLabels, openThreadById, seedAppState, welcomePending, isAdmin,
});
onThemeApplied(pushTheme); // live-retint mounted sandbox frames on theme change
@@ -2238,10 +2238,6 @@ const USER_INVITES = ((document.querySelector('meta[name="user-invites"]') || {}
// they only hide entry points; the server enforces each gate. ENABLE_AI has no
// web entry point to hide (the sidecar enforces it), but rides along so copy
// can consult it.
-const gateMeta = (name) => ((document.querySelector(`meta[name="${name}"]`) || {}).content || "") !== "0";
-const ENABLE_AI = gateMeta("enable-ai");
-const ENABLE_BOTS = gateMeta("enable-bots");
-const ENABLE_APPS = gateMeta("enable-apps");
// Whether THIS member may invite: the toggle governs ordinary members, but an
// admin always can — otherwise the person who just set the space up couldn't
diff --git a/web/static/shellstate.js b/web/static/shellstate.js
index bb6e8f5..724abb3 100644
--- a/web/static/shellstate.js
+++ b/web/static/shellstate.js
@@ -7,6 +7,11 @@
// here; helpers keep moving into their own modules.
export const embed = window.__lchatEmbed || null; // native-webview embed config, or null in a browser
+// Feature gates the server stamps into the page as meta tags ("0" = off).
+const gateMeta = (name) => ((document.querySelector(`meta[name="${name}"]`) || {}).content || "") !== "0";
+export const ENABLE_AI = gateMeta("enable-ai");
+export const ENABLE_BOTS = gateMeta("enable-bots");
+export const ENABLE_APPS = gateMeta("enable-apps");
export let session = null; // { token, name } — set at boot (embed or localStorage)
export let roster = []; // [{user_id, label, public_key}]
export let myUserId = "";