Conversation
Remove Unused ai Function Add Protections For when openai_key is undefined
Update Dependencies
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- actions/checkout, setup-node, cache: v2 -> v4 - Node 16 -> 20 in all three workflows and CONTRIBUTING.md - Replace manual npm cache steps with setup-node's built-in npm cache - Replace archived actions/upload-release-asset with softprops/action-gh-release@v2 - Replace w9jds/firebase-action@master (unpinned third-party action) with direct firebase-tools deploy using GOOGLE_APPLICATION_CREDENTIALS - Drop obsolete FIREBASE_CLI_PREVIEWS env flag from preview workflow Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts: # .github/CONTRIBUTING.md # .github/workflows/cd.yml # .github/workflows/ci.yml # .github/workflows/preview.yml
- Functions runtime 18 -> 24 (max GA runtime on Cloud Functions; 20 is already past its deprecation date) - firebase-functions 4 -> 7, firebase-admin 11 -> 13 (v7 peer cap), typescript 5.9; imports moved to firebase-functions/v1 subpath - Delete dead gpt3_parent function (never called by client, used retired text-davinci-002); remove unused cowsay dep - CI/CD workflows and CONTRIBUTING.md updated to Node 24; add .nvmrc - Fix pre-existing lint/format drift from the Vite branch Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- npm update across the board (MUI 5.18, router 6.30, react 18.3, emotion, eslint plugins, prettier 3.9) - typescript 5.9.3 (root + functions), @types/node 24 in both packages, functions tsconfig target es2017 -> es2023 - firebase-tools 12 -> 15 (required for nodejs24 deploys) - eslint-config-prettier 8 -> 9 - Remove dotenv (never imported; Vite loads .env itself) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- vite 4 -> 8, @vitejs/plugin-react 4 -> 6, vitest 0.33 -> 4, vite-tsconfig-paths 4 -> 6, vite-plugin-pwa 0.16 -> 1.3, jsdom 22 -> 29 - Teach eslint-plugin-import about ESM-only vite packages (import/core-modules) since its node resolver can't follow exports maps Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…upstream - react-router-dom 6.30 -> 7.18 (component APIs unchanged in our usage) - Replace @kokarn/react-zoom-pan-pinch 2.1.5 (unmaintained fork) with upstream react-zoom-pan-pinch 3.7: context.state -> context.transformState, setTransform now comes from useControls(); getContext() shimmed to keep the parent resetCanvas call shape - Drop both react-18 peer overrides (upstream supports react 18 natively) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pan-pinch 4 react-zoom-pan-pinch 4 renamed the instance's transformState back to state (matching the old fork), so the migration shim simplifies. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Visit the preview URL for this PR (updated for commit 1ff7df2): https://mind-map-testing--pr199-revival-2m3jalm0.web.app (expires Tue, 14 Jul 2026 04:43:27 GMT) 🔥 via Firebase Hosting GitHub Action 🌎 Sign: 37740e843bbe4e42b860dff5fdcfb0718403cafd |
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SpiritSeal
requested changes
Jul 6, 2026
epodol
marked this pull request as draft
July 6, 2026 20:26
… feedback) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The update rule read permissions.canPublicEdit from the incoming write (request.resource.data — attacker-controlled) instead of the existing document, so any public map, including view-only ones, could be modified or fully taken over by any request that smuggled canPublicEdit: true. New rules: public-edit eligibility comes from resource.data; every read and write path requires auth (the app always signs users in, at minimum anonymously); non-owners cannot touch permissions; create must set permissions.owner, metadata.createdBy, and everUpdatedBy to the caller's uid; document schema is validated (allowed keys + types) on every write; owners cannot transfer ownership (no UI exists for it). Tests (@firebase/rules-unit-testing v2, pinned to match the firebase 9 client hold — bump to v5 with #191) cover the exploit scenarios plus all legitimate app write/read/query shapes; they run under npm run test via emulators:exec and skip when no emulator is available. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…189) The values are Firebase Web SDK client config — public identifiers that ship in the JS bundle, not secrets — so .env.example carries the real values with a header explaining that, and cd.yml/preview.yml copy it to .env before building (Vite reads .env at build time; without this, deployed builds would get undefined config). Fork users substitute their own project config. Also adds a dev-setup section to the README. Remaining human step from #189: verify API-key HTTP-referrer restrictions in the Google Cloud console for both projects. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
react-hotkeys has been unmaintained since ~2020 and is the likely root cause of #147 (GENERATE_IDEAS / TOGGLE_SIDE_MENU not firing). All bindings now live in src/pages/MindMap/keybindings.ts and are wired with useHotkeys per component; the GlobalHotKeys wrapper elements are gone. Binding strings are matched against KeyboardEvent.code in v5, hence 'backquote' for the old '`' and 'delete' for 'del'. Callbacks passed without a deps array are kept fresh via an internal ref, and enableOnFormTags defaults to false, so hotkeys still don't fire while typing in inputs/dialogs. Needs the manual canvas click-through (already on the human checklist) to confirm #147 is fixed before closing it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Decision: keep PWA support. vite-plugin-pwa (already installed) now has a real config — registerType autoUpdate (no update-prompt UI exists, so silent refresh) and the web manifest moved from public/manifest.json into the plugin config, which injects the link tag itself. Deleted dead code: public/service-worker.js, src/serviceWorker.js (registration was already commented out post-Vite), public/offline.html and public/manifest.json. Returning-user takeover: the old CRA-era worker was network-first for navigations (offline fallback only), so returning users fetch the new HTML, registerSW.js re-registers /sw.js at the same scope and replaces the old registration; index.tsx deletes the orphaned 'offline' cache. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…der Vite) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ff notes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Node IDs were minted client-side as max(id)+1 from the local snapshot, and edits were arrayRemove(oldCopy)+arrayUnion(newCopy) batches - both break under concurrent editing (duplicate IDs, silently duplicated or resurrected nodes when the local copy is stale). - node.id/parent are now strings; new nodes get crypto.randomUUID(). The root keeps the well-known ID '0' so legacy maps (numeric IDs, root 0) stay valid: normalizeNodes() coerces numbers to strings on read, and the first edit persists the normalized list. - All node ops run through one runTransaction wrapper: read a fresh doc, transform the normalized node list with pure helpers (src/nodeOps.ts), write the whole list back. Concurrent-edit semantics: updates of deleted nodes no-op instead of resurrecting; adds under a deleted parent reattach to the root. - 16 vitest unit tests for the pure helpers, plus 3 rules tests locking in the exact transaction write shape (nodes list + dot-path metadata + arrayUnion) for owner/public-editor/view-only. The rules validate nodes only as a <=5000-entry list, so no rules change is needed for the ID type change. Undo (#198) is now unblocked. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 tasks
gpt3.ts called createCompletion with gpt-3.5-turbo (a chat model on the legacy completions endpoint) - broken in prod for years. Swap to Groq's OpenAI-compatible chat endpoint via plain fetch (Node 24 global), which also deletes the openai@3 dependency; no SDK, no provider abstraction. - One shared app-level GROQ_API_KEY Functions secret, same pattern as the old OPENAI_SECRET. Deploy needs a human to run `npx firebase-tools functions:secrets:set GROQ_API_KEY`. - Degrades gracefully: missing key, bad input, quota bursts, or API errors all log and return [], and the panel falls back to Datamuse. Emulator-verified: no-key -> [], invalid key -> real Groq 401 -> []. - The callable keeps its deployed name `gpt3` (renaming needs a client change plus an interactive function-delete on deploy); the panel header now says "AI Ideas" instead of "GPT3". - datamuse.ts never used the OpenAI secret; drop its runWith(secrets) so nothing references OPENAI_SECRET anymore. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 tasks
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
reactfire has been dead since Aug 2023 and pinned the client SDK to
firebase 9. Replace it with src/firebase/: module-level singleton SDK
init (app, App Check, auth, firestore, functions, emulator wiring) plus
the small hook surface the app actually uses — useAuth/useFirestore/
useFunctions (instance getters), useUser/useSigninCheck (one app-wide
onIdTokenChanged subscription behind FirebaseUserProvider, which blocks
render until the initial auth state is known), and
useFirestoreDocData/useFirestoreCollection/useFirestoreCollectionData
(onSnapshot with a { status, data } shape; queryEqual-stable
resubscription so callers can build queries inline).
Also, per the roadmap notes for this pass:
- Delete the unused Storage and Remote Config inits (nothing ever read
them; remoteconfig.template.json was an empty {}), and the firebase.json
remoteconfig block.
- Fix the rules-of-hooks bug in MindMap by splitting it into a loader
(subscribes to the doc, shows Loading) and LoadedMindMap (all hooks
unconditional); a missing or permission-denied doc now surfaces the
intended friendly message instead of a raw TypeError.
- Navigation/ManageMindMaps handle the now-explicit collection loading
state instead of relying on reactfire suspense.
- firebase 9 -> 12, @firebase/rules-unit-testing 2 -> 5 (lockfile
regenerated; the old lock still carried reactfire's ^9 pin).
All 41 tests (rules + nodeOps) pass under the Firestore emulator; tsc,
eslint, prettier, and the production build are clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Go decision: MUI skipped v8; v9 (9.2) still defaults to emotion (pigment-css peer is optional) and supports React 19, so we take the latest major directly rather than parking on 6/7. Actual churn was small, confirmed by inventory before migrating: - `@mui/codemod deprecations/all` converted TextField inputProps/ InputProps -> slotProps.htmlInput/input, Snackbar TransitionProps -> slotProps.transition, ListItemText primaryTypographyProps -> slotProps.primary, Drawer PaperProps -> slotProps.paper. - Manual: Menu PaperProps and InputBase inputProps (codemod missed them), and the nine legacy `Grid item xs/sm` usages -> the v9 Grid `size` prop (GridLegacy is removed in v9). - React 19: @types/react 19 requires an initial value for useRef (MindMapSimulation childRef); everything else was already compatible (createRoot, no defaultProps, no legacy context). react-zoom-pan-pinch 4, react-router-dom 7, react-hotkeys-hook 5, and testing-library 16 all declare React 19 peer support. tsc, eslint, prettier, the 41-test emulator suite, and the production build are all green. Visual smoke check of the routes rides on the existing manual click-through item on the human checklist. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… + React 19) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Jul 7, 2026
Export menu in the map bottom bar. SVG export clones the live canvas element, crops it to the full map extent via getBBox, and inlines background/font so the file stands alone; PNG rasterizes that SVG at 2x (capped at 4096px). Markdown walks the tree from the root into an indented outline, with cycle protection and orphan recovery. Read-only: works on view-only maps. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Each committed transaction records a per-node before/after diff (diffNodes) onto an in-memory stack (max 100, cleared on refresh). Ctrl/Cmd+Z pops one entry and inverts it in a new transaction via withChangesUndone, where every piece applies only if the node still looks the way the recorded operation left it — a newer remote edit wins and the conflicting piece is skipped, never resurrected. Undoing a delete re-adds the node and restores its reparented children; undo transactions are not themselves recorded (no redo). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
/privacy covers the actual data inventory: Firebase Auth incl. auto-created anonymous guest accounts, Firestore map content and edit history, Groq/Datamuse suggestion calls, Analytics/Performance Monitoring, App Check reCAPTCHA v3, and the COPPA posture (no ads, no selling, parent contact). Footer (privacy/GitHub/contact links + App Challenge award) follows the Navigation pattern: present on Home, About, Privacy, SignIn, CreateAccount, Account, ManageMindMaps; absent on the map canvas. Policy text is a DRAFT: human review required before production deploy, and anonymous-account auto-cleanup must be enabled in the Firebase console. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Jul 7, 2026
…promise (#196) Human-review pass on the draft: replace the "we do not show ads" claim with a commitment that ads (if any ever appear) receive no user data; document the existing in-app Delete Account flow and that it does not cascade to maps; drop the self-imposed promise to announce changes on the home page. Also document VITE_RECAPTCHA_PUBLIC_KEY in .env.example — CD builds copy that file verbatim, so App Check never initializes in production until the real site key is committed there. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Keep the existing hero (logo, title, tagline, one primary CTA) and add the standard landing sections below it: award social proof, six feature blurbs (including Wave 4 export), a three-step how-it-works, an FAQ accordion group with matching FAQPage JSON-LD, and a closing CTA. The CTA now disables while the anonymous sign-in/map creation is in flight, the hero uses the 38KB SVG logo instead of the 367KB PNG, and the page is semantic (single h1, sequential h2/h3, main landmark). SEO surface: descriptive <title>, richer meta description, canonical URL, WebApplication JSON-LD replacing the orphaned itemprop microdata, URL-encoded og/twitter image URLs, plus robots.txt (disallow /mindmaps and /account) and sitemap.xml. Verified end-to-end with Playwright against the emulators: all sections render light/dark/mobile with no console errors, and the hero CTA creates a guest account and lands on a working canvas. Recipe persisted in .claude/skills/verify. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Public client identifier; CD copies .env.example into .env at build time, so this makes production builds initialize App Check. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
epodol
marked this pull request as ready for review
July 7, 2026 04:48
Owner
SpiritSeal
requested changes
Jul 7, 2026
Owner
There was a problem hiding this comment.
Few things:
- Let's add tests across all of this code we are adding. Generally, I'm hoping for ~80% code coverage on business logic and >70% coverage on UI and everything else. Use techniques like dependency injection to model for nondeterministic components like the LLM functions.
- I see that we are changing some of the GCP products we are using. Could I get an updated cost analysis?
- Let's talk about deleting stale anon accounts. Do we really want to do this for now? It's not really costing us anything to keep them at our current scale. I'm probably fine with the code we have written for it (haven't read it yet), but may be worth
&& falseing it for now until we have a business need to activate it - Could we have an updated live preview URL for these changes
- Let's have an LLM do a rudimentary security model and security analysis of our app and make sure there aren't any gaping holes (post results mb in changelog or new similarly styled dir?). Would p also be worth adding a SECURITY.md and security policy to bubblemap in case a security researcher finds something and would like to disclose it.
- Generally going forward, let's try to keep 1 PR as 1 atomic reviewable unit of code (i.e. 1 new feature)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Vibe: https://youtu.be/QWIcz3ab358 🎶
Revives the project after ~3 years of dormancy. The audited plan lives in
changelog/2026-07-04-revival-roadmap.md; this PR is the single integration branch for all of it. Kept as a draft until the waves below land.Why
permissions.canPublicEdit: truein its payload.npm auditfindings, almost all via deadreact-scripts.Landed so far
release-0.2.0(CRA → Vite);react-scriptsfully goneengines, CI,.nvmrc, CONTRIBUTING)actions/*@v4, archived/unpinned actions replaced, deploys viafirebase-toolsdirectlyai.ts,gpt3_parent.ts,cowsay,dotenv, peer-override blocksfirebase-tools(rest are deliberate holds, see roadmap)snyk-*branches deleted; backlog triaged into [SECURITY] Firestore rules: public maps can be hijacked — fix update/create rules and add rules unit tests #188–Local/session undo for the mind-map editor #198Wave 1 — security & unblockers (done)
.envhygiene:.envuntracked/gitignored, real (public, client-visible) values live in.env.example, CD/preview workflows copy it before buildingreact-hotkeyswithreact-hotkeys-hook5; bindings centralized insrc/pages/MindMap/keybindings.ts(should close GENERATE_IDEAS & TOGGLE_SIDE_MENU Broken #147 — pending manual verification)vite-plugin-pwa(autoUpdate + manifest), deleted the CRA-erapublic/service-worker.js/src/serviceWorker.js/offline.html; old registration is replaced at the same scope and its orphaned cache is cleaned up145delete-confirmation fix already landed onmainvia Manage MindMaps UI Improvements #146 (ID-scoped dialog state), andno-logout-if-no-accountwas superseded by Auth Improvements #152's anonymous-user handling a week later;334a91bon the145branch would actually regress the permissions schema. Both stale branches can be deleted.process.env.PUBLIC_URLis undefined under Vite)Wave 2 — write model & AI (done)
620f362):crypto.randomUUID()node IDs; all node ops go through a singlerunTransactionwrapper (read fresh doc → pure transform → write back), killing the arrayRemove/arrayUnion duplicate/resurrection failure modes. Pure helpers +normalizeNodes()(legacy numeric-ID coercion) insrc/nodeOps.ts; 16 unit tests + 3 rules tests locking in the transaction write shape. Unblocks Local/session undo for the mind-map editor #198 undo (its natural unit: one committed transaction).36d439d):gpt3.tsnow calls Groq's OpenAI-compatible chat endpoint via plainfetch(llama-3.1-8b-instant);openai@3deleted. Any failure (missing key, quota, API error) logs and returns[]so the panel degrades to Datamuse. Callable keeps its deployed namegpt3; not live until a human sets theGROQ_API_KEYsecret (checklist below).Wave 3 — remaining stack risk (done)
d0597dd)reactfire(dead since 2023) removed →src/firebase/module: singleton SDK init + the handful of hooks the app uses (useUser/useSigninCheckover oneonIdTokenChangedsubscription; Firestore doc/collection hooks overonSnapshotwithqueryEqual-stable resubscription). firebase 9 → 12,@firebase/rules-unit-testing2 → 5 (lockfile regenerated). Riders: unused Storage/Remote Config inits +remoteconfig.template.jsondeleted; MindMap rules-of-hooks bug fixed via loader/LoadedMindMapsplit — missing/denied docs now show the friendly error instead of a raw TypeError.1630982) MUI 5 → 9 + React 18.3 → 19.2 bundled. Go decision: MUI skipped v8, and v9 still defaults to emotion (pigment-css optional) with React 19 support, so we took the latest major directly.@mui/codemod deprecations/all+ manual fixes (MenuPaperProps, InputBaseinputProps, nine legacyGrid item xs/sm→size); React 19 needed one code change (useRefinitial value).Wave 4 — product (done)
c0ceaf7) PNG/SVG/Markdown export from a new menu in the map bottom bar. SVG clones the live canvas element and crops to the full map viagetBBox()(not just the viewport), inlining background/font so the file stands alone; PNG rasterizes that SVG at 2x (capped 4096px); Markdown walks the tree into an indented outline with cycle protection and orphan recovery. Read-only — works on view-only maps.3e171af) Session-local undo on Ctrl/Cmd+Z. Each committed transaction records a per-node before/after diff onto an in-memory stack (max 100, cleared on refresh); undo inverts one entry in a new transaction, and every piece applies only if the node still matches the state the recorded op left it in — newer remote edits win, conflicted pieces are skipped, never resurrected. No redo. Documented in the help dialog.4791c69)/privacypage + site footer. Policy drafted from the live data inventory (auth incl. auto-created anonymous guests, Firestore content/edit history, Groq + Datamuse suggestion calls, Analytics/Perf, App Check reCAPTCHA v3, COPPA posture). Footer (privacy/GitHub/contact/award) on everyNavigationroute, deliberately absent on the map canvas. Policy text is a DRAFT — see checklist.All four waves have landed. What remains before un-drafting is the human-only checklist below.
Human-only checklist (not automatable from this branch)
npx firebase-tools functions:secrets:set GROQ_API_KEY(thenOPENAI_SECRETcan be destroyed)src/pages/PrivacyPolicy/index.tsx) — it makes operational commitments (deletion requests via support@bubblemap.app, anonymous-account cleanup, COPPA handling) a human must stand behind; counsel review if being careful. Record the review here before un-drafting.🤖 Generated with Claude Code