From 4d4269a8a763d20946aff5338f51c8a8d8d57fb8 Mon Sep 17 00:00:00 2001
From: sarah <129242944+sarah-inkeep@users.noreply.github.com>
Date: Tue, 30 Jun 2026 12:17:34 -0700
Subject: [PATCH] feat(open-knowledge): in-app product-updates subscription
(PRD-7196) (#2273)
* feat(open-knowledge): in-app product-updates subscription (PRD-7196)
Regroup the Resources menu into Resources, Community, and Product updates.
Add a Subscribe action that opens a reusable email form (shadcn +
react-hook-form) posting to the docs /api/subscribe endpoint, plus a live
GitHub star count on the GitHub row. Add CORS to the subscribe route so the
desktop and web app can call it cross-origin.
* refactor(open-knowledge): share getGitHubStars from core
Hoist the docs site-nav star fetcher into @inkeep/open-knowledge-core so the
Resources menu reuses it instead of duplicating the GitHub API call. An
optional RequestInit lets the docs site keep its hourly server-side revalidate
while the editor app passes an abort signal.
* fix(open-knowledge): address review feedback on subscribe + star fetch
- subscribe: add a 15s request timeout so a hung connection can't leave the
Submit button spinning forever; log HTTP error responses client-side.
- getGitHubStars: compose a caller-supplied signal with the 5s timeout via
AbortSignal.any instead of letting it overwrite the timeout.
- HelpPopover: reset the nested Subscribe popover when the menu closes.
- SubscribeForm: make the live region's role="status" unconditional so the
success announcement isn't missed (WCAG 4.1.3).
* fix(open-knowledge): a11y + test coverage from subscribe re-review
- SubscribeForm: give the Submit button an accessible name while submitting
(sr-only "Subscribing..." alongside the aria-hidden spinner).
- SubscribeForm: move focus to the confirmation heading after success so
keyboard/SR focus isn't orphaned when the form is replaced.
- Test the reason: 'invalid' and 'unavailable' submit branches, which have
distinct UI from the generic 'error' branch.
* feat(open-knowledge): allow dev override of the subscribe endpoint
Read VITE_SUBSCRIBE_ENDPOINT (defaulting to the hosted docs endpoint) so the
in-app Subscribe form can be pointed at a local docs server to exercise the
cross-origin POST + CORS path without hitting production.
* revert(open-knowledge): drop VITE_SUBSCRIBE_ENDPOINT override; subscribe form tweaks
The env override didn't take effect in the desktop/turbo path and isn't worth
the complexity, so the form posts to the hosted endpoint unconditionally again.
Also folds in success-view copy tweaks (checkmark + "You're subscribed!") and
fixes invalid
+ {/* role="status" is unconditional so the live region is registered
+ before the success text replaces the description — adding the role
+ and the new content in the same render makes screen readers miss
+ the announcement (WCAG 4.1.3). */}
+
+ {subscribed ? (
+ Thanks for subscribing. Watch your inbox for product updates.
+ ) : (
+ (description ?? Get product updates in your inbox.)
+ )}
+
+
+ {onDismiss ? (
+
+ ) : null}
+
+
+ {subscribed ? null : (
+
+ )}
+
+ );
+}
diff --git a/packages/app/src/lib/subscribe.ts b/packages/app/src/lib/subscribe.ts
new file mode 100644
index 000000000..f11731aec
--- /dev/null
+++ b/packages/app/src/lib/subscribe.ts
@@ -0,0 +1,34 @@
+const SUBSCRIBE_ENDPOINT = 'https://openknowledge.ai/api/subscribe';
+
+export type SubscribeResult =
+ | { ok: true }
+ | { ok: false; reason: 'invalid' | 'unavailable' | 'error' };
+
+export async function submitSubscribe(email: string): Promise {
+ try {
+ const response = await fetch(SUBSCRIBE_ENDPOINT, {
+ method: 'POST',
+ headers: { 'content-type': 'application/json' },
+ body: JSON.stringify({ email }),
+ signal: AbortSignal.timeout(15_000),
+ });
+ if (response.ok) {
+ return { ok: true };
+ }
+ if (response.status === 400) {
+ return { ok: false, reason: 'invalid' };
+ }
+ if (response.status === 503) {
+ return { ok: false, reason: 'unavailable' };
+ }
+ console.warn(`[subscribe] action=submit result=http-error status=${response.status}`);
+ return { ok: false, reason: 'error' };
+ } catch (err) {
+ console.warn(
+ `[subscribe] action=submit result=network-error message=${
+ err instanceof Error ? err.message : String(err)
+ }`,
+ );
+ return { ok: false, reason: 'error' };
+ }
+}
diff --git a/packages/app/src/locales/en/messages.json b/packages/app/src/locales/en/messages.json
index 662902dec..2a6a6be2f 100644
--- a/packages/app/src/locales/en/messages.json
+++ b/packages/app/src/locales/en/messages.json
@@ -108,6 +108,7 @@
"2Znqb1": [" (missing)"],
"2bVHbB": ["Available in every project on this computer."],
"2fkdnR": ["Nothing to undo on this file"],
+ "2hYmUH": ["my@email.com"],
"2hpdRq": ["Hide panel (", ["panelShortcutLabel"], ")"],
"2n3oDU": [
"Project-level pages ordered by inbound link count, up to ",
@@ -146,6 +147,7 @@
"3a_Bd7": ["Open source doc: ", ["mirrorSrc"]],
"3aeN7X": ["All files"],
"3cOo2C": ["Create a folder from the current document or folder context."],
+ "3ePd3I": ["What's new"],
"3h5zMp": ["Something went wrong loading the settings panel."],
"3iyGWF": ["Open recent project"],
"3jod3l": ["Connect GitHub"],
@@ -188,6 +190,7 @@
"4g19V6": ["Failed to update property"],
"4hJhzz": ["Table"],
"4jDd0j": ["Anyone can see"],
+ "4mLBx9": ["Subscriptions aren't available right now."],
"4niEzY": ["Sandboxed HTML embed — write HTML, see the rendered preview live."],
"4nj6n8": ["/api/config returned HTTP ", ["code"]],
"4rjwLG": ["Create with ", ["0"], " CLI"],
@@ -410,6 +413,7 @@
"AQGtjK": [
"Commands run with the full access of your macOS user account on this machine. Turn this off to disable the shell."
],
+ "ATGYL1": ["Email address"],
"AVW-K9": ["Failed to load orphan pages"],
"AWOSPo": ["Zoom in"],
"AcK_r0": ["Claude CLI"],
@@ -529,6 +533,7 @@
"E7BhZn": ["Blank note"],
"E9bKSA": ["Creating"],
"EBVbtP": ["Could not duplicate item"],
+ "EDl9kS": ["Subscribe"],
"EDm3wJ": ["Loading diff renderer"],
"EEYbdt": ["Publish"],
"EJuO7B": ["Connect a GitHub account to browse and sync your repositories."],
@@ -650,6 +655,7 @@
"GubkSu": ["Use a starter pack"],
"GuqOb1": ["Failed to load headings"],
"Gx0VS1": ["e.g. drafts/ or *.draft.md"],
+ "GzPQ2f": ["Stay in the loop"],
"H-vDm3": ["Relaunching to install the update…"],
"H0WlWQ": ["Grid of rows and columns with a header row."],
"H86f9p": ["Collapse"],
@@ -915,6 +921,7 @@
"Okd2Xv": ["Toggle underline formatting."],
"OlAl5i": ["Expand all"],
"OmHTGj": ["Disable git auto-sync"],
+ "On0aF2": ["Website"],
"Oo_WWc": ["No pages found"],
"Oos8fo": ["Copy command"],
"OuY8t9": ["Open in current branch"],
@@ -932,7 +939,9 @@
"PB4upl": ["Find previous match"],
"PBwmDJ": ["Searching by meaning"],
"PBxg_E": ["Not now"],
+ "PCNIuJ": ["File an issue"],
"PCSkw2": ["Skills"],
+ "PIMM5l": ["You're subscribed!"],
"PKLY-1": ["Opening..."],
"PMyJ9C": ["No edit session yet"],
"PPFQ3o": ["Installed \"", ["0"], "\" into ", ["1"]],
@@ -1017,6 +1026,7 @@
"SFN6dN": ["Heading 3"],
"STTOnz": [["keyName"], " type: complex value (nested; read-only)"],
"SUOoXn": ["Failed to open project."],
+ "SVLPt5": [["formatted"], " GitHub stars"],
"SXCSQd": ["Copied path"],
"SYfHmN": ["Unpin tab"],
"Sc1bKE": [
@@ -1142,6 +1152,8 @@
"Wgmlsw": ["Fetching"],
"Wh0im4": ["editing [[", ["realCurrentDoc"], "]]"],
"WhOsNE": ["Strikethrough"],
+ "WmPbvm": ["Get product updates in your inbox."],
+ "Wq6Wcm": ["Please enter a valid email address."],
"WqLq6M": ["Open with AI ", ["displayName"]],
"Wt5W56": ["Sync is off — your edits will not sync to the remote repository."],
"Wtw22s": ["Installing"],
@@ -1312,6 +1324,7 @@
"aiArms": ["We couldn't clone this repository."],
"ajz9F8": ["Couldn't load conflict content for ", ["filePath"], ". Try reloading the page."],
"aoa6xA": ["network error (is `ok ui` running?)"],
+ "arUUsK": ["Product updates"],
"az8lvo": ["Off"],
"b3Thhd": ["Upload failed"],
"b4AQFK": ["Open activity panel for ", ["tooltipName"], ", editing ", ["realCurrentDoc"]],
@@ -1357,6 +1370,7 @@
"cZ_8zB": ["Stays on this computer."],
"cdm00A": ["No Incoming"],
"cdmk8A": ["Uncommitted changes"],
+ "chL5IG": ["Community"],
"cklVjM": ["Timeline"],
"cn1sLi": ["Couldn't connect OpenKnowledge tools to Claude Code. Please try again."],
"cnGeoo": ["Delete"],
@@ -1435,6 +1449,7 @@
"fNr-MX": ["Disable semantic search"],
"fVcm9t": [["0", "plural", { "one": ["#", " doc"], "other": ["#", " docs"] }]],
"fWi7pn": ["Initialize starter pack"],
+ "fWsBTs": ["Something went wrong. Please try again."],
"fYKgzG": ["Failed to load recent projects."],
"fYjgXu": ["Unordered list of items."],
"f_PkYv": [
@@ -1624,6 +1639,7 @@
"l6OXqI": ["No installed agents found"],
"l8G3S9": ["Auto-sync is off — you don't have permission to push to this repo"],
"lAiyU_": ["Only the current document uses <0>#", ["tag"], "0>."],
+ "lCC_D5": ["Subscribing..."],
"lGs_bQ": ["Page <0>*0>"],
"lHwYMr": ["Server error: ", ["status"], " ", ["statusText"]],
"lI0Td2": ["Use letters, digits, <0>_0> and <1>-1> only."],
@@ -1776,6 +1792,7 @@
"p0Iq25": ["Insert link"],
"p9sdCy": ["New ignore pattern"],
"pAgkuZ": ["Reopening \"", ["docName"], "\" with a fresh local collaboration cache."],
+ "pBROkv": ["Thanks for subscribing. Watch your inbox for product updates."],
"pEQFsW": ["No matching projects."],
"pFagOn": ["Source editing"],
"pHYP6v": ["Open <0>Customize0> in the sidebar <1/> <2>Skills2>."],
diff --git a/packages/app/src/locales/en/messages.po b/packages/app/src/locales/en/messages.po
index ab4dec821..8e6c95af7 100644
--- a/packages/app/src/locales/en/messages.po
+++ b/packages/app/src/locales/en/messages.po
@@ -270,6 +270,10 @@ msgstr "{failedCount, plural, one {1 file failed to upload} other {{failedCount}
msgid "{fileCount, plural, one {# file} other {# files}}"
msgstr "{fileCount, plural, one {# file} other {# files}}"
+#: src/components/HelpPopover.tsx
+msgid "{formatted} GitHub stars"
+msgstr "{formatted} GitHub stars"
+
#: src/components/ActivityModeContent.tsx
#: src/components/ActivityPanelFileRow.tsx
#: src/components/SyncStatusBadge.tsx
@@ -1143,6 +1147,7 @@ msgstr "Cloning..."
#: src/components/EditorTabs.tsx
#: src/components/InstallInClaudeDesktopDialog.tsx
#: src/components/SeedDialog.tsx
+#: src/components/SubscribeForm.tsx
#: src/components/ui/dialog.tsx
#: src/components/ui/sheet.tsx
#: src/editor/find-replace/FindReplaceBar.tsx
@@ -1272,6 +1277,10 @@ msgstr "Commit or stash changes to switch:"
msgid "Commits happen automatically"
msgstr "Commits happen automatically"
+#: src/components/HelpPopover.tsx
+msgid "Community"
+msgstr "Community"
+
#: src/components/empty-state/use-create-suggestions.ts
msgid "Competitor research"
msgstr "Competitor research"
@@ -2330,6 +2339,10 @@ msgstr "Edits at the source land here — no copy-paste drift."
msgid "Email"
msgstr "Email"
+#: src/components/SubscribeForm.tsx
+msgid "Email address"
+msgstr "Email address"
+
#: src/editor/slash-command/component-items.tsx
msgid "Embed a video with native player controls."
msgstr "Embed a video with native player controls."
@@ -2715,6 +2728,10 @@ msgstr "File"
msgid "File already exists"
msgstr "File already exists"
+#: src/components/HelpPopover.tsx
+msgid "File an issue"
+msgstr "File an issue"
+
#: src/components/FileTree.tsx
msgid "File drop zone"
msgstr "File drop zone"
@@ -2899,6 +2916,10 @@ msgstr "Get {displayName}"
msgid "Get Claude Code"
msgstr "Get Claude Code"
+#: src/components/SubscribeForm.tsx
+msgid "Get product updates in your inbox."
+msgstr "Get product updates in your inbox."
+
#: src/components/settings/SettingsDialogBody.tsx
msgid "Git auto-sync"
msgstr "Git auto-sync"
@@ -3748,6 +3769,10 @@ msgstr "moved template"
msgid "Multi-page PDF viewer with toolbar controls (thumbnails, page nav, zoom)."
msgstr "Multi-page PDF viewer with toolbar controls (thumbnails, page nav, zoom)."
+#: src/components/SubscribeForm.tsx
+msgid "my@email.com"
+msgstr "my@email.com"
+
#: src/components/SkillProperties.tsx
#: src/components/TemplateProperties.tsx
msgid "name"
@@ -4692,6 +4717,10 @@ msgstr "Pin tab"
msgid "Plain"
msgstr "Plain"
+#: src/components/SubscribeForm.tsx
+msgid "Please enter a valid email address."
+msgstr "Please enter a valid email address."
+
#: src/components/settings/SettingsDialogBody.tsx
#: src/components/settings/SettingsDialogShell.tsx
msgid "Preferences"
@@ -4729,6 +4758,10 @@ msgstr "Private repository"
msgid "probe failed"
msgstr "probe failed"
+#: src/components/HelpPopover.tsx
+msgid "Product updates"
+msgstr "Product updates"
+
#: src/components/settings/ProjectTemplatesSection.tsx
msgid "project"
msgstr "project"
@@ -5777,6 +5810,10 @@ msgstr "Something went wrong opening this file (HTTP {status})."
msgid "Something went wrong starting the terminal."
msgstr "Something went wrong starting the terminal."
+#: src/components/SubscribeForm.tsx
+msgid "Something went wrong. Please try again."
+msgstr "Something went wrong. Please try again."
+
#: src/lib/keyboard-shortcuts.ts
msgid "Source editing"
msgstr "Source editing"
@@ -5861,6 +5898,10 @@ msgstr "Starting sign-in flow"
msgid "status"
msgstr "status"
+#: src/components/SubscribeForm.tsx
+msgid "Stay in the loop"
+msgstr "Stay in the loop"
+
#: src/components/settings/SharingSection.tsx
#: src/components/SharingModeField.tsx
msgid "Stays on this computer."
@@ -5886,6 +5927,19 @@ msgstr "Strikethrough"
msgid "Subfolder under current folder"
msgstr "Subfolder under current folder"
+#: src/components/HelpPopover.tsx
+#: src/components/SubscribeForm.tsx
+msgid "Subscribe"
+msgstr "Subscribe"
+
+#: src/components/SubscribeForm.tsx
+msgid "Subscribing..."
+msgstr "Subscribing..."
+
+#: src/components/SubscribeForm.tsx
+msgid "Subscriptions aren't available right now."
+msgstr "Subscriptions aren't available right now."
+
#: src/lib/keyboard-shortcuts.ts
msgid "Suggestion menu navigation"
msgstr "Suggestion menu navigation"
@@ -6114,6 +6168,10 @@ msgstr "Terminal settings not loaded yet — try again in a moment."
msgid "Text"
msgstr "Text"
+#: src/components/SubscribeForm.tsx
+msgid "Thanks for subscribing. Watch your inbox for product updates."
+msgstr "Thanks for subscribing. Watch your inbox for product updates."
+
#: src/components/ShareReceiveDialog.tsx
msgid "That folder isn't an OpenKnowledge project yet. Initialize it and open?"
msgstr "That folder isn't an OpenKnowledge project yet. Initialize it and open?"
@@ -6808,6 +6866,10 @@ msgstr "We'll build <0>openknowledge.skill0>, save it to <1>~/Downloads1>, a
msgid "We'll create a repository and start syncing — no terminal needed."
msgstr "We'll create a repository and start syncing — no terminal needed."
+#: src/components/HelpPopover.tsx
+msgid "Website"
+msgstr "Website"
+
#: src/components/NewSkillDialog.tsx
msgid "What agents match on to decide when to use the skill."
msgstr "What agents match on to decide when to use the skill."
@@ -6833,6 +6895,10 @@ msgstr "What should the AI do? (optional)"
msgid "What would you like to create?"
msgstr "What would you like to create?"
+#: src/components/HelpPopover.tsx
+msgid "What's new"
+msgstr "What's new"
+
#: src/components/settings/SettingsDialogBody.tsx
msgid "When enabled, the agent opens or refreshes the preview after each edit. Disable if you manage your own preview window (OK Desktop, a browser tab on another display, etc.)."
msgstr "When enabled, the agent opens or refreshes the preview after each edit. Disable if you manage your own preview window (OK Desktop, a browser tab on another display, etc.)."
@@ -6962,6 +7028,10 @@ msgstr "You picked the filesystem root (/). Scaffolding here will scan every fil
msgid "You picked your home directory. OpenKnowledge will index everything in your home tree — large and may surface personal files."
msgstr "You picked your home directory. OpenKnowledge will index everything in your home tree — large and may surface personal files."
+#: src/components/SubscribeForm.tsx
+msgid "You're subscribed!"
+msgstr "You're subscribed!"
+
#: src/components/settings/SettingsDialogBody.tsx
#: src/components/SyncStatusBadge.tsx
msgid "Your GitHub session expired — sign in again to verify push access."
diff --git a/packages/app/src/locales/pseudo/messages.json b/packages/app/src/locales/pseudo/messages.json
index 99aeea713..e56121e63 100644
--- a/packages/app/src/locales/pseudo/messages.json
+++ b/packages/app/src/locales/pseudo/messages.json
@@ -108,6 +108,7 @@
"2Znqb1": [" (ḿĩśśĩńĝ)"],
"2bVHbB": ["Àvàĩĺàƀĺē ĩń ēvēŕŷ ƥŕōĴēćţ ōń ţĥĩś ćōḿƥũţēŕ."],
"2fkdnR": ["Ńōţĥĩńĝ ţō ũńďō ōń ţĥĩś ƒĩĺē"],
+ "2hYmUH": ["ḿŷ@ēḿàĩĺ.ćōḿ"],
"2hpdRq": ["Ĥĩďē ƥàńēĺ (", ["panelShortcutLabel"], ")"],
"2n3oDU": [
"ƤŕōĴēćţ-ĺēvēĺ ƥàĝēś ōŕďēŕēď ƀŷ ĩńƀōũńď ĺĩńķ ćōũńţ, ũƥ ţō ",
@@ -146,6 +147,7 @@
"3a_Bd7": ["Ōƥēń śōũŕćē ďōć: ", ["mirrorSrc"]],
"3aeN7X": ["Àĺĺ ƒĩĺēś"],
"3cOo2C": ["Ćŕēàţē à ƒōĺďēŕ ƒŕōḿ ţĥē ćũŕŕēńţ ďōćũḿēńţ ōŕ ƒōĺďēŕ ćōńţēxţ."],
+ "3ePd3I": ["Ŵĥàţ'ś ńēŵ"],
"3h5zMp": ["Śōḿēţĥĩńĝ ŵēńţ ŵŕōńĝ ĺōàďĩńĝ ţĥē śēţţĩńĝś ƥàńēĺ."],
"3iyGWF": ["Ōƥēń ŕēćēńţ ƥŕōĴēćţ"],
"3jod3l": ["Ćōńńēćţ ĜĩţĤũƀ"],
@@ -188,6 +190,7 @@
"4g19V6": ["Ƒàĩĺēď ţō ũƥďàţē ƥŕōƥēŕţŷ"],
"4hJhzz": ["Ţàƀĺē"],
"4jDd0j": ["Àńŷōńē ćàń śēē"],
+ "4mLBx9": ["Śũƀśćŕĩƥţĩōńś àŕēń'ţ àvàĩĺàƀĺē ŕĩĝĥţ ńōŵ."],
"4niEzY": ["Śàńďƀōxēď ĤŢḾĹ ēḿƀēď — ŵŕĩţē ĤŢḾĹ, śēē ţĥē ŕēńďēŕēď ƥŕēvĩēŵ ĺĩvē."],
"4nj6n8": ["/àƥĩ/ćōńƒĩĝ ŕēţũŕńēď ĤŢŢƤ ", ["code"]],
"4rjwLG": ["Ćŕēàţē ŵĩţĥ ", ["0"], " ĆĹĨ"],
@@ -410,6 +413,7 @@
"AQGtjK": [
"Ćōḿḿàńďś ŕũń ŵĩţĥ ţĥē ƒũĺĺ àććēśś ōƒ ŷōũŕ ḿàćŌŚ ũśēŕ àććōũńţ ōń ţĥĩś ḿàćĥĩńē. Ţũŕń ţĥĩś ōƒƒ ţō ďĩśàƀĺē ţĥē śĥēĺĺ."
],
+ "ATGYL1": ["Ēḿàĩĺ àďďŕēśś"],
"AVW-K9": ["Ƒàĩĺēď ţō ĺōàď ōŕƥĥàń ƥàĝēś"],
"AWOSPo": ["Źōōḿ ĩń"],
"AcK_r0": ["Ćĺàũďē ĆĹĨ"],
@@ -529,6 +533,7 @@
"E7BhZn": ["ßĺàńķ ńōţē"],
"E9bKSA": ["Ćŕēàţĩńĝ"],
"EBVbtP": ["Ćōũĺď ńōţ ďũƥĺĩćàţē ĩţēḿ"],
+ "EDl9kS": ["Śũƀśćŕĩƀē"],
"EDm3wJ": ["Ĺōàďĩńĝ ďĩƒƒ ŕēńďēŕēŕ"],
"EEYbdt": ["Ƥũƀĺĩśĥ"],
"EJuO7B": ["Ćōńńēćţ à ĜĩţĤũƀ àććōũńţ ţō ƀŕōŵśē àńď śŷńć ŷōũŕ ŕēƥōśĩţōŕĩēś."],
@@ -650,6 +655,7 @@
"GubkSu": ["Ũśē à śţàŕţēŕ ƥàćķ"],
"GuqOb1": ["Ƒàĩĺēď ţō ĺōàď ĥēàďĩńĝś"],
"Gx0VS1": ["ē.ĝ. ďŕàƒţś/ ōŕ *.ďŕàƒţ.ḿď"],
+ "GzPQ2f": ["Śţàŷ ĩń ţĥē ĺōōƥ"],
"H-vDm3": ["Ŕēĺàũńćĥĩńĝ ţō ĩńśţàĺĺ ţĥē ũƥďàţē…"],
"H0WlWQ": ["Ĝŕĩď ōƒ ŕōŵś àńď ćōĺũḿńś ŵĩţĥ à ĥēàďēŕ ŕōŵ."],
"H86f9p": ["Ćōĺĺàƥśē"],
@@ -915,6 +921,7 @@
"Okd2Xv": ["Ţōĝĝĺē ũńďēŕĺĩńē ƒōŕḿàţţĩńĝ."],
"OlAl5i": ["Ēxƥàńď àĺĺ"],
"OmHTGj": ["Ďĩśàƀĺē ĝĩţ àũţō-śŷńć"],
+ "On0aF2": ["Ŵēƀśĩţē"],
"Oo_WWc": ["Ńō ƥàĝēś ƒōũńď"],
"Oos8fo": ["Ćōƥŷ ćōḿḿàńď"],
"OuY8t9": ["Ōƥēń ĩń ćũŕŕēńţ ƀŕàńćĥ"],
@@ -932,7 +939,9 @@
"PB4upl": ["Ƒĩńď ƥŕēvĩōũś ḿàţćĥ"],
"PBwmDJ": ["Śēàŕćĥĩńĝ ƀŷ ḿēàńĩńĝ"],
"PBxg_E": ["Ńōţ ńōŵ"],
+ "PCNIuJ": ["Ƒĩĺē àń ĩśśũē"],
"PCSkw2": ["Śķĩĺĺś"],
+ "PIMM5l": ["Ŷōũ'ŕē śũƀśćŕĩƀēď!"],
"PKLY-1": ["Ōƥēńĩńĝ..."],
"PMyJ9C": ["Ńō ēďĩţ śēśśĩōń ŷēţ"],
"PPFQ3o": ["Ĩńśţàĺĺēď \"", ["0"], "\" ĩńţō ", ["1"]],
@@ -1017,6 +1026,7 @@
"SFN6dN": ["Ĥēàďĩńĝ 3"],
"STTOnz": [["keyName"], " ţŷƥē: ćōḿƥĺēx vàĺũē (ńēśţēď; ŕēàď-ōńĺŷ)"],
"SUOoXn": ["Ƒàĩĺēď ţō ōƥēń ƥŕōĴēćţ."],
+ "SVLPt5": [["formatted"], " ĜĩţĤũƀ śţàŕś"],
"SXCSQd": ["Ćōƥĩēď ƥàţĥ"],
"SYfHmN": ["Ũńƥĩń ţàƀ"],
"Sc1bKE": [
@@ -1142,6 +1152,8 @@
"Wgmlsw": ["Ƒēţćĥĩńĝ"],
"Wh0im4": ["ēďĩţĩńĝ [[", ["realCurrentDoc"], "]]"],
"WhOsNE": ["Śţŕĩķēţĥŕōũĝĥ"],
+ "WmPbvm": ["Ĝēţ ƥŕōďũćţ ũƥďàţēś ĩń ŷōũŕ ĩńƀōx."],
+ "Wq6Wcm": ["Ƥĺēàśē ēńţēŕ à vàĺĩď ēḿàĩĺ àďďŕēśś."],
"WqLq6M": ["Ōƥēń ŵĩţĥ ÀĨ ", ["displayName"]],
"Wt5W56": ["Śŷńć ĩś ōƒƒ — ŷōũŕ ēďĩţś ŵĩĺĺ ńōţ śŷńć ţō ţĥē ŕēḿōţē ŕēƥōśĩţōŕŷ."],
"Wtw22s": ["Ĩńśţàĺĺĩńĝ"],
@@ -1312,6 +1324,7 @@
"aiArms": ["Ŵē ćōũĺďń'ţ ćĺōńē ţĥĩś ŕēƥōśĩţōŕŷ."],
"ajz9F8": ["Ćōũĺďń'ţ ĺōàď ćōńƒĺĩćţ ćōńţēńţ ƒōŕ ", ["filePath"], ". Ţŕŷ ŕēĺōàďĩńĝ ţĥē ƥàĝē."],
"aoa6xA": ["ńēţŵōŕķ ēŕŕōŕ (ĩś `ōķ ũĩ` ŕũńńĩńĝ?)"],
+ "arUUsK": ["Ƥŕōďũćţ ũƥďàţēś"],
"az8lvo": ["Ōƒƒ"],
"b3Thhd": ["Ũƥĺōàď ƒàĩĺēď"],
"b4AQFK": ["Ōƥēń àćţĩvĩţŷ ƥàńēĺ ƒōŕ ", ["tooltipName"], ", ēďĩţĩńĝ ", ["realCurrentDoc"]],
@@ -1357,6 +1370,7 @@
"cZ_8zB": ["Śţàŷś ōń ţĥĩś ćōḿƥũţēŕ."],
"cdm00A": ["Ńō Ĩńćōḿĩńĝ"],
"cdmk8A": ["Ũńćōḿḿĩţţēď ćĥàńĝēś"],
+ "chL5IG": ["Ćōḿḿũńĩţŷ"],
"cklVjM": ["Ţĩḿēĺĩńē"],
"cn1sLi": ["Ćōũĺďń'ţ ćōńńēćţ ŌƥēńĶńōŵĺēďĝē ţōōĺś ţō Ćĺàũďē Ćōďē. Ƥĺēàśē ţŕŷ àĝàĩń."],
"cnGeoo": ["Ďēĺēţē"],
@@ -1435,6 +1449,7 @@
"fNr-MX": ["Ďĩśàƀĺē śēḿàńţĩć śēàŕćĥ"],
"fVcm9t": [["0", "plural", { "one": ["#", " ďōć"], "other": ["#", " ďōćś"] }]],
"fWi7pn": ["Ĩńĩţĩàĺĩźē śţàŕţēŕ ƥàćķ"],
+ "fWsBTs": ["Śōḿēţĥĩńĝ ŵēńţ ŵŕōńĝ. Ƥĺēàśē ţŕŷ àĝàĩń."],
"fYKgzG": ["Ƒàĩĺēď ţō ĺōàď ŕēćēńţ ƥŕōĴēćţś."],
"fYjgXu": ["Ũńōŕďēŕēď ĺĩśţ ōƒ ĩţēḿś."],
"f_PkYv": [
@@ -1624,6 +1639,7 @@
"l6OXqI": ["Ńō ĩńśţàĺĺēď àĝēńţś ƒōũńď"],
"l8G3S9": ["Àũţō-śŷńć ĩś ōƒƒ — ŷōũ ďōń'ţ ĥàvē ƥēŕḿĩśśĩōń ţō ƥũśĥ ţō ţĥĩś ŕēƥō"],
"lAiyU_": ["Ōńĺŷ ţĥē ćũŕŕēńţ ďōćũḿēńţ ũśēś <0>#", ["tag"], "0>."],
+ "lCC_D5": ["Śũƀśćŕĩƀĩńĝ..."],
"lGs_bQ": ["Ƥàĝē <0>*0>"],
"lHwYMr": ["Śēŕvēŕ ēŕŕōŕ: ", ["status"], " ", ["statusText"]],
"lI0Td2": ["Ũśē ĺēţţēŕś, ďĩĝĩţś, <0>_0> àńď <1>-1> ōńĺŷ."],
@@ -1776,6 +1792,7 @@
"p0Iq25": ["Ĩńśēŕţ ĺĩńķ"],
"p9sdCy": ["Ńēŵ ĩĝńōŕē ƥàţţēŕń"],
"pAgkuZ": ["Ŕēōƥēńĩńĝ \"", ["docName"], "\" ŵĩţĥ à ƒŕēśĥ ĺōćàĺ ćōĺĺàƀōŕàţĩōń ćàćĥē."],
+ "pBROkv": ["Ţĥàńķś ƒōŕ śũƀśćŕĩƀĩńĝ. Ŵàţćĥ ŷōũŕ ĩńƀōx ƒōŕ ƥŕōďũćţ ũƥďàţēś."],
"pEQFsW": ["Ńō ḿàţćĥĩńĝ ƥŕōĴēćţś."],
"pFagOn": ["Śōũŕćē ēďĩţĩńĝ"],
"pHYP6v": ["Ōƥēń <0>Ćũśţōḿĩźē0> ĩń ţĥē śĩďēƀàŕ <1/> <2>Śķĩĺĺś2>."],
diff --git a/packages/app/src/locales/pseudo/messages.po b/packages/app/src/locales/pseudo/messages.po
index 3e1e93599..1c57c1629 100644
--- a/packages/app/src/locales/pseudo/messages.po
+++ b/packages/app/src/locales/pseudo/messages.po
@@ -270,6 +270,10 @@ msgstr ""
msgid "{fileCount, plural, one {# file} other {# files}}"
msgstr ""
+#: src/components/HelpPopover.tsx
+msgid "{formatted} GitHub stars"
+msgstr ""
+
#: src/components/ActivityModeContent.tsx
#: src/components/ActivityPanelFileRow.tsx
#: src/components/SyncStatusBadge.tsx
@@ -1138,6 +1142,7 @@ msgstr ""
#: src/components/EditorTabs.tsx
#: src/components/InstallInClaudeDesktopDialog.tsx
#: src/components/SeedDialog.tsx
+#: src/components/SubscribeForm.tsx
#: src/components/ui/dialog.tsx
#: src/components/ui/sheet.tsx
#: src/editor/find-replace/FindReplaceBar.tsx
@@ -1267,6 +1272,10 @@ msgstr ""
msgid "Commits happen automatically"
msgstr ""
+#: src/components/HelpPopover.tsx
+msgid "Community"
+msgstr ""
+
#: src/components/empty-state/use-create-suggestions.ts
msgid "Competitor research"
msgstr ""
@@ -2325,6 +2334,10 @@ msgstr ""
msgid "Email"
msgstr ""
+#: src/components/SubscribeForm.tsx
+msgid "Email address"
+msgstr ""
+
#: src/editor/slash-command/component-items.tsx
msgid "Embed a video with native player controls."
msgstr ""
@@ -2710,6 +2723,10 @@ msgstr ""
msgid "File already exists"
msgstr ""
+#: src/components/HelpPopover.tsx
+msgid "File an issue"
+msgstr ""
+
#: src/components/FileTree.tsx
msgid "File drop zone"
msgstr ""
@@ -2894,6 +2911,10 @@ msgstr ""
msgid "Get Claude Code"
msgstr ""
+#: src/components/SubscribeForm.tsx
+msgid "Get product updates in your inbox."
+msgstr ""
+
#: src/components/settings/SettingsDialogBody.tsx
msgid "Git auto-sync"
msgstr ""
@@ -3743,6 +3764,10 @@ msgstr ""
msgid "Multi-page PDF viewer with toolbar controls (thumbnails, page nav, zoom)."
msgstr ""
+#: src/components/SubscribeForm.tsx
+msgid "my@email.com"
+msgstr ""
+
#: src/components/SkillProperties.tsx
#: src/components/TemplateProperties.tsx
msgid "name"
@@ -4687,6 +4712,10 @@ msgstr ""
msgid "Plain"
msgstr ""
+#: src/components/SubscribeForm.tsx
+msgid "Please enter a valid email address."
+msgstr ""
+
#: src/components/settings/SettingsDialogBody.tsx
#: src/components/settings/SettingsDialogShell.tsx
msgid "Preferences"
@@ -4724,6 +4753,10 @@ msgstr ""
msgid "probe failed"
msgstr ""
+#: src/components/HelpPopover.tsx
+msgid "Product updates"
+msgstr ""
+
#: src/components/settings/ProjectTemplatesSection.tsx
msgid "project"
msgstr ""
@@ -5772,6 +5805,10 @@ msgstr ""
msgid "Something went wrong starting the terminal."
msgstr ""
+#: src/components/SubscribeForm.tsx
+msgid "Something went wrong. Please try again."
+msgstr ""
+
#: src/lib/keyboard-shortcuts.ts
msgid "Source editing"
msgstr ""
@@ -5856,6 +5893,10 @@ msgstr ""
msgid "status"
msgstr ""
+#: src/components/SubscribeForm.tsx
+msgid "Stay in the loop"
+msgstr ""
+
#: src/components/settings/SharingSection.tsx
#: src/components/SharingModeField.tsx
msgid "Stays on this computer."
@@ -5881,6 +5922,19 @@ msgstr ""
msgid "Subfolder under current folder"
msgstr ""
+#: src/components/HelpPopover.tsx
+#: src/components/SubscribeForm.tsx
+msgid "Subscribe"
+msgstr ""
+
+#: src/components/SubscribeForm.tsx
+msgid "Subscribing..."
+msgstr ""
+
+#: src/components/SubscribeForm.tsx
+msgid "Subscriptions aren't available right now."
+msgstr ""
+
#: src/lib/keyboard-shortcuts.ts
msgid "Suggestion menu navigation"
msgstr ""
@@ -6109,6 +6163,10 @@ msgstr ""
msgid "Text"
msgstr ""
+#: src/components/SubscribeForm.tsx
+msgid "Thanks for subscribing. Watch your inbox for product updates."
+msgstr ""
+
#: src/components/ShareReceiveDialog.tsx
msgid "That folder isn't an OpenKnowledge project yet. Initialize it and open?"
msgstr ""
@@ -6803,6 +6861,10 @@ msgstr ""
msgid "We'll create a repository and start syncing — no terminal needed."
msgstr ""
+#: src/components/HelpPopover.tsx
+msgid "Website"
+msgstr ""
+
#: src/components/NewSkillDialog.tsx
msgid "What agents match on to decide when to use the skill."
msgstr ""
@@ -6828,6 +6890,10 @@ msgstr ""
msgid "What would you like to create?"
msgstr ""
+#: src/components/HelpPopover.tsx
+msgid "What's new"
+msgstr ""
+
#: src/components/settings/SettingsDialogBody.tsx
msgid "When enabled, the agent opens or refreshes the preview after each edit. Disable if you manage your own preview window (OK Desktop, a browser tab on another display, etc.)."
msgstr ""
@@ -6957,6 +7023,10 @@ msgstr ""
msgid "You picked your home directory. OpenKnowledge will index everything in your home tree — large and may surface personal files."
msgstr ""
+#: src/components/SubscribeForm.tsx
+msgid "You're subscribed!"
+msgstr ""
+
#: src/components/settings/SettingsDialogBody.tsx
#: src/components/SyncStatusBadge.tsx
msgid "Your GitHub session expired — sign in again to verify push access."
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index 6e8611aee..7eb3a7c3a 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -1071,6 +1071,7 @@ export { createCodeFenceTracker } from './utils/code-fence-tracker.ts';
export { isEmbedUrlRewritable, rewriteEmbedUrl } from './utils/embed-url-rewrite.ts';
export { extensionOf } from './utils/extension.ts';
export { formatFileSize } from './utils/file-size.ts';
+export { getGitHubStars } from './utils/github-stars.ts';
export {
AGENT_COLORS,
AGENT_ICON_COLORS,
diff --git a/docs/src/lib/github-stars.ts b/packages/core/src/utils/github-stars.ts
similarity index 64%
rename from docs/src/lib/github-stars.ts
rename to packages/core/src/utils/github-stars.ts
index b63da28cd..eb408e11d 100644
--- a/docs/src/lib/github-stars.ts
+++ b/packages/core/src/utils/github-stars.ts
@@ -1,14 +1,18 @@
const REPO_API_URL = 'https://api.github.com/repos/inkeep/open-knowledge';
-export async function getGitHubStars(): Promise {
+export async function getGitHubStars(init?: RequestInit): Promise {
+ const { signal: callerSignal, ...restInit } = init ?? {};
+ const signal = callerSignal
+ ? AbortSignal.any([AbortSignal.timeout(5_000), callerSignal])
+ : AbortSignal.timeout(5_000);
try {
const res = await fetch(REPO_API_URL, {
- signal: AbortSignal.timeout(5_000),
- next: { revalidate: 3600 },
headers: {
accept: 'application/vnd.github+json',
- 'user-agent': 'openknowledge.ai site nav',
+ 'user-agent': 'openknowledge.ai',
},
+ ...restInit,
+ signal,
});
if (!res.ok) {
console.warn(`[github-stars] GitHub API responded ${res.status}`);