eleven / ocho Public

web: searchui.js — resolve the four identifiers the extraction left dangling (#945)

chatAppFor and chatAppLoadOlder join openThread in one wireSearch()
registration; storeBackfillMark/storeRunBackfill import from store.js
directly; relTime moves to format.js (pure, per convention) and both
callers import it. Found via a full called-identifier audit after
Paul hit the chatAppFor ReferenceError — suspect-list greps miss what
an audit enumerates. The boot smoke now drives search end-to-end
(query the just-sent message, require a result, click it), so this
class can't ship silently again.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell a6459880322ddb8ed7e276dfd3e7fd972df4adad parent 14daa27
3 files changed, +27 −22
  • web/static/format.js +16 −0
  • web/static/searchui.js +8 −3
  • web/static/shell.js +3 −19
diff --git a/web/static/format.js b/web/static/format.js
index 50f5267..9abd554 100644
--- a/web/static/format.js
+++ b/web/static/format.js
@@ -357,3 +357,19 @@ export function changelogParagraphs(text) {
.map((p) => p.replace(/[ \t]*\n[ \t]*/g, " ").trim())
.filter(Boolean);
}
+
+// A compact "last activity" label: time today, "Yesterday", a weekday this week,
+// else a short date.
+export function relTime(sec) {
+ if (!sec) return "";
+ const d = new Date(sec * 1000);
+ const now = new Date();
+ if (d.toDateString() === now.toDateString()) {
+ return d.toLocaleTimeString([], { hour: "numeric", minute: "2-digit" });
+ }
+ const y = new Date(now);
+ y.setDate(now.getDate() - 1);
+ if (d.toDateString() === y.toDateString()) return "Yesterday";
+ if ((now - d) / 86400000 < 7) return d.toLocaleDateString([], { weekday: "short" });
+ return d.toLocaleDateString([], { day: "2-digit", month: "2-digit" });
+}
diff --git a/web/static/searchui.js b/web/static/searchui.js
index 7041156..4e4a14e 100644
--- a/web/static/searchui.js
+++ b/web/static/searchui.js
@@ -4,14 +4,19 @@
// state via shellstate.js; navigation to a result's thread is the injected
// openThread hook (the one shell behaviour this needs).
import { SearchIndex, snippetFor } from "/static/search.js";
-import { allMessages as storeAllMessages } from "/static/store.js";
+import { allMessages as storeAllMessages, getBackfillMark as storeBackfillMark, runBackfill as storeRunBackfill } from "/static/store.js";
import { decrypt } from "/static/crypto.js";
import { scrollToMessage } from "/static/apps/chat.js";
import { session, threads, threadKeys, myUserId, threadName } from "/static/shellstate.js";
+import { relTime } from "/static/format.js";
+// The three shell behaviours search rides: navigation into a result's thread,
+// the chat-thread predicate, and scroll-back paging for the deep walk.
let openThreadFn = () => {};
-export function onSearchOpen(fn) {
- openThreadFn = fn;
+let chatAppFor = () => null;
+let chatAppLoadOlder = async () => {};
+export function wireSearch(deps) {
+ ({ openThread: openThreadFn, chatAppFor, chatAppLoadOlder } = deps);
}
// ---- message search (#586) -------------------------------------------------
diff --git a/web/static/shell.js b/web/static/shell.js
index 1c219a0..bcf2562 100644
--- a/web/static/shell.js
+++ b/web/static/shell.js
@@ -47,7 +47,7 @@ import {
translateMentions,
capAllows,
} from "/static/ui.js";
-import { richTokens, blockTokens, firstLink, replySnippet, formatBytes, autoGroupTitle, nameList, membershipLine, emojiOnlyCount, youtubeId } from "/static/format.js";
+import { richTokens, blockTokens, firstLink, replySnippet, formatBytes, autoGroupTitle, nameList, membershipLine, emojiOnlyCount, youtubeId, relTime } from "/static/format.js";
import { emojify, emojiMatches } from "/static/emoji.js";
import { startBootScene, finishBootScene } from "/static/bootscene.js";
import { processImage, videoMeta } from "/static/media.js";
@@ -133,7 +133,7 @@ import "/static/apps/admin.js"; // the admin panel (admin-only; opened via /admi
import "/static/apps/appstudio.js"; // make + manage your own member apps (docs/member-apps.md)
import { openMemoryPanel } from "/static/apps/memory.js"; // /memory — the AI-memory scratchpad (docs/eleven-memory.md)
import { setHost as setChatAppHost, setConnected as setChatAppConnected, appendNotice, setNoticeTyping, feedGlideToBottom, scrollToMessage } from "/static/apps/chat.js";
-import { scheduleMessageSearch, onSearchOpen, searchIdx } from "/static/searchui.js"; // message search over the local store (#586)
+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 } from "/static/shellstate.js"; // the shared shell state (docs/shell-modules.md)
@@ -177,7 +177,7 @@ import {
import { anyComposerHasText, formatAsYouTypePref, setFormatAsYouType, classicComposerPref, setClassicComposer } from "/static/composer.js";
onThemeApplied(pushTheme); // live-retint mounted sandbox frames on theme change
-onSearchOpen(openThread); // a tapped search result navigates to its thread
+wireSearch({ openThread, chatAppFor, chatAppLoadOlder }); // navigation + thread predicate + scroll-back paging
// Ephemeral transport (ctx.emitEphemeral): same encryption as emit, but the
// envelope's `ephemeral` flag asks the server to relay-and-forget — no store,
@@ -3129,22 +3129,6 @@ function renderThreadList() {
}
}
-// A compact "last activity" label: time today, "Yesterday", a weekday this week,
-// else a short date.
-function relTime(sec) {
- if (!sec) return "";
- const d = new Date(sec * 1000);
- const now = new Date();
- if (d.toDateString() === now.toDateString()) {
- return d.toLocaleTimeString([], { hour: "numeric", minute: "2-digit" });
- }
- const y = new Date(now);
- y.setDate(now.getDate() - 1);
- if (d.toDateString() === y.toDateString()) return "Yesterday";
- if ((now - d) / 86400000 < 7) return d.toLocaleDateString([], { weekday: "short" });
- return d.toLocaleDateString([], { day: "2-digit", month: "2-digit" });
-}
-
// ---- time separators + sender-name collapse (feed grouping) -------------
function formatTime(ms) {