Skip to content

store sprite.value again, use it properly in setSpriteSize#2

Merged
cpirich merged 1 commit into
stagingfrom
fix_setSprite
Sep 22, 2014
Merged

store sprite.value again, use it properly in setSpriteSize#2
cpirich merged 1 commit into
stagingfrom
fix_setSprite

Conversation

@cpirich

@cpirich cpirich commented Sep 22, 2014

Copy link
Copy Markdown
Contributor

A recent change broke setSpriteSize. setSprite used to store the value in sprite.value. That was useful so setSpriteSize() could call back into setSprite() with the same value as was previously set.

I restored the code that stores sprite.value.

And I added a conditional check in setSpriteSize() to avoid calling setSprite() when the sprite is hidden. The command will still work (so you can change the size before you set the image), but there is no need to execute all of the code in setSprite() unless the sprite is visible.

TODO: add a test case for setSpriteSize() so this doesn't happen again in the future :)

@davidsbailey

Copy link
Copy Markdown
Member

LGTM

cpirich added a commit that referenced this pull request Sep 22, 2014
store sprite.value again, use it properly in setSpriteSize
@cpirich cpirich merged commit fb1b2d6 into staging Sep 22, 2014
@cpirich cpirich deleted the fix_setSprite branch September 22, 2014 19:13
deploy-code-org added a commit that referenced this pull request Sep 22, 2014
commit fb1b2d6
Merge: 207e4db 0713f6b
Author: cpirich <chris@code.org>
Date:   Mon Sep 22 12:13:28 2014 -0700

    Merge pull request #2 from code-dot-org/fix_setSprite

    store sprite.value again, use it properly in setSpriteSize

commit 207e4db
Author: Brian Jordan <bcjordan@gmail.com>
Date:   Mon Sep 22 12:12:02 2014 -0700

    Update README.md
@Bjvanminnen Bjvanminnen mentioned this pull request Oct 10, 2014
Bjvanminnen added a commit that referenced this pull request Nov 14, 2014
[Finishes #81619770] new prebuilts
joshlory pushed a commit that referenced this pull request Mar 4, 2015
Format is now:
(1 stage, first level) "Hour of Code"
(1 stage, not first level) "Hour of Code #4"
(multiple stages) "20-Hour Intro Course: The Maze #2"
wjordan added a commit that referenced this pull request Sep 4, 2015
deploy-code-org added a commit that referenced this pull request Sep 4, 2015
commit fe06578
Merge: 4e0de33 fe7e738
Author: Brad Buchanan <bradley.c.buchanan@gmail.com>
Date:   Fri Sep 4 13:02:13 2015 -0700

    Merge pull request #3838 from code-dot-org/netsim-visualization-reset

    Consult UUIDs when deciding to destroy vizelements.

commit fe7e738
Author: Brad Buchanan <brad@code.org>
Date:   Fri Sep 4 12:38:39 2015 -0700

    Better comments.

commit 4e0de33
Merge: 24ae4ae 1c955c3
Author: Will Jordan <wjordan@users.noreply.github.com>
Date:   Fri Sep 4 12:25:55 2015 -0700

    Merge pull request #3877 from code-dot-org/rails_4_2_update

    Rails 4 2 update (extra fixes #2)

commit 1c955c3
Author: Will Jordan <will@code.org>
Date:   Fri Sep 4 12:21:28 2015 -0700

    load i18n locales before application fork

commit 24ae4ae
Merge: 8a999a3 6f3e0a7
Author: Andre Stackhouse <CaptainStack@outlook.com>
Date:   Fri Sep 4 12:12:26 2015 -0700

    Merge pull request #3876 from code-dot-org/automated-workshop-email-reminder-prerequisites

    Much simpler prerequisite calculation (not using activity_constants)
@bcjordan bcjordan mentioned this pull request Nov 20, 2015
1 task
philbogle added a commit that referenced this pull request Apr 19, 2016
Hamms added a commit that referenced this pull request May 3, 2016
bcjordan added a commit that referenced this pull request May 25, 2016
* Test running Circle test against Sauce Labs

* Update test

* Try adding colons

* Try indentation change

* Make map params a map

* - Decrypt levels
- Add sauce labs to locals.yml

* Try using full build

* Run all tests.
- Remove extra seeds/setup

* Skip default database seeding

* Try to force skipping database step

* Update runner to auto-retry

* Try 10 parallel tests

* Try not double-running grunt tests, even fewer parallel test instances

* Set circle tests to "green" mode. Should fix hourOfCode_signedIn.feature—(fixing it from logging out on visits to /hoc/1)

* Try 7 parallel instances (will check for out of memory error)

* Try 6 parallel instances

* Try removing require from steps

* Update circle.yml to copy over artifacts. Remove some gems from Gemfile based on bundle_require branch

* Remove moreutils and `ts` (moreutils conflicting with parallel used by CircleCI)

* Add blank? require

* Add time

* Require environment for i18n translate
- Require konacha in Gemfile again
- Include ActiveSupport keygenerator in cookie helpers

* Increase # parallel

* Try 13 parallel tests

* Try 10 parallel

* Try 9 tests

* Try 8 parallel

* Undo gemfile changes

* Move require rails env out into own helper

* Parallelize across containers

* Try running parallel failure in non-parallel

* Try removing single quotes

* Test adding [run-ui] annotation to run tests

* Check for `sudo npm` on Linux [run-ui]
- Fix alias yaml syntax

* Fix node reference (might be circular now?)

* Use `which node` for symlink

* Undo node changes, use npm_use_sudo: false

* Update to do initial database setup

* Build code studio, explicitly run apps tests using npm test

* Update instance node version

* Try building only if files changed (may skip many tests). [run-ui-tests]

* Use script for [run-ui-tests]

* Fix location

* Add new cache location [run-ui-tests]

* Move UI test skipping logic into Rakefile :circle namespace [ui test]

* New test rake tasks [test all] [test ui]

* Add system background helper, fix ./bin/sc command

* Fix rake param name [test ui] [test all]

* Try using system_with_hipchat_logging to get output on success

* Bump file [test ui] [test all]

* Fix indentation
- Collapse single settings
- Add longer timeout for long-running tests scripts with little output

* Bump for test run [test ui] [test all]

* Attempt to fix circle.yml formatting

* Attempt to fix circle.yml formatting #2 [test ui] [test all]

* Try using new system commands

* Bump for test [test ui]

* Fix timeout usage of rails helpers

* Use magic-retry for test rerun [test ui]

* Bump to test everything [test all]

* Move setup steps into dependencies phase so caching occurs.

* Fix up system command to not print 2>&1 for quiet commands

* Force development dependency installation [test ui]

* Try using --with [test ui]

* Try using --without '' [test ui]

* Try using 4 parallel [test ui]

* Try removing development exclusion after rake install

* Try also bundle installing after bundle config removal

* Move bundle install to end [test ui]

* Only use rerun when development [test all] [test ui]

* Try removing  ./bin/dashboard-server (already auto-running?) [test all] [test ui]
- Remove redundant DB steps

* Move set_hoc_flags to after DB setup [test all] [test ui]

* Move hoc flag comment

* Add back dashboard server [test ui]

* Try without set_hoc_flags [test all] [test ui]

* Set hoc flags to green (don't require login for star wars et al) [test all] [test ui]

* Try adding script.id log [test all]

* More debug calls [test all]

* Try running dashboard with rails/rack env set [test ui]

* Try flipping order [grab scripts on test time] [test all] [test ui]

* Try seeding between unit tests and UI tests [test all] [test ui]

* Fix typo [test ui] [test all]

* Localize apps. Retry 4 times. [test ui] [test all]

* Fix script test. Add explanation to re-seeding. [test all] [test ui]

* Remove assets/sources s3 directory overrides [test all] [test ui]

* Add @no_circle annotation. Run without @no_circle tests when running UI tests via Circle [test ui] [test all]

* Extract circle.rake, test.rake. Move git utilities into GitUtils.
- Determine base branch through a mapping

* Fix spacing , force test run. [test ui] [test all]

* Reflect moved method

* Force test [test all] [test ui]

* Update grunt-sass to potentially fix circleci failure

Potential fix for issue: `Error: The `libsass` binding was not found in [CDO]/apps/node_modules/grunt-sass/node_modules/node-sass/vendor/linux-x64-14/binding.node`

* Bump file with whitespace change to force circle test [test ui] [test all]

* Introduce whitespace change to trigger build [test all] [test ui]

* 16 tests in parallel [test ui]

* Skip tests with circle issues [test ui]

* Try 30 parallel [test ui]

* Try 20 parallel [test ui]

* Try 40 in parallel [test all] [test ui]

* Update to stream output. Try 30 parallel. [test ui] [test all]

* Test adding hipchat logging for failures [test ui] [test all]

* Include format_duration (dupication) temporarily [test ui] [test all]

* Pull out multiple definitions of `format_duration` to `RakeUtils.format_duration` [test all] [test ui]

* Pull *stream_output commands into RakeUtils (aliasing the original "system" command). [test all] [test ui]

* Merge remote-tracking branch 'origin/staging' into sauce-circle-fast [test all] [test ui]

* Try different logging, see if shows up in circleci output [test all] [test ui]

* Properly use original system [test all] [test ui]

* Use correct way of aliasing methods [test all] [test ui]

* Fix streaming command exit status output [test all] [test ui]

* Try magic retry [test all] [test ui]

* Back to retry count (memory usage effect?) [test all] [test ui]

* Try with 35 parallel [test all] [test ui]

* Remove test logging, remove duplicate "Running " from strings [test all] [test ui]

* Move tag definitions out of circle.yml parameters and into circle.rake as constants [test ui] [test all]

* Add explanation of rake utils system command aliasing.

* Remove unused helper file [test all] [test ui]

* Try latest sauce connect [test ui]

* Try latest sauce connect (correct unzip) [test ui]

* Try adding no_circle to free response submittable [test ui]

* Update documentation [test ui] [test all]

* Add ? to fnmatch. [test all]

* Use Kernel.system rather than aliasing. $?.exitstatus -> $?.success? [test all]

* Include apps in code-studio testing dependencies

* Undo unintentional change from merge

* Change diff detection glob behavior. Add git utils test.

* Move glob_matches_file_path? into rake_utils

* Fix reference. Add lib/* to shared test triggers.

* Fix changed PR file detection method.

* Make changed file output a bit easier to scan.

* Try using origin/
@aoby aoby mentioned this pull request Jun 21, 2016
caleybrock added a commit that referenced this pull request Aug 1, 2016
vijayamanohararaj added a commit that referenced this pull request May 8, 2024
vijayamanohararaj added a commit that referenced this pull request May 8, 2024
…Viewing BubbleChoice progress (#58431)

* Fixing flaky UI test Firefox_teacher_tools_level_types_bubble_choice_Viewing BubbleChoice progress

* root cause #2
@snickell snickell mentioned this pull request Aug 10, 2024
22 tasks
fisher-alice added a commit that referenced this pull request Feb 3, 2025
Speculative fix for flaky flappydrag UI test (attempt #2)
bencodeorg added a commit that referenced this pull request Aug 4, 2025
bencodeorg added a commit that referenced this pull request Aug 4, 2025
bencodeorg added a commit that referenced this pull request Aug 4, 2025
* Add submittable to weblab2, simplify logic for showing submittable view

* Approach #1: set hasRun to true in Web Lab 2

* Approach #2: add another explicit prop for skipping run check on submit button

* Revert "Approach #2: add another explicit prop for skipping run check on submit button"

This reverts commit 76d845e.
edcodedotorg pushed a commit that referenced this pull request Apr 14, 2026
Every step of token creation is now logged with the orange circle prefix
so testers can filter the console by 🟠 to see the full Turnstile flow.

Success paths logged:
- getTurnstileToken() entry + total elapsed on delivery
- DevTools probe duration (pass and blocked)
- Script: already cached / first inject / loaded
- getToken() pre-fetch hit vs miss
- Challenge enqueued + starting (gap = wait time behind chain)
- Previous widget removal + container child count before/after
- render() call + widgetId assigned + container child count after
- Token callback: token length + ms since render()
- Pre-fetch resolved + token length

Failure paths logged (all re-thrown except intentional swallows):
- DevTools probe: Worker onerror (swallowed, assume safe)
- DevTools probe: CSP/unsupported blocked (swallowed, assume safe)
- DevTools blocked → TurnstileDevToolsError thrown
- Script load failed (logged, reject propagates)
- getTurnstileToken() catch: error + elapsed before rethrow
- remove() threw: error + rethrow
- Container non-empty after remove(): child count warning
- Container non-empty before render(): widget accumulation warning
- render() threw: error + rethrow (timeout cleared first)
- render() returned falsy widgetId
- TIMEOUT: 30s expired — widgetId + container child count
- remove() in timeout handler threw (logged, not rethrown in cleanup)
- Double-settle #1: token callback after timeout — discarded + len
- Double-settle #2: timeout after callback — no-op
- Pre-fetch failed (swallowed, nextToken cleared — speculative)

Structural fixes:
- Container created in TurnstileManager constructor, appended directly
  to document.body (outside React tree, can never be unmounted)
- Stored as private field — all child-count checks use the same node,
  no re-querying by id on each call

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP
edcodedotorg added a commit that referenced this pull request Apr 24, 2026
…71603)

* Add Cloudflare Turnstile invisible challenge to AI Gateway requests

Dynamically loads the Turnstile script on first use (no HTML changes needed),
fetches an invisible challenge token, and attaches it as X-Turnstile-Token
header on every generateText and transcribe request to the AI Gateway worker.

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Move Turnstile site key to CDO config, expose via script data tag

Adds turnstile_site_key to config.yml.erb (global default, same key for all
environments) and surfaces it to the frontend via a script data attribute in
the application layout, matching the existing pattern used for Statsig keys.
turnstile.ts now reads the key from the DOM rather than hardcoding it.

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Expose Turnstile site key via DCDO frontend config instead of layout tag

Adds turnstile-site-key to DCDO.frontend_config so it is available to
the frontend via DCDO.get() through the existing DCDO script tag, with
no changes to application.html.haml needed.

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Rename DCDO key to ai-gateway-turnstile-site-key for clarity

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Rename CDO config key to ai_gateway_turnstile_site_key

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Fix TS type error in getSiteKey by casting DCDO.get result to string

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Fix TypeScript error: cast DCDO.get result through unknown before string

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Fix Turnstile widget accumulation causing infinite classList error loop

Remove the previous widget before rendering a new one so retries after
worker failures don't leave orphaned widgets that loop internally.

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Refactor TurnstileManager to serialize challenges and add timeout recovery

- Encapsulate all widget state (widgetId, pending callbacks) in a private
  class so it can only be accessed through the serialized promise chain
- Chain ensures only one challenge runs at a time, preventing concurrent
  callers from racing on shared state
- 30s timeout per challenge: cleans up the widget and rejects so the chain
  advances and queued callers are not permanently blocked
- settled flag prevents timeout and token callback from both firing
- DOM health check before reset(); falls back to fresh render if widget
  was detached; clears container.innerHTML before render to evict orphaned content

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Fix TurnstileManager to remove+render instead of reset on each call

Revert to the same widget lifecycle used in the last working implementation:
always remove the existing widget and render a fresh one. The previous reset()
approach and container.innerHTML='' were behavioral changes that did not exist
in the working code and appear to have broken the integration.

Also simplifies the settle pattern by inlining it into the callback — no need
for pendingResolve/pendingReject fields since the only caller of settle is the
inline callback and the timeout.

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Add DevTools debugger detection to fail fast before Turnstile challenge

Turnstile fires a `debugger` call inside an anonymous Web Worker script.
If DevTools is open with breakpoints active on anonymous scripts, that pause
breaks the challenge and the request times out or returns an invalid token.

Probe for this condition before rendering the widget by running a `debugger`
statement inside `new Function(...)` (same anonymous-script context Turnstile
uses) and measuring elapsed time with performance.now(). If elapsed > 100ms
a human resumed the debugger, meaning Turnstile will also be blocked.

On detection, throw TurnstileDevToolsError (exported) so callers can
instanceof-check and surface a targeted message:
  - Deactivate breakpoints (Ctrl/Cmd+F8), or
  - DevTools Settings → Ignore List → enable anonymous scripts

If new Function() is blocked by CSP the probe catches and returns false
(assumes safe), so production environments are unaffected.

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Surface DevTools conflict as chat error with console guidance

When the DevTools debugger probe fires (breakpoints active on anonymous
scripts), instead of silently hanging or timing out:

turnstile.ts:
- Log console.error with a clear explanation of why the challenge fails
- Log a console.group with three fix options:
    1. Close DevTools
    2. (collapsed) Step-by-step guide to ignore anonymous scripts
    3. Deactivate breakpoints with Ctrl/Cmd+F8
- Throw TurnstileDevToolsError (existing export) after logging

submitChatContents.ts:
- Import TurnstileDevToolsError
- Skip metrics logError for TurnstileDevToolsError (expected condition,
  not a server fault) alongside the existing 403 skip
- Add TurnstileDevToolsError branch that shows the targeted chat message:
  "Tutor chat messages cannot be sent due to your browser's dev tools
   being open. Please close dev tools and try again or see message in
   dev tools for other options."
- Update generic error dispatch to pass commonI18n.aiChatResponseError()
  as chatMessageText directly (no longer uses 'error' sentinel)

ChatMessageView.tsx:
- Remove hardcoded commonI18n.aiChatResponseError() override for
  Status.ERROR so the chatMessageText field is used directly, enabling
  per-error custom messages while keeping the same visual error styling

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Fix DevTools probe to use Worker so main thread is never blocked

The previous new Function() approach ran debugger on the main thread,
which meant the user HAD to click Resume in DevTools before we could
measure elapsed time and detect the issue. That's the same bad UX we
were trying to avoid.

New approach: spin up a Blob Worker that runs `debugger; postMessage('ok')`.
- Worker is a sourceless anonymous script → same context as Turnstile's
  own Web Worker, so the Ignore List setting applies identically.
- If DevTools is pausing on anonymous scripts the Worker suspends, the
  message never arrives, and the 100ms timeout fires → we terminate the
  Worker (which clears the DevTools pause automatically) → resolve true.
- If no pause, the Worker posts immediately (microseconds) → resolve false.
- Main thread is never blocked. User never needs to click Resume.
- If Blob/Worker creation fails (CSP) we catch and resolve false (assume safe).

runChallenge() is now async to await the Promise<boolean> probe.

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Fix regression: revert ChatMessageView Status.ERROR override, use Notification for DevTools error

The previous commit changed getChatMessageDisplayText to pass chatMessageText
through for Status.ERROR, but aichatApi.ts dispatches Status.ERROR assistant
messages with chatMessageText: modelResponse (raw server text) that was
intentionally hidden by the i18n override. Exposing modelResponse directly
was a regression.

Changes:
- Revert ChatMessageView.tsx Status.ERROR case back to always returning
  commonI18n.aiChatResponseError(), preserving the existing override behavior
- Revert generic error dispatch chatMessageText back to 'error' sentinel
- Change the TurnstileDevToolsError handler to dispatch a Notification
  (notificationType: 'error') instead of a ChatMessage — consistent with
  how rate limit errors are shown, and avoids needing to change the
  Status.ERROR display logic

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Resolve merge conflict: keep simple activeWidgetId approach + DevTools probe

The TurnstileManager refactor (4b81a97, 2457f73) broke the tool and
was reverted locally. This merge resolution keeps the simple proven
module-level activeWidgetId pattern from before those commits, while
retaining all the DevTools detection work added afterwards:

- debuggerWillPauseInAnonymousScope() Blob Worker probe
- TurnstileDevToolsError (exported for instanceof checks in callers)
- console.error + console.group guidance when DevTools blocks Turnstile
- 30s CHALLENGE_TIMEOUT_MS on the render callback
- No TurnstileManager class, no serialized promise chain

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Fix Turnstile double-render: don't null activeWidgetId in callback

Clearing activeWidgetId in the token callback meant the second call to
getTurnstileToken() skipped remove() and rendered into an occupied
container. Turnstile warned "already been rendered in this container",
the new widget's callback never fired, and the 30s timeout triggered.

Keep activeWidgetId set after the first render so subsequent calls
always remove the previous widget before rendering a fresh one.

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Fix TurnstileDevToolsError detection across webpack chunk boundary

The error was thrown inside the dynamically-imported aichat-client-api
chunk (via getClientApi()), which bundles its own copy of turnstile.ts.
This made instanceof TurnstileDevToolsError fail in the statically-
imported submitChatContents.ts because the two class objects differ
across chunk boundaries.

Switch to checking error.name === 'TurnstileDevToolsError', which
works regardless of which chunk the error originated from.

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Add comments explaining error.name check over instanceof

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Improve DevTools Ignore List instructions in Turnstile guidance

- F1 is the most reliable way to open Settings (gear icon location varies)
- Clarify Ignore List is in the left sidebar and may need scrolling
- Note that older Chrome called it "Blackboxing"
- Clarify how to close Settings (Escape/✕)

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Warn about Console settings gear vs main DevTools Settings gear

Users can confuse the Console panel's settings gear with the main
DevTools Settings gear. Step 1 now explicitly calls this out and
recommends F1 to avoid any ambiguity.

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Remove misleading Ignore List guidance from Turnstile DevTools message

"Anonymous scripts from eval or console" only covers eval() and
console-typed scripts, not Blob Worker scripts. Both our probe and
Cloudflare Turnstile run inside Blob Workers, so that setting has
no effect. Removed Option 2 (Ignore List) and simplified to the two
options that actually work: close DevTools or press Ctrl+F8/Cmd+F8.

Also added an explicit NOTE explaining why the Ignore List doesn't
apply, so developers don't waste time trying it.

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Fix DevTools guidance: clarify breakpoint deactivation and Ignore List limits

- Recommend clicking the Sources panel button directly (more reliable
  than keyboard shortcut, especially on Mac where F8 = media key)
- Clarify Mac keyboard shortcut requires Fn key: Fn+Cmd+F8
- Update Ignore List NOTE: it doesn't apply to Worker contexts at all,
  not just "Anonymous scripts" — no pattern (including blob:) can
  suppress debugger statements inside Web Workers

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Pre-fetch next Turnstile token after each use to reduce latency/500s

A single chat message makes 3+ calls to getTurnstileToken (safety check
on user input, main generation, safety check on model output). Previously
each call removed the old widget and rendered a brand-new challenge from
scratch, so calls 2 and 3 always had to wait for a fresh challenge and
had a window for token expiry between the render and the request.

Now after consuming a token we immediately kick off renderFreshToken()
in the background. The next call grabs the already-in-flight promise
(or a fresh one if none is pending) and clears it before awaiting so
concurrent callers can't accidentally share the same one-time-use token.

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Bring back TurnstileManager class with serialization + settled flag

Recovers the good structural ideas from commits 4b81a97/2457f7328
(which were reverted due to buggy reset()/innerHTML='' calls) and
merges them with the current working remove()+render() approach and
the pre-fetch optimization added in a96a492.

What's restored:
- TurnstileManager class encapsulating all widget state (widgetId,
  chain, nextToken) so nothing leaks into module scope
- Promise chain serialization: only one widget renders at a time;
  chain always advances on rejection so queued callers never block
- settled flag in runChallenge() prevents timeout and token callback
  from both firing if they race at the 30s boundary

What's kept from current working code:
- remove() + render() for each challenge (NOT reset())
- Pre-fetch: after each token is delivered, schedulePrefetch() enqueues
  the next challenge so subsequent calls within one chat message
  (safety check → main generation → output safety check) get a token
  that is already completing rather than waiting on a fresh challenge

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Add verbose 🟠 [Cloudflare Turnstile] logging for human testing

Every step of token creation is now logged with the orange circle prefix
so testers can filter the console by 🟠 to see the full Turnstile flow.

Success paths logged:
- getTurnstileToken() entry + total elapsed on delivery
- DevTools probe duration (pass and blocked)
- Script: already cached / first inject / loaded
- getToken() pre-fetch hit vs miss
- Challenge enqueued + starting (gap = wait time behind chain)
- Previous widget removal + container child count before/after
- render() call + widgetId assigned + container child count after
- Token callback: token length + ms since render()
- Pre-fetch resolved + token length

Failure paths logged (all re-thrown except intentional swallows):
- DevTools probe: Worker onerror (swallowed, assume safe)
- DevTools probe: CSP/unsupported blocked (swallowed, assume safe)
- DevTools blocked → TurnstileDevToolsError thrown
- Script load failed (logged, reject propagates)
- getTurnstileToken() catch: error + elapsed before rethrow
- remove() threw: error + rethrow
- Container non-empty after remove(): child count warning
- Container non-empty before render(): widget accumulation warning
- render() threw: error + rethrow (timeout cleared first)
- render() returned falsy widgetId
- TIMEOUT: 30s expired — widgetId + container child count
- remove() in timeout handler threw (logged, not rethrown in cleanup)
- Double-settle #1: token callback after timeout — discarded + len
- Double-settle #2: timeout after callback — no-op
- Pre-fetch failed (swallowed, nextToken cleared — speculative)

Structural fixes:
- Container created in TurnstileManager constructor, appended directly
  to document.body (outside React tree, can never be unmounted)
- Stored as private field — all child-count checks use the same node,
  no re-querying by id on each call

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* fix merge

* Remove DCDO/yml sitekey plumbing; hardcode turnstile SITE_KEY in TS

Public Cloudflare Turnstile sitekey doesn't need per-environment
config or frontend_config bridging — just a TS constant in turnstile.ts.

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Rename SITE_KEY to TURNSTILE_SITE_KEY

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Refactor TurnstileManager to lazy singleton; gate on useTurnstile experiment

- Private constructor + static getInstance() — nothing runs at import time
- getTurnstileToken() moves from standalone export to instance method
- Call sites check experiments.isEnabledAllowingQueryString('useTurnstile')
  before calling getInstance(); experiment off → no instance, no DOM div,
  no script, no widget, no header
- Enable via ?enableExperiments=useTurnstile (persists to localStorage)

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* lint fixes

* update imports (that should have worked as is)

* update turnstile message

* make turnstile option console group collapsed by default

* nextToken => nextTokenPromise

* Move turnstile.ts into turnstile/ directory, split into focused modules

- constants.ts: all top-level consts
- types.ts: TurnstileDevToolsError + Window declaration
- debuggerProbe.ts: debuggerWillPauseInAnonymousScope
- loadScript.ts: loadTurnstileScript + scriptLoadPromise
- manager.ts: TurnstileManager singleton class
- util.ts: fetchTurnstileTokenIfEnabled + turnstileHeaders (eliminates
  duplication between generateText.ts and transcribe.ts)
- index.ts: public re-exports only

generateText.ts and transcribe.ts simplified to single-line calls.

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Add unit tests for turnstile util, experiment gate, and singleton invariants

Covers:
- turnstileHeaders: correct header when token present, empty when null
- fetchTurnstileTokenIfEnabled: getInstance not called when experiment off,
  called and token returned when experiment on
- TurnstileManager.getInstance: same instance on repeated calls, container
  div appended to body on first call, no duplicate containers on repeat calls

Widget lifecycle (serialization, pre-fetch, settled-flag race) deferred —
requires mocking window.turnstile callbacks at controlled timing, out of
scope while feature is experimental and human-tested via 🟠 logging.

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* Extract isTurnstileDevToolsError predicate into turnstile/util

Centralizes the error.name string comparison and the explanation of why
instanceof cannot be used across webpack chunk boundaries. submitChatContents
now imports the predicate rather than repeating the raw check with comments.

https://claude.ai/code/session_011kxb8TDhtsuXEetzZomJQP

* lint fixes

---------

Co-authored-by: Claude <noreply@anthropic.com>
stephenliang pushed a commit that referenced this pull request May 7, 2026
Add a p5 playground into our ml playground
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants