Skip to content

Add notecard file import; fix CRLF/BOM in script file import - #355

Closed
Shadowolf7 wants to merge 8 commits into
AlchemyViewer:developfrom
Shadowolf7:worktree-notecard-script-editor-features
Closed

Add notecard file import; fix CRLF/BOM in script file import#355
Shadowolf7 wants to merge 8 commits into
AlchemyViewer:developfrom
Shadowolf7:worktree-notecard-script-editor-features

Conversation

@Shadowolf7

Copy link
Copy Markdown

Summary

  • Adds a "Load..." button to the notecard preview floater (LLPreviewNotecard) so notecard content can be loaded from a file on disk, not just pasted. No equivalent existed before. Uses the same async LLFilePickerReplyThread pattern already used elsewhere in the tree, feeding into the existing loadNotecardText() path (plain setText, not importBuffer -- that's reserved for the Linden text version container format used by embedded-item round trips).
  • Scripts already had a working "Load from file..." feature wired via the panel_script_ed.xml File menu (LLScriptEdCore::loadScriptFromFile) -- discovered while scoping this, no new UI needed there.
  • Hardens both loadNotecardText() (notecard load button + external-editor sync path) and loadScriptFromFile() (script) against files that aren't our own tmp-file round trips: strips a UTF-8 BOM and normalizes CR/CRLF line endings. The script path previously reconstructed the buffer with getline(), which left stray \r characters on CRLF (Windows-authored) files; it now reads in binary mode and normalizes explicitly.

Notes

  • Verified LLEmbeddedItems::removeUnusedChars() scans the live text and self-prunes stale embedded-item references at save time (llviewertexteditor.cpp:472), so replacing a notecard's text via the new load button cannot leave dangling embedded-item references on the next save.
  • Known pre-existing gap, intentionally left untouched to keep this change scoped: loadScriptFromFile/saveScriptToFile pass a raw LLScriptEdCore* across the async file-picker callback rather than a safe LLHandle, so closing the script floater while the OS file dialog is still open could dangle.
  • Both changed .cpp files were verified with -fsyntax-only against the production compiler flags and precompiled header (via the existing build-Linux-ninja-os compile database, source swapped to the worktree copy) -- this was not linked or run. The new "Load..." button's XUI layout (left_pad="4", width 66, landing at 108-174px against the "Save" button at 178px) is arithmetic-only and has not been visually verified in a running viewer.

Test plan

  • Build the viewer and open a notecard; confirm "Load..." button appears and is laid out correctly next to Edit/Save/Delete
  • Load a plain-text file into a notecard, confirm content replaces editor text and Save enables/works
  • Load a notecard containing embedded inventory, then use "Load..." to replace its text, save, and confirm no stale embedded items remain
  • Load a Windows-authored (CRLF) script/notecard file via each path and confirm no stray \r artifacts appear in the loaded text
  • Load a file with a UTF-8 BOM and confirm no BOM marker appears in the loaded text

🤖 Generated with Claude Code

Roger Luedecke and others added 8 commits July 30, 2026 23:31
Renames the user-facing/OS-facing product identity across build
config, macOS bundle IDs (viewer + CEF plugin), Windows resource
metadata, in-app strings, installer/Linux packaging scripts, and
docs. Adds placeholder icon/logo art under indra/newview/branding/
(replacing the external alchemy-branding vcpkg dependency for now)
and doc/vayu_logo.png.

Internal code identifiers (settings keys, shader dir names, vcpkg
triplets/ports, the "alchemy" skin theme) and .github/ CI workflows
are intentionally left untouched — see the plan notes for why.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- gGLManager.mHasBPTCTextureCompression detects GL 4.2 core / GL_ARB_texture_compression_bptc
- setManualImage's runtime compression path now prefers explicit BC7 over
  generic driver-chosen formats when available, sharing one lookup table
  (compressed_internal_format) so the swap and its detection can't drift apart
- Fixes an isCompressed() regression: widening it to also detect the
  generic-compression-on-upload case broke setImage()'s data_hasmips
  branch, which relies on isCompressed() to tell raw pixels from
  pre-compressed blocks. Split into isCompressed() (source-precompressed,
  e.g. DXT containers) and willCompressOnUpload() (will be compressed by
  setManualImage), each used only where they're actually the right question.
- setSubImage()/setSubImageFromFrameBuffer() now refuse partial updates on
  a compressed-or-will-compress texture instead of issuing a GL call
  (glTexSubImage2D/glCopyTexSubImage2D) that's illegal on compressed
  internal formats -- previously silent corruption, now a clear LL_ERRS
- dataFormatBits/dataFormatBytes/dataFormatVRAMBytes/dataFormatComponents
  didn't know the BC7 enums, so any BC7-compressed texture's memory
  accounting hit LL_ERRS("Unknown format") the moment it was allocated

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TyvmxbNsTvqm79FvJRUP3J
FMOD Studio on Linux:
- FMODSTUDIO.cmake looked for libs under api/core/lib/x64, but the Linux
  SDK ships them under x86_64 -- find_library never matched
- lllistener_fmodstudio.cpp/llaudioengine_fmodstudio.cpp reinterpret-cast
  LLVector3's float array to FMOD_VECTOR* and dereferenced it, which is
  strict-aliasing UB and a hard error under -Werror=strict-aliasing on
  current GCC; build by value instead (to_fmod_vector helper)
- viewer_manifest.py never bundled libfmod.so* into the package (FMOD
  isn't a vcpkg dependency, so nothing copied it) -- binary linked against
  it but the .so was never shipped, so the packaged viewer couldn't start.
  New --fmod= manifest arg / FMOD_MANIFEST_LIB_DIR CMake var fix this.

Packaging: the "Viewer Manifest Copy" post-build step passed
--actions=copy alone. is_packaging_viewer() (gates whether shaders, fonts,
skins, app_settings XML etc. are even considered) requires "package" in
the actions list, but only copy_action() (needs "copy") actually writes
files -- package_action() alone is a no-op. So this step silently produced
a package missing nearly everything needed to run, without failing the
build. Now passes "copy package" together, matching the LOCAL_DIST_DIR
block's existing pattern. strip_binaries() (package_finish) is a no-op
for non-Release buildtypes, so normal dev builds keep their debug info.

Launcher: refresh_desktop_app_entry.sh only reinstalled the .desktop
entry when its content actually changed (previously unconditional on
every launch), added PrefersNonDefaultGPU=true, and exposes a "Launch
with Zink (OpenGL-over-Vulkan)" right-click action via a new vayu-zink
wrapper (gamemoderun + switcherooctl launch, degrading gracefully if
either tool is absent). wrapper.sh carries over AMD performance tuning
(AMD_DEBUG=lowprecision, mimalloc) from the Firestorm high-performance
launcher.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TyvmxbNsTvqm79FvJRUP3J
Auto-detects ccache and sets it as the C/C++ compiler launcher before
project()/enable_language() run (where CMake starts honoring
CMAKE_<LANG>_COMPILER_LAUNCHER). Silently a no-op if ccache isn't
installed, so this is safe to leave on by default. Speeds up rebuilds
after a git pull/rebase that touch many files but leave most preprocessed
output unchanged -- Ninja alone recompiles anything with a newer mtime
even if the content is identical to something already compiled.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TyvmxbNsTvqm79FvJRUP3J
LLViewerDynamicTexture (snapshot previews, floater thumbnails, etc.) and
the terrain paintmap baking texture both refresh via
setSubImageFromFrameBuffer(), which LLImageGL now refuses on a
compressed-or-will-compress texture (partial/copy updates are illegal on
compressed internal formats). Neither disabled compression on its
texture, so enabling RenderCompressTextures hit that guard immediately.
Fix mirrors the existing setAllowCompression(false) pattern already used
for media textures and UI images.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TyvmxbNsTvqm79FvJRUP3J
Completes the drop of the vendored, patched curl 7.54.1 (which existed
solely for an old HTTP/1.1 pipelining fix that no longer applies --
modern curl dropped real HTTP/1.1 pipelining years ago, making
CURLOPT_PIPELINING a no-op regardless) in favor of stock vcpkg curl.

That drop exposed a real, previously-masked bug: Linden Lab's asset
CDN sends "Content-Encoding: binary/octet-stream" on essentially every
asset response (a MIME type, not a transfer coding -- almost certainly
swapped with Content-Type in the S3 object metadata upstream). Old
curl silently tolerated an unrecognized Content-Encoding value; curl
8.21.0 treats it as a hard failure (CURLE_BAD_CONTENT_ENCODING /
"Easy_61"), which was surfacing as near-100% mesh header and texture
fetch failures -- explaining why meshes and sculpts stopped loading,
especially right after a teleport into a busy region.

Add a per-policy-class PO_CONTENT_DECODING option to llcorehttp and
disable it for the binary asset-fetch classes (texture, mesh1, mesh2,
large mesh) that talk to the CDN, while leaving it enabled for
capability calls (inventory, agent, etc.) that legitimately use gzip.
Both CURLOPT_ENCODING and CURLOPT_HTTP_CONTENT_DECODING are set
explicitly on every request rather than left unset when disabled,
since libcurl easy handles are recycled from an unreset pool
(HttpLibcurl::HandleCache) and would otherwise carry a prior request's
decoding state forward.

Also restore the QAModeHttpTrace debug setting (referenced in
_httpoprequest.cpp but missing from settings.xml, so libcurl verbose
tracing had no way to be enabled) -- it's what surfaced the bogus
response header in the first place.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J7R2Za4br1N947HEDgf8zr
LLMeshRepoThread::run()'s header-fetch loop silently dropped a request
on final retry exhaustion (just a debug log), unlike the LOD and skin
loops right next to it, which push into mUnavailableQ on the identical
exhaustion condition so waiting objects get unstuck. Any LLVOVolume
waiting on that mesh's header never received notifyMeshLoaded or a
forced LOD fallback, leaving it at its placeholder shape permanently
with no further retry.

fetchMeshHeader() returns false when the local HTTP layer refuses to
even enqueue the request (congestion), which is rare in ordinary play
but close to guaranteed right after a teleport into a busy region, when
a whole region's object list arrives at once and hits the fixed
concurrent-request cap -- matching the reported symptom of mesh
objects never rezzing after teleporting into crowded regions.

Mirrors LLMeshHeaderHandler::processFailure(): push all LOD levels for
the mesh onto mUnavailableQ so dependent objects fall back instead of
waiting forever.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J7R2Za4br1N947HEDgf8zr
Notecards had no way to load content from disk (only copy/paste or the
external-editor round trip); scripts already had one via the File menu's
"Load from file..." item. Add a "Load..." button to the notecard preview
floater using the same async LLFilePickerReplyThread pattern, feeding
into the existing loadNotecardText() path (plain setText, not
importBuffer -- that's reserved for the "Linden text version" container
format used for embedded-item round trips).

Both loadNotecardText() (notecard, also used by the external-editor sync
path) and LLScriptEdCore::loadScriptFromFile() (script) now strip a UTF-8
BOM and normalize CR/CRLF line endings, since files picked off disk may
not be ASCII/LF like our own tmp-file round trips. The script path
previously reconstructed the buffer with getline(), which left stray \r
characters on Windows-authored files; it now reads the file in binary
mode and normalizes explicitly.

Verified LLEmbeddedItems::removeUnusedChars() scans live text and
self-prunes stale embedded-item references at save time, so replacing a
notecard's text via the new load button cannot leave dangling embedded
items.

Known pre-existing gap, not touched here: loadScriptFromFile/
saveScriptToFile pass a raw LLScriptEdCore* across the async file-picker
callback rather than a safe LLHandle, so closing the floater while the
OS file dialog is open could dangle. Left as-is to keep this change
scoped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 60a288b7-9504-4ce3-b097-61ceb36a109a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Shadowolf7

Copy link
Copy Markdown
Author

Opened by mistake against the wrong repository (should have targeted my own fork, not upstream). Closing; the correct PR will be opened against Shadowolf7/Vayu-Viewer instead. Apologies for the noise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant