Showing preview only (7,783K chars total). Download the full file or copy to clipboard to get everything.
Repository: glowbom/glowby
Branch: main
Commit: 84215fc03b8b
Files: 339
Total size: 7.4 MB
Directory structure:
gitextract_gwqak2n4/
├── .claude/
│ └── settings.json
├── .github/
│ └── workflows/
│ └── glowby-release.yml
├── .gitignore
├── AGENTS.md
├── CLI.md
├── LICENSE
├── README.md
├── backend/
│ ├── backend_info_page.go
│ ├── claude_anthropic.go
│ ├── codex_chatgpt.go
│ ├── elevenlabs_audio.go
│ ├── fireworks_fireworks.go
│ ├── gemini_google.go
│ ├── gemini_image.go
│ ├── gemini_text_with_attachment.go
│ ├── gemini_veo.go
│ ├── gemini_video.go
│ ├── go.mod
│ ├── go.sum
│ ├── gpt5_openai.go
│ ├── gpt5_responses_api.go
│ ├── grok_image.go
│ ├── grok_video.go
│ ├── grok_xai.go
│ ├── handlers.go
│ ├── main.go
│ ├── model_helpers.go
│ ├── o3_openai.go
│ ├── openai_image.go
│ ├── opencode_driver.go
│ ├── opencode_folder_picker.go
│ ├── opencode_media_postpass.go
│ ├── opencode_project_history.go
│ ├── opencode_project_open.go
│ ├── opencode_zen.go
│ ├── openrouter_openrouter.go
│ ├── prompts.go
│ ├── r1_groq.go
│ ├── r1_ollama.go
│ ├── search.go
│ ├── security.go
│ ├── stack_specs.go
│ └── types.go
├── cli/
│ ├── code.go
│ ├── doctor.go
│ ├── go.mod
│ ├── main.go
│ └── version.go
├── docs/
│ ├── .dockerignore
│ ├── .gitignore
│ ├── Dockerfile
│ ├── README.md
│ ├── app/
│ │ ├── app.css
│ │ ├── components/
│ │ │ ├── brand-title.tsx
│ │ │ ├── docs-route-view.tsx
│ │ │ ├── docs-search.tsx
│ │ │ └── nav-actions.tsx
│ │ ├── lib/
│ │ │ ├── docs.ts
│ │ │ ├── meta.ts
│ │ │ ├── site.ts
│ │ │ ├── source.browser.tsx
│ │ │ └── source.server.ts
│ │ ├── root.tsx
│ │ ├── routes/
│ │ │ ├── docs-page.tsx
│ │ │ └── home.tsx
│ │ └── routes.ts
│ ├── content/
│ │ └── docs/
│ │ ├── desktop.mdx
│ │ ├── glowbom.mdx
│ │ ├── glowby-oss.mdx
│ │ ├── index.mdx
│ │ ├── meta.json
│ │ └── quickstart.mdx
│ ├── package.json
│ ├── react-router.config.ts
│ ├── server.log
│ ├── source.config.ts
│ ├── test-browser.js
│ ├── test-search.js
│ ├── test.ts
│ ├── tsconfig.json
│ └── vite.config.ts
├── legacy/
│ ├── GlowbyGenius.md
│ ├── README.md
│ ├── app/
│ │ ├── .gitignore
│ │ ├── .metadata
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── analysis_options.yaml
│ │ ├── android/
│ │ │ ├── .gitignore
│ │ │ ├── app/
│ │ │ │ ├── build.gradle
│ │ │ │ └── src/
│ │ │ │ ├── debug/
│ │ │ │ │ └── AndroidManifest.xml
│ │ │ │ ├── main/
│ │ │ │ │ ├── AndroidManifest.xml
│ │ │ │ │ ├── kotlin/
│ │ │ │ │ │ └── com/
│ │ │ │ │ │ └── example/
│ │ │ │ │ │ └── pwa/
│ │ │ │ │ │ └── MainActivity.kt
│ │ │ │ │ └── res/
│ │ │ │ │ ├── drawable/
│ │ │ │ │ │ └── launch_background.xml
│ │ │ │ │ └── values/
│ │ │ │ │ └── styles.xml
│ │ │ │ └── profile/
│ │ │ │ └── AndroidManifest.xml
│ │ │ ├── build.gradle
│ │ │ ├── gradle/
│ │ │ │ └── wrapper/
│ │ │ │ └── gradle-wrapper.properties
│ │ │ ├── gradle.properties
│ │ │ └── settings.gradle
│ │ ├── assets/
│ │ │ ├── talk.glowbom
│ │ │ ├── test.html
│ │ │ └── website.html
│ │ ├── ios/
│ │ │ ├── .gitignore
│ │ │ ├── Flutter/
│ │ │ │ ├── .last_build_id
│ │ │ │ ├── AppFrameworkInfo.plist
│ │ │ │ ├── Debug.xcconfig
│ │ │ │ └── Release.xcconfig
│ │ │ ├── Podfile
│ │ │ ├── Runner/
│ │ │ │ ├── AppDelegate.swift
│ │ │ │ ├── Assets.xcassets/
│ │ │ │ │ ├── AppIcon.appiconset/
│ │ │ │ │ │ └── Contents.json
│ │ │ │ │ └── LaunchImage.imageset/
│ │ │ │ │ ├── Contents.json
│ │ │ │ │ └── README.md
│ │ │ │ ├── Base.lproj/
│ │ │ │ │ ├── LaunchScreen.storyboard
│ │ │ │ │ └── Main.storyboard
│ │ │ │ ├── Glowby.swift
│ │ │ │ ├── Info.plist
│ │ │ │ └── Runner-Bridging-Header.h
│ │ │ ├── Runner.xcodeproj/
│ │ │ │ ├── project.pbxproj
│ │ │ │ ├── project.xcworkspace/
│ │ │ │ │ ├── contents.xcworkspacedata
│ │ │ │ │ └── xcshareddata/
│ │ │ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ │ │ └── WorkspaceSettings.xcsettings
│ │ │ │ └── xcshareddata/
│ │ │ │ └── xcschemes/
│ │ │ │ └── Runner.xcscheme
│ │ │ └── Runner.xcworkspace/
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata/
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ │ ├── lib/
│ │ │ ├── main.dart
│ │ │ ├── models/
│ │ │ │ └── ai.dart
│ │ │ ├── services/
│ │ │ │ ├── hugging_face_api.dart
│ │ │ │ ├── openai_api.dart
│ │ │ │ └── pulze_ai_api.dart
│ │ │ ├── utils/
│ │ │ │ ├── color_utils.dart
│ │ │ │ ├── text_to_speech.dart
│ │ │ │ ├── timestamp.dart
│ │ │ │ ├── utils.dart
│ │ │ │ ├── utils_desktop.dart
│ │ │ │ ├── utils_stub.dart
│ │ │ │ └── utils_web.dart
│ │ │ └── views/
│ │ │ ├── dialogs/
│ │ │ │ ├── ai_error_dialog.dart
│ │ │ │ ├── ai_settings_dialog.dart
│ │ │ │ └── api_key_dialog.dart
│ │ │ ├── html/
│ │ │ │ ├── html_view_screen.dart
│ │ │ │ ├── html_view_screen_desktop.dart
│ │ │ │ ├── html_view_screen_interface.dart
│ │ │ │ ├── html_view_screen_mobile.dart
│ │ │ │ ├── html_view_screen_stub.dart
│ │ │ │ └── html_view_screen_web.dart
│ │ │ ├── screens/
│ │ │ │ ├── chat_screen.dart
│ │ │ │ ├── global_settings.dart
│ │ │ │ ├── magical_loading_view.dart
│ │ │ │ └── talk_screen.dart
│ │ │ └── widgets/
│ │ │ ├── message.dart
│ │ │ ├── message_bubble.dart
│ │ │ ├── messages.dart
│ │ │ ├── new_message.dart
│ │ │ ├── paint_window.dart
│ │ │ └── tasks_view.dart
│ │ ├── linux/
│ │ │ ├── .gitignore
│ │ │ ├── CMakeLists.txt
│ │ │ ├── flutter/
│ │ │ │ ├── CMakeLists.txt
│ │ │ │ ├── generated_plugin_registrant.cc
│ │ │ │ ├── generated_plugin_registrant.h
│ │ │ │ └── generated_plugins.cmake
│ │ │ ├── main.cc
│ │ │ ├── my_application.cc
│ │ │ └── my_application.h
│ │ ├── macos/
│ │ │ ├── .gitignore
│ │ │ ├── Flutter/
│ │ │ │ ├── Flutter-Debug.xcconfig
│ │ │ │ ├── Flutter-Release.xcconfig
│ │ │ │ └── GeneratedPluginRegistrant.swift
│ │ │ ├── Podfile
│ │ │ ├── Runner/
│ │ │ │ ├── AppDelegate.swift
│ │ │ │ ├── Assets.xcassets/
│ │ │ │ │ └── AppIcon.appiconset/
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Base.lproj/
│ │ │ │ │ └── MainMenu.xib
│ │ │ │ ├── Configs/
│ │ │ │ │ ├── AppInfo.xcconfig
│ │ │ │ │ ├── Debug.xcconfig
│ │ │ │ │ ├── Release.xcconfig
│ │ │ │ │ └── Warnings.xcconfig
│ │ │ │ ├── DebugProfile.entitlements
│ │ │ │ ├── Info.plist
│ │ │ │ ├── MainFlutterWindow.swift
│ │ │ │ └── Release.entitlements
│ │ │ ├── Runner.xcodeproj/
│ │ │ │ ├── project.pbxproj
│ │ │ │ ├── project.xcworkspace/
│ │ │ │ │ └── xcshareddata/
│ │ │ │ │ └── IDEWorkspaceChecks.plist
│ │ │ │ └── xcshareddata/
│ │ │ │ └── xcschemes/
│ │ │ │ └── Runner.xcscheme
│ │ │ ├── Runner.xcworkspace/
│ │ │ │ ├── contents.xcworkspacedata
│ │ │ │ └── xcshareddata/
│ │ │ │ └── IDEWorkspaceChecks.plist
│ │ │ └── RunnerTests/
│ │ │ └── RunnerTests.swift
│ │ ├── pubspec.yaml
│ │ ├── web/
│ │ │ ├── index.html
│ │ │ ├── manifest.json
│ │ │ └── tv.js
│ │ └── windows/
│ │ ├── .gitignore
│ │ ├── CMakeLists.txt
│ │ ├── flutter/
│ │ │ ├── CMakeLists.txt
│ │ │ ├── generated_plugin_registrant.cc
│ │ │ ├── generated_plugin_registrant.h
│ │ │ └── generated_plugins.cmake
│ │ └── runner/
│ │ ├── CMakeLists.txt
│ │ ├── Runner.rc
│ │ ├── flutter_window.cpp
│ │ ├── flutter_window.h
│ │ ├── main.cpp
│ │ ├── resource.h
│ │ ├── runner.exe.manifest
│ │ ├── utils.cpp
│ │ ├── utils.h
│ │ ├── win32_window.cpp
│ │ └── win32_window.h
│ ├── backend/
│ │ └── aws/
│ │ └── fetchImageAsBase64/
│ │ └── index.mjs
│ ├── dist/
│ │ ├── assets/
│ │ │ ├── AssetManifest.bin.json
│ │ │ ├── AssetManifest.json
│ │ │ ├── FontManifest.json
│ │ │ ├── NOTICES
│ │ │ ├── assets/
│ │ │ │ └── talk.glowbom
│ │ │ ├── fonts/
│ │ │ │ └── MaterialIcons-Regular.otf
│ │ │ └── shaders/
│ │ │ └── ink_sparkle.frag
│ │ ├── canvaskit/
│ │ │ ├── canvaskit.js
│ │ │ ├── canvaskit.js.symbols
│ │ │ ├── canvaskit.wasm
│ │ │ ├── chromium/
│ │ │ │ ├── canvaskit.js
│ │ │ │ ├── canvaskit.js.symbols
│ │ │ │ └── canvaskit.wasm
│ │ │ ├── skwasm.js
│ │ │ ├── skwasm.js.symbols
│ │ │ ├── skwasm.wasm
│ │ │ └── skwasm.worker.js
│ │ ├── flutter.js
│ │ ├── flutter_bootstrap.js
│ │ ├── flutter_service_worker.js
│ │ ├── index.html
│ │ ├── manifest.json
│ │ ├── tv.js
│ │ └── version.json
│ └── docs/
│ └── README.md
├── project/
│ ├── AGENTS.md
│ ├── android/
│ │ ├── .gitignore
│ │ ├── .idea/
│ │ │ ├── .gitignore
│ │ │ ├── .name
│ │ │ ├── compiler.xml
│ │ │ ├── deploymentTargetDropDown.xml
│ │ │ ├── gradle.xml
│ │ │ ├── inspectionProfiles/
│ │ │ │ └── Project_Default.xml
│ │ │ ├── kotlinc.xml
│ │ │ ├── migrations.xml
│ │ │ ├── misc.xml
│ │ │ └── vcs.xml
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── .gitignore
│ │ │ ├── build.gradle.kts
│ │ │ ├── proguard-rules.pro
│ │ │ └── src/
│ │ │ └── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── assets/
│ │ │ │ └── custom.glowbom
│ │ │ ├── java/
│ │ │ │ └── com/
│ │ │ │ └── glowbom/
│ │ │ │ └── custom/
│ │ │ │ ├── AiExtensions.kt
│ │ │ │ ├── MainActivity.kt
│ │ │ │ └── ui/
│ │ │ │ └── theme/
│ │ │ │ ├── Color.kt
│ │ │ │ ├── Theme.kt
│ │ │ │ └── Type.kt
│ │ │ └── res/
│ │ │ ├── drawable/
│ │ │ │ └── ic_launcher_background.xml
│ │ │ ├── drawable-v24/
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── mipmap-anydpi-v26/
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── values/
│ │ │ │ ├── colors.xml
│ │ │ │ ├── strings.xml
│ │ │ │ └── themes.xml
│ │ │ └── xml/
│ │ │ ├── backup_rules.xml
│ │ │ └── data_extraction_rules.xml
│ │ ├── build.gradle.kts
│ │ ├── gradle/
│ │ │ └── wrapper/
│ │ │ └── gradle-wrapper.properties
│ │ ├── gradle.properties
│ │ ├── gradlew
│ │ ├── gradlew.bat
│ │ └── settings.gradle.kts
│ ├── apple/
│ │ ├── Custom/
│ │ │ ├── AiExtensions.swift
│ │ │ ├── Assets.xcassets/
│ │ │ │ ├── AccentColor.colorset/
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── AppIcon.appiconset/
│ │ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ │ ├── ContentView.swift
│ │ │ ├── Custom.entitlements
│ │ │ ├── CustomApp.swift
│ │ │ ├── Preview Content/
│ │ │ │ └── Preview Assets.xcassets/
│ │ │ │ └── Contents.json
│ │ │ └── custom.glowbom
│ │ ├── Custom.xcodeproj/
│ │ │ ├── project.pbxproj
│ │ │ ├── project.xcworkspace/
│ │ │ │ ├── contents.xcworkspacedata
│ │ │ │ ├── xcshareddata/
│ │ │ │ │ └── IDEWorkspaceChecks.plist
│ │ │ │ └── xcuserdata/
│ │ │ │ └── jacobilin.xcuserdatad/
│ │ │ │ └── UserInterfaceState.xcuserstate
│ │ │ └── xcuserdata/
│ │ │ └── jacobilin.xcuserdatad/
│ │ │ └── xcschemes/
│ │ │ └── xcschememanagement.plist
│ │ └── README.md
│ ├── glowbom.json
│ ├── prototype/
│ │ ├── assets.json
│ │ └── index.html
│ └── web/
│ ├── .gitignore
│ ├── README.md
│ ├── components.json
│ ├── eslint.config.mjs
│ ├── next.config.mjs
│ ├── package.json
│ ├── postcss.config.js
│ ├── public/
│ │ └── custom.glowbom
│ ├── src/
│ │ ├── app/
│ │ │ ├── components/
│ │ │ │ ├── AiExtensions.tsx
│ │ │ │ └── Custom.tsx
│ │ │ ├── globals.css
│ │ │ ├── layout.tsx
│ │ │ └── page.tsx
│ │ ├── components/
│ │ │ └── ui/
│ │ │ ├── button.tsx
│ │ │ ├── card.tsx
│ │ │ ├── dialog.tsx
│ │ │ ├── dropdown-menu.tsx
│ │ │ ├── form.tsx
│ │ │ ├── input.tsx
│ │ │ ├── label.tsx
│ │ │ ├── pagination.tsx
│ │ │ ├── progress.tsx
│ │ │ ├── tabs.tsx
│ │ │ ├── toast.tsx
│ │ │ ├── toaster.tsx
│ │ │ ├── toggle.tsx
│ │ │ ├── tooltip.tsx
│ │ │ └── use-toast.ts
│ │ ├── lib/
│ │ │ └── utils.ts
│ │ └── models/
│ │ └── types.ts
│ ├── tailwind.config.ts
│ └── tsconfig.json
├── scripts/
│ └── install.sh
└── web/
├── LICENSE
├── index.html
├── package.json
├── src/
│ ├── App.tsx
│ ├── hooks/
│ │ └── useRefineRun.ts
│ ├── lib/
│ │ ├── api.ts
│ │ ├── console-render.ts
│ │ ├── model-catalog.ts
│ │ ├── server-auth.ts
│ │ └── sse.ts
│ ├── main.tsx
│ ├── styles.css
│ └── types/
│ └── opencode.ts
├── tsconfig.json
└── vite.config.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .claude/settings.json
================================================
{
"permissions": {
"allow": [
"Bash(go build:*)"
]
}
}
================================================
FILE: .github/workflows/glowby-release.yml
================================================
name: Release glowby
on:
push:
tags:
- 'v*'
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
goos: [darwin, linux, windows]
goarch: [amd64, arm64]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.24'
- name: Build binary
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
CGO_ENABLED: '0'
run: |
if [ -d oss/cli ]; then
CLI_DIR="oss/cli"
else
CLI_DIR="cli"
fi
TAG="${GITHUB_REF#refs/tags/}"
SHA="$(git rev-parse --short HEAD)"
DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
EXT=""
if [ "${{ matrix.goos }}" = "windows" ]; then EXT=".exe"; fi
cd "${CLI_DIR}"
go build \
-ldflags "-s -w -X main.version=${TAG} -X main.commit=${SHA} -X main.date=${DATE}" \
-o "glowby-${{ matrix.goos }}-${{ matrix.goarch }}${EXT}" \
.
- name: Create archive
run: |
if [ -d oss/cli ]; then
CLI_DIR="oss/cli"
else
CLI_DIR="cli"
fi
cd "${CLI_DIR}"
BIN="glowby-${{ matrix.goos }}-${{ matrix.goarch }}"
if [ "${{ matrix.goos }}" = "windows" ]; then
mv "${BIN}.exe" glowby.exe
zip "${BIN}.zip" glowby.exe
else
mv "${BIN}" glowby
tar czf "${BIN}.tar.gz" glowby
fi
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: glowby-${{ matrix.goos }}-${{ matrix.goarch }}
path: |
oss/cli/glowby-*.tar.gz
oss/cli/glowby-*.zip
cli/glowby-*.tar.gz
cli/glowby-*.zip
release:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
path: artifacts
merge-multiple: true
- name: Create release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
files: artifacts/*
================================================
FILE: .gitignore
================================================
# See https://www.dartlang.org/guides/libraries/private-files
# Files and directories created by pub
.dart_tool/
.packages
build/
# If you're building an application, you may want to check-in your pubspec.lock
pubspec.lock
# Directory created by dartdoc
# If you don't generate documentation locally you can remove this line.
doc/api/
# Avoid committing generated Javascript files:
*.dart.js
*.info.json # Produced by the --dump-info flag.
*.js # When generated by dart2js. Don't specify *.js if your
# project includes source files written in JavaScript.
*.js_
*.js.deps
*.js.map
chat/.DS_Store
.DS_Store
/presets/compose/.idea
presets/swiftui/Custom.xcodeproj/xcuserdata/jacobilin.xcuserdatad/xcschemes/xcschememanagement.plist
presets/swiftui/Custom.xcodeproj/project.xcworkspace/xcuserdata/jacobilin.xcuserdatad/UserInterfaceState.xcuserstate
/docs/.react-router/
/docs/.source/
/docs/build/
/docs/node_modules/
.obsidian/workspace.json
.obsidian/core-plugins.json
.obsidian/appearance.json
.obsidian/app.json
project.zip
/web/node_modules
Untitled.md
/web/dist
backend/glowbom-backend
backend/assets/flux-comfyui-workflow.json
backend/assets/128.json
backend/assets/128-api.json
backend/assets/104.json
backend/node/local.ts
backend/package.json
/backend/saved_audio
/backend/saved_images
backend/veo-bridge.js
backend/veo-cli.js
cli/glowby
backend/server
================================================
FILE: AGENTS.md
================================================
# Workspace Instructions
## Commit Messages
- After making code changes, always suggest a one-line conventional commit message.
- Prefer the format `type: summary`.
================================================
FILE: CLI.md
================================================
# glowby CLI
Terminal-first CLI for Glowby OSS. Starts the Go backend and web UI, opens the browser, and manages the full local dev workflow from one command.
## Install
### From GitHub Releases
```sh
curl -fsSL https://raw.githubusercontent.com/glowbom/glowby/main/scripts/install.sh | sh
```
Or set a custom install directory:
```sh
GLOWBY_INSTALL_DIR=~/.local/bin curl -fsSL ... | sh
```
### Build from source
```sh
cd cli
go build -o glowby .
```
## Commands
### `glowby code`
Start the backend, web UI, and open the browser from a local Glowby checkout.
```sh
glowby code # Start Glowby from the current checkout
glowby code /path/to/project # Start Glowby and print a project path hint
```
What it does:
1. Starts the Go backend (`go run .` in `backend/`)
2. Runs `bun install` in `web/` if `node_modules/` is missing
3. Reclaims ports `4569` and `4572` if they are already occupied by a previous Glowby run
4. Starts the web dev server (`bun run dev` in `web/`)
5. Waits for the web server to be ready, then opens the browser
6. If a project path is given, prints the path so you can load it in the UI
Press Ctrl+C to stop both servers.
**Argument parsing:**
- If a positional arg is given and it is an existing directory, it is treated as the project path
- If it is not an existing directory, the command exits with an error
**Finding the Glowby root:** The CLI looks for sibling `backend/` and `web/` directories relative to the binary location or the current working directory and its parent directories.
### `glowby doctor`
Check that required tools are installed.
```sh
glowby doctor
```
Checks for: `go` (required), `bun` (required), `opencode` (required), and a local Glowby checkout with sibling `backend/` and `web/` directories. Returns exit code 1 if required dependencies are missing.
### `glowby version`
```sh
glowby version
```
Prints version, commit hash, and build date. These are injected at build time via ldflags.
## Local Development
```sh
cd cli
# Build
go build -o glowby .
# Build with version info
go build -ldflags "-s -w \
-X main.version=v0.1.0 \
-X main.commit=$(git rev-parse --short HEAD) \
-X main.date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
-o glowby .
# Run
./glowby version
./glowby doctor
./glowby code
# Vet
go vet ./...
```
## Releases
Releases are built automatically by GitHub Actions when a tag matching `v*` is pushed.
```sh
git tag v0.1.0
git push origin v0.1.0
```
The workflow builds binaries for:
- macOS (amd64, arm64)
- Linux (amd64, arm64)
- Windows (amd64, arm64)
Archives are uploaded to the GitHub Release page.
## Exit Codes
| Code | Meaning |
|------|---------|
| 0 | Success |
| 1 | Command or dependency failure |
| 2 | Usage error (unknown command, bad args) |
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2023 Glowbom, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Glowby OSS
> We just launched Glowby OSS! [See the announcement](https://x.com/jacobilin/status/2035059308463833292)
**Build anything locally.**
Glowby helps you build production-ready software with coding agents. It is an open source coding agent workflow for real projects. It is built primarily for Glowbom projects, but the workflow can also work with other project structures.
## What It Does
- Make software projects and prototypes production-ready with coding agents
- Run on local projects with ChatGPT login, API keys, or OpenCode config
## Vision
We believe that you should own your code and data. Every line of code Glowby generates lives on your machine, in standard project files you can open with any editor. No vendor lock-in.
## Install
Install the Glowby CLI:
```bash
curl -fsSL https://raw.githubusercontent.com/glowbom/glowby/main/scripts/install.sh | sudo sh
```
For Windows, we recommend using WSL and running the install command inside Ubuntu.
Then clone the repo and enter it:
```bash
git clone https://github.com/glowbom/glowby.git
cd glowby
```
## Quickstart
Glowby needs these tools available on your `PATH`:
- [Go](https://go.dev/)
- [Bun](https://bun.sh/)
- [OpenCode](https://opencode.ai/)
Run the built-in environment check and launch Glowby:
```bash
glowby doctor
glowby code
```
Run those commands from the Glowby repo root, where `backend/` and `web/` live side by side.
## Security Defaults
`glowby code` now hardens the local stack by default:
- Glowby services bind to loopback (`127.0.0.1`) instead of all interfaces
- the backend API requires a per-run bearer token
- the OpenCode bridge runs with `OPENCODE_SERVER_PASSWORD`
To view the generated credentials for the current session, start Glowby with `glowby code --show-local-auth`.
If you launch the stack manually, set equivalent env vars yourself:
```bash
export GLOWBY_BIND_HOST=127.0.0.1
export GLOWBY_SERVER_TOKEN="$(openssl rand -hex 32)"
export OPENCODE_SERVER_PASSWORD="$(openssl rand -hex 32)"
```
Then run the backend with those env vars, and start the web app with:
```bash
export VITE_GLOWBY_SERVER_TOKEN="$GLOWBY_SERVER_TOKEN"
```
## Start Using Glowby OSS
1. Open `http://localhost:4572`
2. Load a local project
3. Choose how you want to run the agent:
- ChatGPT login
- API keys
- OpenCode config
4. Start a refine run
## Cost
You can build with Glowby for free. Run local AI models on your own computer or connect to free cloud models. If you want access to premium models, you can connect a paid account or your own API keys, but you do not need to.
## Requirements And Setup
If `glowby doctor` reports missing tools, install them first and confirm they are available on your `PATH`:
```bash
go version
bun --version
opencode --version
```
If any command is not found, restart your terminal first. If it still does not work, add the tool's install location to your `PATH` or reinstall it using the tool's recommended installer.
On macOS, a common fix is to add the tool's bin directory to your shell profile (usually `~/.zshrc`) and then reload it:
```bash
# Common PATH fixes on macOS
echo 'export PATH="/usr/local/go/bin:$PATH"' >> ~/.zshrc
echo 'export BUN_INSTALL="$HOME/.bun"' >> ~/.zshrc
echo 'export PATH="$BUN_INSTALL/bin:$PATH"' >> ~/.zshrc
# Add the directory that contains the opencode binary
echo 'export PATH="/path/to/opencode/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
```
If you use Bash instead of zsh, update `~/.bash_profile` or `~/.bashrc` instead.
### Manual fallback
If you prefer to launch the stack without the CLI, run the backend and web app separately:
#### Backend
```bash
cd backend
go run .
```
The backend runs on `http://localhost:4569`.
#### Web app
```bash
cd web
bun install
bun run dev
```
The web app runs on `http://localhost:4572`.
## Using the Bundled Default Project
This repo includes a ready-to-use Glowbom default project in `project/`.
You can use `project/` as your main starting template without logging in to Glowbom.com or downloading a project export first. Just copy the folder, rename it if you want, and start customizing it locally.
The bundled project includes:
- `project/prototype/` - reference design and assets
- `project/apple/` - Apple app project
- `project/android/` - Android app project
- `project/web/` - web app project
- `project/glowbom.json` - project manifest
If you only need some targets, remove the platform folders you do not want:
- Delete `project/apple/` if you do not need Apple platforms
- Delete `project/android/` if you do not need Android
- Delete `project/web/` if you do not need web
- Keep all of them if you want to build every platform in sync from one Glowbom project
## Project Structure
- `backend/` - Go backend
- `project/` - bundled default Glowbom project template
- `web/` - React + Vite web app
- `legacy/` - older Glowby code kept for reference
================================================
FILE: backend/backend_info_page.go
================================================
package main
import (
"encoding/json"
"fmt"
"html"
"net/http"
"os"
"path/filepath"
"strings"
)
const (
glowbyRepositoryURL = "https://github.com/glowbom/glowby"
glowbomWebsiteURL = "https://glowbom.com"
codexAppServerURL = "https://developers.openai.com/codex/app-server/"
glowbomDesktopPDFPath = "docs/2026/Glowbom_Desktop_A_Sketch_to_Software_System.pdf"
)
type backendInfo struct {
Name string `json:"name"`
Summary string `json:"summary"`
HowItWorks string `json:"howItWorks"`
RuntimeURL string `json:"runtimeURL"`
Links []backendInfoLink `json:"links"`
Drivers []backendDriver `json:"drivers"`
ProjectDescriptionURL string `json:"projectDescriptionURL,omitempty"`
ProjectDescriptionPath string `json:"projectDescriptionPath,omitempty"`
}
type backendInfoLink struct {
Label string `json:"label"`
URL string `json:"url"`
}
type backendDriver struct {
ID string `json:"id"`
Label string `json:"label"`
Status string `json:"status"` // available | planned
Description string `json:"description"`
}
func resolveGlowbomDesktopPDFPath() (string, bool) {
candidates := []string{
filepath.FromSlash(filepath.Join("..", glowbomDesktopPDFPath)),
filepath.FromSlash(glowbomDesktopPDFPath),
}
for _, candidate := range candidates {
abs, err := filepath.Abs(candidate)
if err != nil {
continue
}
info, err := os.Stat(abs)
if err != nil || info.IsDir() {
continue
}
return abs, true
}
return "", false
}
func resolveGlowbyPublicAssetPath(fileName string) (string, bool) {
safeName := strings.TrimSpace(fileName)
if safeName == "" || strings.Contains(safeName, "/") || strings.Contains(safeName, "\\") {
return "", false
}
candidates := []string{
filepath.FromSlash(filepath.Join("..", "website", "glowby-oss", "public", safeName)),
filepath.FromSlash(filepath.Join("website", "glowby-oss", "public", safeName)),
}
for _, candidate := range candidates {
abs, err := filepath.Abs(candidate)
if err != nil {
continue
}
info, err := os.Stat(abs)
if err != nil || info.IsDir() {
continue
}
return abs, true
}
return "", false
}
func serveGlowbyPublicAsset(w http.ResponseWriter, r *http.Request, fileName string) {
if r.Method != http.MethodGet && r.Method != http.MethodHead {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
assetPath, ok := resolveGlowbyPublicAssetPath(fileName)
if !ok {
http.NotFound(w, r)
return
}
w.Header().Set("Cache-Control", "no-store")
http.ServeFile(w, r, assetPath)
}
func glowbyFaviconHandler(w http.ResponseWriter, r *http.Request) {
serveGlowbyPublicAsset(w, r, "favicon.png")
}
func glowbyLogoSVGHandler(w http.ResponseWriter, r *http.Request) {
serveGlowbyPublicAsset(w, r, "logo-svg.svg")
}
func glowbyBackendInfoPayload() backendInfo {
info := backendInfo{
Name: "Glowby",
Summary: "Choose a project folder and let Glowby finish the engineering work locally.",
HowItWorks: "Glowby manages agent drivers for you, keeps context in one place, and streams every step while code is being improved.",
RuntimeURL: "http://127.0.0.1:" + getAgentPort(),
Links: []backendInfoLink{
{Label: "Glowby OSS repository", URL: glowbyRepositoryURL},
{Label: "Glowbom website", URL: glowbomWebsiteURL},
{Label: "Codex App Server", URL: codexAppServerURL},
{Label: "Backend metadata (JSON)", URL: "/opencode/about"},
{Label: "Backend health", URL: "/opencode/health"},
{Label: "Auth status", URL: "/opencode/auth/status"},
},
Drivers: []backendDriver{
{
ID: "opencode",
Label: "OpenCode",
Status: "available",
Description: "Default driver today. Great for local refine runs and broad model/provider flexibility.",
},
{
ID: "codex-app-server",
Label: "Codex App Server",
Status: "planned",
Description: "Next planned driver. You will be able to switch drivers without changing your project workflow.",
},
},
}
if pdfPath, ok := resolveGlowbomDesktopPDFPath(); ok {
info.ProjectDescriptionURL = "/opencode/about/project-description"
info.ProjectDescriptionPath = pdfPath
info.Links = append(info.Links, backendInfoLink{
Label: "Glowbom Desktop system paper (PDF)",
URL: info.ProjectDescriptionURL,
})
}
return info
}
func normalizedLinkHref(raw string) string {
href := strings.TrimSpace(raw)
if href == "" {
return "#"
}
return href
}
func glowbyBackendHomeHandler(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
info := glowbyBackendInfoPayload()
if strings.Contains(strings.ToLower(r.Header.Get("Accept")), "application/json") {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(info)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Header().Set("Cache-Control", "no-store")
var linksHTML strings.Builder
for _, link := range info.Links {
label := html.EscapeString(strings.TrimSpace(link.Label))
href := html.EscapeString(normalizedLinkHref(link.URL))
if label == "" || href == "" {
continue
}
linksHTML.WriteString(fmt.Sprintf(`<li><a href="%s" target="_blank" rel="noreferrer">%s</a></li>`, href, label))
}
var driversHTML strings.Builder
for _, driver := range info.Drivers {
label := html.EscapeString(strings.TrimSpace(driver.Label))
status := "Planned"
if strings.EqualFold(strings.TrimSpace(driver.Status), "available") {
status = "Available"
}
description := html.EscapeString(strings.TrimSpace(driver.Description))
driversHTML.WriteString(fmt.Sprintf(
`<li><strong>%s</strong> <span class="chip">%s</span><div class="driver-desc">%s</div></li>`,
label,
html.EscapeString(status),
description,
))
}
projectPathLine := `<p class="meta"><strong>Project description PDF:</strong> not found locally.</p>`
if strings.TrimSpace(info.ProjectDescriptionPath) != "" {
projectPathLine = fmt.Sprintf(
`<p class="meta"><strong>Local PDF path:</strong> <code>%s</code></p>`,
html.EscapeString(info.ProjectDescriptionPath),
)
}
page := fmt.Sprintf(`<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/png" href="/favicon.png" />
<title>%s</title>
<style>
:root {
color-scheme: light;
--bg: #fafafa;
--surface: #ffffff;
--surface-soft: #f3f8f6;
--text: #2b2b2b;
--muted: #5c5c5c;
--border: #dfe9e4;
--brand-green: #29de92;
--brand-cyan: #29ded3;
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
background: linear-gradient(180deg, #eaf0ef 0%%, #f1f4f3 32%%, #f8f8f8 100%%);
color: var(--text);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
}
.topbar {
height: 80px;
border-bottom: 1px solid #eef4f1;
background: #ffffff;
}
.topbar-inner {
max-width: 1420px;
height: 100%%;
margin: 0 auto;
padding: 0 22px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 18px;
}
.topbar-logo {
display: inline-flex;
align-items: center;
}
.topbar-logo img {
width: 118px;
height: 29px;
margin-top: 8px;
}
.topbar-links {
display: flex;
align-items: center;
gap: 22px;
margin-left: auto;
margin-right: 8px;
}
.topbar-links a {
color: var(--text);
text-decoration: none;
font-size: 16px;
font-weight: 400;
}
.topbar-links a:hover {
color: var(--brand-green);
text-decoration: underline;
}
.button {
border: none;
border-radius: 999px;
padding: 11px 18px;
background-image: radial-gradient(circle farthest-side at 0%% 0%%, var(--brand-green), var(--brand-cyan));
color: #ffffff;
font: inherit;
font-weight: 600;
text-decoration: none;
}
main {
width: min(1120px, 100%%);
margin: 0 auto;
padding: 24px 16px 34px;
display: grid;
gap: 12px;
}
.hero {
border: 1px solid #e2f0ea;
border-radius: 16px;
background: linear-gradient(140deg, rgba(255, 255, 255, 0.98) 0%%, rgba(238, 250, 244, 0.98) 100%%);
box-shadow: 0 18px 44px rgba(41, 222, 146, 0.14);
padding: 22px 22px;
display: grid;
gap: 8px;
}
.card {
border: 1px solid var(--border);
border-radius: 16px;
background: #ffffff;
box-shadow: 0 8px 22px rgba(43, 43, 43, 0.08);
padding: 16px;
display: grid;
gap: 8px;
}
h1 {
margin: 0 0 6px;
font-size: clamp(1.9rem, 1.15rem + 2.1vw, 2.7rem);
line-height: 1;
}
h2 {
margin: 0;
font-size: 1.08rem;
}
p {
margin: 0;
line-height: 1.5;
}
ol, ul {
margin: 0;
padding-left: 20px;
display: grid;
gap: 7px;
}
a {
color: #177e52;
text-decoration: none;
font-weight: 600;
}
a:hover { text-decoration: underline; }
.meta {
margin-top: 8px;
color: #5c5c5c;
font-size: 0.9rem;
}
code {
background: #f1f6f3;
border-radius: 6px;
padding: 2px 6px;
}
.chip {
display: inline-flex;
align-items: center;
border-radius: 999px;
border: 1px solid #bfe6d3;
background: #effbf5;
color: #157047;
font-size: 0.75rem;
font-weight: 700;
padding: 3px 9px;
margin-left: 6px;
}
.driver-desc {
color: var(--muted);
font-size: 0.9rem;
margin-top: 2px;
}
@media (max-width: 880px) {
.topbar {
height: 72px;
}
.topbar-inner {
padding: 0 12px;
}
.topbar-links {
display: none;
}
.button {
padding: 9px 13px;
font-size: 0.82rem;
}
}
</style>
</head>
<body>
<header class="topbar">
<div class="topbar-inner">
<a class="topbar-logo" href="https://glowbom.com" target="_blank" rel="noreferrer">
<img alt="Glowbom" src="/logo-svg.svg" />
</a>
<nav class="topbar-links">
<a href="https://glowbom.com/glowby/" target="_blank" rel="noreferrer">Glowby</a>
<a href="https://glowbom.com/desktop/" target="_blank" rel="noreferrer">Desktop</a>
<a href="https://glowbom.com/terms.html" target="_blank" rel="noreferrer">Terms</a>
<a href="https://glowbom.com/pricing/" target="_blank" rel="noreferrer">Pricing</a>
<a href="https://glowbom.com/docs/" target="_blank" rel="noreferrer">Docs</a>
<a href="https://glowbom.com/#case_studies" target="_blank" rel="noreferrer">Apps</a>
</nav>
<a class="button" href="https://glowbom.com/draw" target="_blank" rel="noreferrer">Get Started for Free</a>
</div>
</header>
<main>
<section class="hero">
<h1>%s</h1>
<p>%s</p>
<p class="meta">%s</p>
<p class="meta">OpenCode runtime: <code>%s</code></p>
</section>
<section class="card">
<h2>How It Works</h2>
<ol>
<li>Open the Glowby web UI at <code>http://127.0.0.1:4572</code>.</li>
<li>Choose a local Glowbom project folder.</li>
<li>Pick an agent driver and run <code>/opencode/refine</code>.</li>
<li>Watch live logs, answer prompts, and open outputs in your IDE.</li>
</ol>
</section>
<section class="card">
<h2>Agent Drivers</h2>
<ul>%s</ul>
</section>
<section class="card">
<h2>Links</h2>
<ul>%s</ul>
%s
</section>
</main>
</body>
</html>`,
html.EscapeString(info.Name),
html.EscapeString(info.Name),
html.EscapeString(info.Summary),
html.EscapeString(info.HowItWorks),
html.EscapeString(info.RuntimeURL),
driversHTML.String(),
linksHTML.String(),
projectPathLine,
)
_, _ = w.Write([]byte(page))
}
func openCodeAboutHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(glowbyBackendInfoPayload())
}
func openCodeProjectDescriptionHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
pdfPath, ok := resolveGlowbomDesktopPDFPath()
if !ok {
http.Error(w, "Project description PDF not found", http.StatusNotFound)
return
}
w.Header().Set("Cache-Control", "no-store")
http.ServeFile(w, r, pdfPath)
}
================================================
FILE: backend/claude_anthropic.go
================================================
package main
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"io"
"math"
"net/http"
"strings"
"time"
)
const claudeSonnetDefaultModel = "claude-sonnet-4-6"
const claudeOpusDefaultModel = "claude-opus-4-6"
func isClaudeOpusModel(modelID string) bool {
return strings.HasPrefix(strings.ToLower(strings.TrimSpace(modelID)), "claude-opus-")
}
func claudeModelTokenRates(modelID string) (inputCostPer1M float64, outputCostPer1M float64) {
if isClaudeOpusModel(modelID) {
return 5.0, 25.0
}
return 3.0, 15.0
}
// callClaudeDrawToCodeApiFull handles draw-to-code using Claude API with native vision
func callClaudeDrawToCodeApiFull(imageBase64, userPrompt, template, imageSource, apiKey string) (*R1Response, error) {
return callClaudeDrawToCodeApiFullWithModel(imageBase64, userPrompt, template, imageSource, apiKey, claudeSonnetDefaultModel)
}
// callClaudeDrawToCodeApiFullWithModel handles draw-to-code with specific Claude model
func callClaudeDrawToCodeApiFullWithModel(imageBase64, userPrompt, template, imageSource, apiKey, modelID string) (*R1Response, error) {
if apiKey == "" {
return &R1Response{
AIResponse: "No Claude API key provided. Please add your key in Settings.",
TokenUsage: map[string]int{"inputTokens": 0, "outputTokens": 0, "totalTokens": 0},
Cost: 0,
}, nil
}
// 1) Build system prompt
systemPrompt := getSystemPrompt(template, imageSource)
systemPrompt += " Replace @tailwind placeholders with the Tailwind CSS CDN link to load the real framework."
// 2) Build detailed task
detailedTask := buildDetailedTaskDescription(template, imageSource, userPrompt)
// 3) Construct multimodal message with image + text for Claude's native vision
// Claude expects content array with image and text blocks
userContent := []interface{}{
map[string]interface{}{
"type": "image",
"source": map[string]interface{}{
"type": "base64",
"media_type": "image/jpeg",
"data": imageBase64,
},
},
map[string]interface{}{
"type": "text",
"text": strings.Join([]string{
fmt.Sprintf("Detailed Task Description: %s", detailedTask),
fmt.Sprintf("Human: %s", userPrompt),
}, "\n"),
},
}
// 4) Call Claude API with vision support
messages := []map[string]interface{}{
{"role": "user", "content": userContent},
}
// Use max tokens for draw-to-code (Claude Sonnet 4.6 supports up to 64k output tokens)
// Use 32768 tokens (32K) with 4096 thinking budget for comprehensive HTML generation
aiResp, inputTokens, outputTokens, err := callClaudeAPIWithThinkingBudgetAndModel(messages, systemPrompt, apiKey, modelID, 32768, 4096)
if err != nil {
return nil, err
}
// 5) Calculate cost based on model
// Sonnet 4.6: Input $3/1M, Output $15/1M
// Opus 4.6: Input $5/1M, Output $25/1M
inputCostPer1M, outputCostPer1M := claudeModelTokenRates(modelID)
cost := (float64(inputTokens) / 1_000_000.0 * inputCostPer1M) + (float64(outputTokens) / 1_000_000.0 * outputCostPer1M)
return &R1Response{
AIResponse: aiResp,
TokenUsage: map[string]int{
"inputTokens": inputTokens,
"outputTokens": outputTokens,
"totalTokens": inputTokens + outputTokens,
},
Cost: cost,
}, nil
}
// callClaudeDrawToCodeStreaming handles draw-to-code with streaming for specified Claude model
func callClaudeDrawToCodeStreaming(w http.ResponseWriter, imageBase64, userPrompt, template, imageSource, apiKey, modelID string) error {
if apiKey == "" {
return fmt.Errorf("no Claude API key provided")
}
// Build system prompt (reuse existing logic)
systemPrompt := getSystemPrompt(template, imageSource)
systemPrompt += " Replace @tailwind placeholders with the Tailwind CSS CDN link to load the real framework."
// Build detailed task
detailedTask := buildDetailedTaskDescription(template, imageSource, userPrompt)
// Construct multimodal message with image + text for Claude's native vision
userContent := []interface{}{
map[string]interface{}{
"type": "image",
"source": map[string]interface{}{
"type": "base64",
"media_type": "image/jpeg",
"data": imageBase64,
},
},
map[string]interface{}{
"type": "text",
"text": strings.Join([]string{
fmt.Sprintf("Detailed Task Description: %s", detailedTask),
fmt.Sprintf("Human: %s", userPrompt),
}, "\n"),
},
}
messages := []map[string]interface{}{
{"role": "user", "content": userContent},
}
// Call existing streaming function with 32K tokens and 4K thinking budget
return callClaudeAPIStreamingWithModel(w, messages, systemPrompt, apiKey, modelID, 32768)
}
// callClaudeApiGo handles normal chat using Claude API
func callClaudeApiGo(prevMsgs []ChatMessage, newMsg, apiKey string) (*R1Response, error) {
return callClaudeApiGoWithModel(prevMsgs, newMsg, apiKey, claudeSonnetDefaultModel)
}
// callClaudeApiGoWithModel handles normal chat using a specific Claude model
func callClaudeApiGoWithModel(prevMsgs []ChatMessage, newMsg, apiKey, modelID string) (*R1Response, error) {
if apiKey == "" {
return &R1Response{
AIResponse: "No Claude API key provided. Please add your key in Settings.",
TokenUsage: map[string]int{},
Cost: 0,
}, nil
}
// Gather system message
systemMsg := defaultSystemPrompt
var messages []map[string]interface{}
for _, m := range prevMsgs {
if m.Role == "system" {
systemMsg = m.Content
} else if m.Role == "user" || m.Role == "assistant" {
messages = append(messages, map[string]interface{}{
"role": m.Role,
"content": m.Content,
})
}
}
// Add new user message
messages = append(messages, map[string]interface{}{
"role": "user",
"content": newMsg,
})
// Call Claude API with increased output limit for comprehensive responses.
aiResp, inputTokens, outputTokens, err := callClaudeAPIWithThinkingBudgetAndModel(messages, systemMsg, apiKey, modelID, 8192, 2048)
if err != nil {
return nil, err
}
// Calculate cost based on selected model.
inputCostPer1M, outputCostPer1M := claudeModelTokenRates(modelID)
cost := (float64(inputTokens) / 1_000_000.0 * inputCostPer1M) + (float64(outputTokens) / 1_000_000.0 * outputCostPer1M)
return &R1Response{
AIResponse: aiResp,
TokenUsage: map[string]int{
"inputTokens": inputTokens,
"outputTokens": outputTokens,
"totalTokens": inputTokens + outputTokens,
},
Cost: cost,
}, nil
}
// callClaudeAPI makes the actual HTTP request to Anthropic API
func callClaudeAPI(messages []map[string]interface{}, systemPrompt, apiKey string, maxTokens int) (string, int, int, error) {
return callClaudeAPIWithThinkingBudget(messages, systemPrompt, apiKey, maxTokens, 2048)
}
// callClaudeAPIWithThinkingBudget makes the actual HTTP request with custom thinking budget
func callClaudeAPIWithThinkingBudget(messages []map[string]interface{}, systemPrompt, apiKey string, maxTokens int, thinkingBudget int) (string, int, int, error) {
return callClaudeAPIWithThinkingBudgetAndModel(messages, systemPrompt, apiKey, claudeSonnetDefaultModel, maxTokens, thinkingBudget)
}
// callClaudeAPIWithThinkingBudgetAndModel makes the actual HTTP request with custom thinking budget and model
func callClaudeAPIWithThinkingBudgetAndModel(messages []map[string]interface{}, systemPrompt, apiKey, modelID string, maxTokens int, thinkingBudget int) (string, int, int, error) {
// Thinking budget must be less than max_tokens
if maxTokens <= thinkingBudget {
thinkingBudget = maxTokens / 4 // Use quarter for thinking if max_tokens is small
}
reqBody := map[string]interface{}{
"model": modelID,
"max_tokens": maxTokens,
"messages": messages,
"system": systemPrompt,
"thinking": map[string]interface{}{
"type": "enabled",
"budget_tokens": thinkingBudget,
},
}
jsonBytes, _ := json.Marshal(reqBody)
fmt.Printf("[DEBUG] Claude request body: %s\n", string(jsonBytes))
req, err := http.NewRequest("POST", "https://api.anthropic.com/v1/messages", bytes.NewReader(jsonBytes))
if err != nil {
fmt.Printf("[ERROR] Failed to create request: %v\n", err)
return "", 0, 0, err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-api-key", apiKey)
req.Header.Set("anthropic-version", "2023-06-01")
fmt.Println("[DEBUG] Calling Claude API...")
resp, err := http.DefaultClient.Do(req)
fmt.Printf("[DEBUG] Claude API responded with status: %v, error: %v\n", resp, err)
if err != nil {
return "", 0, 0, err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
b, _ := io.ReadAll(resp.Body)
errMsg := fmt.Sprintf("Claude API error (%d): %s", resp.StatusCode, string(b))
fmt.Println("[ERROR]", errMsg)
return "", 0, 0, fmt.Errorf("%s", errMsg)
}
// Parse response
bodyBytes, _ := io.ReadAll(resp.Body)
var claudeResp struct {
Content []struct {
Type string `json:"type"`
Text string `json:"text"`
Thinking string `json:"thinking"` // For extended thinking blocks
} `json:"content"`
Usage struct {
InputTokens int `json:"input_tokens"`
OutputTokens int `json:"output_tokens"`
} `json:"usage"`
}
if err := json.Unmarshal(bodyBytes, &claudeResp); err != nil {
return "", 0, 0, fmt.Errorf("failed to parse Claude response: %v", err)
}
// Extract text from content blocks, including thinking blocks
var responseText strings.Builder
var thinkingText strings.Builder
for _, block := range claudeResp.Content {
if block.Type == "thinking" && block.Thinking != "" {
thinkingText.WriteString(block.Thinking)
} else if block.Text != "" {
responseText.WriteString(block.Text)
}
}
// Prepend thinking in <think> tags if present
var fullResponse strings.Builder
if thinkingText.Len() > 0 {
fullResponse.WriteString("<think>")
fullResponse.WriteString(thinkingText.String())
fullResponse.WriteString("</think>")
}
fullResponse.WriteString(responseText.String())
fmt.Printf("[DEBUG] Claude response: %d input tokens, %d output tokens\n",
claudeResp.Usage.InputTokens, claudeResp.Usage.OutputTokens)
return fullResponse.String(),
claudeResp.Usage.InputTokens,
claudeResp.Usage.OutputTokens,
nil
}
// getMagicEditTool returns the tool definition for magic_edit
func getMagicEditTool() map[string]interface{} {
return map[string]interface{}{
"name": "magic_edit",
"description": "Trigger a visual code edit on the currently loaded project. Use this when the user asks you to make visual or code changes to their project (like changing colors, adding elements, modifying layouts, etc.).",
"input_schema": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"edit_description": map[string]interface{}{
"type": "string",
"description": "A clear, specific description of what changes to make to the code/design. Be detailed about colors, positions, sizes, etc.",
},
},
"required": []string{"edit_description"},
},
}
}
// callClaudeChatStreaming streams Claude response via SSE to the writer (for chat)
func callClaudeChatStreaming(w http.ResponseWriter, prevMsgs []ChatMessage, newMsg, apiKey string) error {
return callClaudeChatStreamingWithModel(w, prevMsgs, newMsg, apiKey, claudeSonnetDefaultModel)
}
// callClaudeChatStreamingWithModel streams Claude response with specific model
func callClaudeChatStreamingWithModel(w http.ResponseWriter, prevMsgs []ChatMessage, newMsg, apiKey, modelID string) error {
return callClaudeChatStreamingWithModelAndTools(w, prevMsgs, newMsg, apiKey, modelID, nil)
}
// callClaudeChatStreamingWithModelAndTools streams Claude response with specific model and optional tools
func callClaudeChatStreamingWithModelAndTools(w http.ResponseWriter, prevMsgs []ChatMessage, newMsg, apiKey, modelID string, tools []map[string]interface{}) error {
if apiKey == "" {
return fmt.Errorf("no Claude API key provided")
}
// Gather system message
systemMsg := defaultSystemPrompt
var messages []map[string]interface{}
for _, m := range prevMsgs {
if m.Role == "system" {
systemMsg = m.Content
} else if m.Role == "user" || m.Role == "assistant" {
messages = append(messages, map[string]interface{}{
"role": m.Role,
"content": m.Content,
})
}
}
// Add new user message
messages = append(messages, map[string]interface{}{
"role": "user",
"content": newMsg,
})
// Call streaming Claude API with increased output limit
return callClaudeAPIStreamingWithModelAndTools(w, messages, systemMsg, apiKey, modelID, 8192, tools)
}
// callClaudeAPIStreaming streams Claude responses via SSE directly to the HTTP response writer
func callClaudeAPIStreaming(w http.ResponseWriter, messages []map[string]interface{}, systemPrompt, apiKey string, maxTokens int) error {
return callClaudeAPIStreamingWithModel(w, messages, systemPrompt, apiKey, claudeSonnetDefaultModel, maxTokens)
}
// callClaudeAPIStreamingWithModel streams Claude responses with specific model
func callClaudeAPIStreamingWithModel(w http.ResponseWriter, messages []map[string]interface{}, systemPrompt, apiKey, modelID string, maxTokens int) error {
return callClaudeAPIStreamingWithModelAndTools(w, messages, systemPrompt, apiKey, modelID, maxTokens, nil)
}
// callClaudeAPIStreamingWithModelAndTools streams Claude responses with tools support
func callClaudeAPIStreamingWithModelAndTools(w http.ResponseWriter, messages []map[string]interface{}, systemPrompt, apiKey, modelID string, maxTokens int, tools []map[string]interface{}) error {
// Thinking budget must be less than max_tokens
thinkingBudget := 2048
if maxTokens <= thinkingBudget {
thinkingBudget = maxTokens / 2 // Use half for thinking if max_tokens is small
}
reqBody := map[string]interface{}{
"model": modelID,
"max_tokens": maxTokens,
"messages": messages,
"system": systemPrompt,
"stream": true,
"thinking": map[string]interface{}{
"type": "enabled",
"budget_tokens": thinkingBudget,
},
}
// Add tools if provided
if len(tools) > 0 {
reqBody["tools"] = tools
}
jsonBytes, _ := json.Marshal(reqBody)
req, err := http.NewRequest("POST", "https://api.anthropic.com/v1/messages", bytes.NewReader(jsonBytes))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-api-key", apiKey)
req.Header.Set("anthropic-version", "2023-06-01")
fmt.Println("[DEBUG] Calling Claude API with streaming...")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
b, _ := io.ReadAll(resp.Body)
errMsg := fmt.Sprintf("Claude API error (%d): %s", resp.StatusCode, string(b))
fmt.Println("[ERROR]", errMsg)
return fmt.Errorf("%s", errMsg)
}
fmt.Println("[DEBUG] Claude API responded with status 200, starting to read stream...")
// Stream response chunks to client
var responseBuilder strings.Builder
var thinkingBuilder strings.Builder
var thinkingSent bool
var thinkingStart time.Time
var thinkingDurationSent bool
var inputTokens, outputTokens int
var currentThinkingStep strings.Builder
var currentStepTitleSent bool
var currentBlockType string
// Tool use tracking
var currentToolId string
var currentToolName string
var currentToolInput strings.Builder
reader := bufio.NewReader(resp.Body)
for {
line, err := reader.ReadString('\n')
if len(line) > 0 {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "data:") {
dataStr := strings.TrimSpace(line[6:])
var event struct {
Type string `json:"type"`
Index int `json:"index"`
Delta struct {
Type string `json:"type"`
Text string `json:"text"`
Thinking string `json:"thinking"` // For extended thinking deltas
PartialJson string `json:"partial_json"` // For tool use JSON deltas
} `json:"delta"`
ContentBlock struct {
Type string `json:"type"`
Text string `json:"text"`
Thinking string `json:"thinking"`
Id string `json:"id"` // Tool use ID
Name string `json:"name"` // Tool use name
} `json:"content_block"`
Message struct {
Usage struct {
InputTokens int `json:"input_tokens"`
OutputTokens int `json:"output_tokens"`
} `json:"usage"`
} `json:"message"`
Usage struct {
OutputTokens int `json:"output_tokens"`
} `json:"usage"`
Error struct {
Type string `json:"type"`
Message string `json:"message"`
} `json:"error"`
}
if e := json.Unmarshal([]byte(dataStr), &event); e == nil {
// Check for error events
if event.Type == "error" || event.Error.Type != "" {
errMsg := fmt.Sprintf("Claude streaming error: %s - %s", event.Error.Type, event.Error.Message)
fmt.Println("[ERROR]", errMsg)
return fmt.Errorf("%s", errMsg)
}
// Debug: log event types
if event.Type != "" {
fmt.Printf("[Claude DEBUG] Event type: %s, block type: %s, index: %d\n", event.Type, event.ContentBlock.Type, event.Index)
}
// Handle content_block_start - track what type of block we're in
if event.Type == "content_block_start" {
currentBlockType = event.ContentBlock.Type
if currentBlockType == "thinking" {
fmt.Printf("[Claude DEBUG] Starting thinking block (index %d)\n", event.Index)
if thinkingStart.IsZero() {
thinkingStart = time.Now()
}
currentThinkingStep.Reset()
currentStepTitleSent = false
} else if currentBlockType == "tool_use" {
// Tool use block starting
currentToolId = event.ContentBlock.Id
currentToolName = event.ContentBlock.Name
currentToolInput.Reset()
fmt.Printf("[Claude DEBUG] Starting tool_use block: %s (id: %s)\n", currentToolName, currentToolId)
}
}
// Handle content_block_stop - send tool_use event when tool block ends
if event.Type == "content_block_stop" && currentBlockType == "tool_use" {
// Parse the accumulated JSON input
inputJSON := currentToolInput.String()
fmt.Printf("[Claude] Tool use complete: %s, input: %s\n", currentToolName, inputJSON)
// Send tool_use event to client
toolUseData := map[string]interface{}{
"tool_use": map[string]interface{}{
"id": currentToolId,
"name": currentToolName,
"input": inputJSON,
},
}
toolUseBytes, _ := json.Marshal(toolUseData)
fmt.Fprintf(w, "data: %s\n\n", toolUseBytes)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
// Reset tool tracking
currentToolId = ""
currentToolName = ""
currentToolInput.Reset()
}
// Handle content_block_delta - process content based on current block type
if event.Type == "content_block_delta" {
// Handle tool_use input JSON deltas
if currentBlockType == "tool_use" && event.Delta.PartialJson != "" {
currentToolInput.WriteString(event.Delta.PartialJson)
} else if currentBlockType == "thinking" && event.Delta.Thinking != "" {
// Thinking content - use Delta.Thinking field, not Delta.Text!
thinkingBuilder.WriteString(event.Delta.Thinking)
currentThinkingStep.WriteString(event.Delta.Thinking)
// Try to extract and send the title once we have a complete title
if !currentStepTitleSent {
text := currentThinkingStep.String()
hasCompleteMarkdown := strings.HasPrefix(text, "**") && strings.Count(text, "**") >= 2
hasNewlineSeparation := strings.Count(text, "\n") >= 2
if hasCompleteMarkdown || hasNewlineSeparation {
stepTitle := extractThinkingStepTitle(text)
if stepTitle != "" {
thinkingStepData := map[string]string{"thinkingStep": stepTitle}
thinkingStepBytes, _ := json.Marshal(thinkingStepData)
fmt.Fprintf(w, "data: %s\n\n", thinkingStepBytes)
fmt.Printf("[Claude] Sending thinking step: %s\n", stepTitle)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
currentStepTitleSent = true
}
}
}
} else if currentBlockType == "text" && event.Delta.Text != "" {
// Text content (actual response)
// Send complete thinking before first content chunk
if !thinkingSent && thinkingBuilder.Len() > 0 {
// Send the last thinking step if we haven't sent its title yet
if !currentStepTitleSent && currentThinkingStep.Len() > 0 {
stepTitle := extractThinkingStepTitle(currentThinkingStep.String())
if stepTitle != "" {
thinkingStepData := map[string]string{"thinkingStep": stepTitle}
thinkingStepBytes, _ := json.Marshal(thinkingStepData)
fmt.Fprintf(w, "data: %s\n\n", thinkingStepBytes)
fmt.Printf("[Claude] Sending final thinking step: %s\n", stepTitle)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
currentStepTitleSent = true
}
}
thinkingText := "<think>" + thinkingBuilder.String() + "</think>"
chunkData := map[string]string{"chunk": thinkingText}
chunkBytes, _ := json.Marshal(chunkData)
fmt.Fprintf(w, "data: %s\n\n", chunkBytes)
fmt.Println("[Claude] Sent complete thinking to client")
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
thinkingSent = true
// Send thinking duration
if !thinkingDurationSent && !thinkingStart.IsZero() {
duration := time.Since(thinkingStart).Seconds()
duration = math.Round(duration*10) / 10
metaData := map[string]any{
"meta": map[string]any{
"thinkingSeconds": duration,
},
}
metaBytes, _ := json.Marshal(metaData)
fmt.Fprintf(w, "data: %s\n\n", metaBytes)
fmt.Printf("[Claude] Sent thinking duration: %.1fs\n", duration)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
thinkingDurationSent = true
}
}
responseBuilder.WriteString(event.Delta.Text)
// Send content chunk to client
chunkData := map[string]string{"chunk": event.Delta.Text}
chunkBytes, _ := json.Marshal(chunkData)
fmt.Fprintf(w, "data: %s\n\n", chunkBytes)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
}
}
// Extract usage from message_start
if event.Type == "message_start" {
inputTokens = event.Message.Usage.InputTokens
}
// Extract usage from message_delta
if event.Type == "message_delta" {
outputTokens = event.Usage.OutputTokens
// Calculate cost based on model
// Sonnet 4.6: Input $3/1M, Output $15/1M
// Opus 4.6: Input $5/1M, Output $25/1M
inputCostPer1M, outputCostPer1M := claudeModelTokenRates(modelID)
cost := (float64(inputTokens) / 1_000_000.0 * inputCostPer1M) + (float64(outputTokens) / 1_000_000.0 * outputCostPer1M)
// Send done event with usage
doneData := map[string]interface{}{
"done": true,
"tokenUsage": map[string]int{
"inputTokens": inputTokens,
"outputTokens": outputTokens,
"totalTokens": inputTokens + outputTokens,
},
"cost": cost,
}
doneBytes, _ := json.Marshal(doneData)
fmt.Fprintf(w, "data: %s\n\n", doneBytes)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
}
}
}
}
if err != nil {
if err == io.EOF {
break
}
return err
}
}
fmt.Printf("[Claude] Stream complete: %d input tokens, %d output tokens\n", inputTokens, outputTokens)
return nil
}
================================================
FILE: backend/codex_chatgpt.go
================================================
package main
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"io"
"math"
"net/http"
"runtime"
"strings"
"time"
)
// buildCodexInput converts ChatMessage history + new user message into the
// structured input array expected by chatgpt.com/backend-api/codex/responses.
// User messages use {"type":"input_text"}, assistant messages use {"type":"output_text"}.
func buildCodexInput(prevMsgs []ChatMessage, newMsg string) []interface{} {
var input []interface{}
for _, m := range prevMsgs {
switch m.Role {
case "user":
input = append(input, map[string]interface{}{
"role": "user",
"content": []map[string]interface{}{
{"type": "input_text", "text": m.Content},
},
})
case "assistant":
input = append(input, map[string]interface{}{
"type": "message",
"role": "assistant",
"content": []map[string]interface{}{
{"type": "output_text", "text": m.Content, "annotations": []string{}},
},
"status": "completed",
})
}
}
input = append(input, map[string]interface{}{
"role": "user",
"content": []map[string]interface{}{
{"type": "input_text", "text": newMsg},
},
})
return input
}
// buildCodexRequestBody builds the standard request body for the Codex Responses API.
func buildCodexRequestBody(instructions string, input []interface{}, reasoningEffort string, stream bool, modelID string) map[string]interface{} {
resolvedModel := normalizeOpenAIModelID(modelID)
return map[string]interface{}{
"model": resolvedModel,
"store": false,
"stream": stream,
"instructions": instructions,
"input": input,
"reasoning": map[string]interface{}{
"effort": reasoningEffort,
"summary": "auto",
},
"text": map[string]interface{}{
"verbosity": "medium",
},
"include": []string{"reasoning.encrypted_content"},
"tool_choice": "auto",
"parallel_tool_calls": true,
}
}
// callChatGPTCodexStreaming proxies a chat request through the ChatGPT backend
// Codex Responses API, using the JWT access_token and chatgpt_account_id.
func callChatGPTCodexStreaming(w http.ResponseWriter, prevMsgs []ChatMessage, newMsg, accessToken, accountID, modelID string) error {
if accessToken == "" {
return fmt.Errorf("no ChatGPT access token provided")
}
if accountID == "" {
return fmt.Errorf("no ChatGPT account ID provided")
}
systemMsg := defaultSystemPrompt
for _, m := range prevMsgs {
if m.Role == "system" {
systemMsg = m.Content
break
}
}
input := buildCodexInput(prevMsgs, newMsg)
reqBody := buildCodexRequestBody(systemMsg, input, "medium", true, modelID)
return doChatGPTCodexRequest(w, reqBody, accessToken, accountID)
}
// callChatGPTCodexDrawToCodeStreaming handles draw-to-code via the ChatGPT backend
// Codex Responses API.
func callChatGPTCodexDrawToCodeStreaming(w http.ResponseWriter, imageBase64, userPrompt, template, imageSource, accessToken, accountID, modelID string) error {
if accessToken == "" {
return fmt.Errorf("no ChatGPT access token provided")
}
if accountID == "" {
return fmt.Errorf("no ChatGPT account ID provided")
}
systemPrompt := getSystemPrompt(template, imageSource)
systemPrompt += " Replace @tailwind placeholders with the Tailwind CSS CDN link to load the real framework."
detailedTask := buildDetailedTaskDescription(template, imageSource, userPrompt)
userMessage := detailedTask + "\n\n[An image/screenshot was provided for reference. Generate the code based on the description above.]"
input := []interface{}{
map[string]interface{}{
"role": "user",
"content": []map[string]interface{}{
{"type": "input_text", "text": userMessage},
},
},
}
reqBody := buildCodexRequestBody(systemPrompt, input, "low", true, modelID)
return doChatGPTCodexRequest(w, reqBody, accessToken, accountID)
}
// callChatGPTCodexNonStreaming makes a non-streaming call through the ChatGPT backend API.
func callChatGPTCodexNonStreaming(prevMsgs []ChatMessage, newMsg, accessToken, accountID, modelID string) (*R1Response, error) {
if accessToken == "" {
return &R1Response{
AIResponse: "No ChatGPT access token provided. Please re-login in AI Settings.",
TokenUsage: map[string]int{},
Cost: 0,
}, nil
}
systemMsg := defaultSystemPrompt
for _, m := range prevMsgs {
if m.Role == "system" {
systemMsg = m.Content
break
}
}
input := buildCodexInput(prevMsgs, newMsg)
reqBody := buildCodexRequestBody(systemMsg, input, "medium", false, modelID)
jsonBytes, _ := json.Marshal(reqBody)
req, err := http.NewRequest("POST",
"https://chatgpt.com/backend-api/codex/responses",
bytes.NewReader(jsonBytes))
if err != nil {
return nil, err
}
setChatGPTCodexHeaders(req, accessToken, accountID)
fmt.Println("[CODEX] Calling ChatGPT Codex API (non-streaming)...")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
b, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("ChatGPT Codex API error (%d): %s", resp.StatusCode, string(b))
}
b, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
// Parse Responses API format
var result struct {
Output []struct {
Content []struct {
Text string `json:"text"`
} `json:"content"`
} `json:"output"`
Usage struct {
InputTokens int `json:"input_tokens"`
OutputTokens int `json:"output_tokens"`
} `json:"usage"`
}
if err := json.Unmarshal(b, &result); err != nil {
return nil, fmt.Errorf("failed to parse Codex response: %v", err)
}
var text strings.Builder
for _, out := range result.Output {
for _, c := range out.Content {
text.WriteString(c.Text)
}
}
cost := estimateOpenAITextCost(modelID, result.Usage.InputTokens, result.Usage.OutputTokens)
return &R1Response{
AIResponse: text.String(),
TokenUsage: map[string]int{
"inputTokens": result.Usage.InputTokens,
"outputTokens": result.Usage.OutputTokens,
"totalTokens": result.Usage.InputTokens + result.Usage.OutputTokens,
},
Cost: cost,
}, nil
}
// doChatGPTCodexRequest sends a streaming request to the ChatGPT backend Codex API
// and proxies SSE events back to the client in the same format as gpt5_responses_api.go.
func doChatGPTCodexRequest(w http.ResponseWriter, reqBody map[string]interface{}, accessToken, accountID string) error {
jsonBytes, _ := json.Marshal(reqBody)
req, err := http.NewRequest("POST",
"https://chatgpt.com/backend-api/codex/responses",
bytes.NewReader(jsonBytes))
if err != nil {
return err
}
setChatGPTCodexHeaders(req, accessToken, accountID)
fmt.Println("[CODEX] Calling ChatGPT Codex API with streaming...")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
b, _ := io.ReadAll(resp.Body)
bodyText := string(b)
return fmt.Errorf("ChatGPT Codex API error (%d): %s", resp.StatusCode, bodyText)
}
// Parse and proxy SSE — same event format as Responses API
var responseBuilder strings.Builder
var reasoningBuilder strings.Builder
var reasoningSent bool
var reasoningStart time.Time
var reasoningDurationSent bool
var inputTokens, outputTokens, reasoningTokens int
var lastSummaryIndex int = -1
var currentThinkingStep strings.Builder
var currentStepTitleSent bool
reader := bufio.NewReader(resp.Body)
for {
line, err := reader.ReadString('\n')
if len(line) > 0 {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "data:") {
dataStr := strings.TrimSpace(line[5:])
if dataStr == "[DONE]" {
break
}
var chunk struct {
Type string `json:"type"`
SequenceNumber int `json:"sequence_number"`
SummaryIndex int `json:"summary_index"`
Delta string `json:"delta"`
Text string `json:"text"`
Response struct {
Usage struct {
InputTokens int `json:"input_tokens"`
OutputTokens int `json:"output_tokens"`
OutputTokensDetails struct {
ReasoningTokens int `json:"reasoning_tokens"`
} `json:"output_tokens_details"`
} `json:"usage"`
} `json:"response"`
}
if e := json.Unmarshal([]byte(dataStr), &chunk); e == nil {
// Handle reasoning delta events
if chunk.Type == "response.reasoning_summary_text.delta" {
if reasoningStart.IsZero() {
reasoningStart = time.Now()
}
if chunk.Delta != "" {
if chunk.SummaryIndex != lastSummaryIndex {
if lastSummaryIndex != -1 {
reasoningBuilder.WriteString("\n\n")
}
currentThinkingStep.Reset()
currentStepTitleSent = false
lastSummaryIndex = chunk.SummaryIndex
}
reasoningBuilder.WriteString(chunk.Delta)
currentThinkingStep.WriteString(chunk.Delta)
if !currentStepTitleSent {
text := currentThinkingStep.String()
hasCompleteMarkdown := strings.HasPrefix(text, "**") && strings.Count(text, "**") >= 2
hasNewlineSeparation := strings.Count(text, "\n") >= 2
if hasCompleteMarkdown || hasNewlineSeparation {
stepTitle := extractThinkingStepTitle(text)
if stepTitle != "" {
thinkingStepData := map[string]string{"thinkingStep": stepTitle}
thinkingStepBytes, _ := json.Marshal(thinkingStepData)
fmt.Fprintf(w, "data: %s\n\n", thinkingStepBytes)
fmt.Printf("[CODEX] Sending thinking step: %s\n", stepTitle)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
currentStepTitleSent = true
}
}
}
}
}
// Handle output text delta events
if chunk.Type == "response.output_text.delta" {
if chunk.Delta != "" {
// Send complete reasoning before first content chunk
if !reasoningSent && reasoningBuilder.Len() > 0 {
if !currentStepTitleSent && currentThinkingStep.Len() > 0 {
stepTitle := extractThinkingStepTitle(currentThinkingStep.String())
if stepTitle != "" {
thinkingStepData := map[string]string{"thinkingStep": stepTitle}
thinkingStepBytes, _ := json.Marshal(thinkingStepData)
fmt.Fprintf(w, "data: %s\n\n", thinkingStepBytes)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
}
}
thinkingText := "<think>" + reasoningBuilder.String() + "</think>"
chunkData := map[string]string{"chunk": thinkingText}
chunkBytes, _ := json.Marshal(chunkData)
fmt.Fprintf(w, "data: %s\n\n", chunkBytes)
fmt.Println("[CODEX] Sent complete reasoning chain to client")
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
reasoningSent = true
duration := time.Since(reasoningStart).Seconds()
duration = math.Round(duration*10) / 10
metaData := map[string]any{
"meta": map[string]any{
"thinkingSeconds": duration,
},
}
metaBytes, _ := json.Marshal(metaData)
fmt.Fprintf(w, "data: %s\n\n", metaBytes)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
reasoningDurationSent = true
}
responseBuilder.WriteString(chunk.Delta)
chunkData := map[string]string{"chunk": chunk.Delta}
chunkBytes, _ := json.Marshal(chunkData)
fmt.Fprintf(w, "data: %s\n\n", chunkBytes)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
}
}
// Extract usage from final completed event
if chunk.Type == "response.completed" {
inputTokens = chunk.Response.Usage.InputTokens
outputTokens = chunk.Response.Usage.OutputTokens
reasoningTokens = chunk.Response.Usage.OutputTokensDetails.ReasoningTokens
if !reasoningDurationSent && !reasoningStart.IsZero() {
duration := time.Since(reasoningStart).Seconds()
duration = math.Round(duration*10) / 10
metaData := map[string]any{
"meta": map[string]any{
"thinkingSeconds": duration,
},
}
metaBytes, _ := json.Marshal(metaData)
fmt.Fprintf(w, "data: %s\n\n", metaBytes)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
reasoningDurationSent = true
}
modelID, _ := reqBody["model"].(string)
cost := estimateOpenAITextCost(modelID, inputTokens, outputTokens)
doneData := map[string]interface{}{
"done": true,
"tokenUsage": map[string]int{
"inputTokens": inputTokens,
"outputTokens": outputTokens,
"reasoningTokens": reasoningTokens,
"totalTokens": inputTokens + outputTokens,
},
"cost": cost,
}
doneBytes, _ := json.Marshal(doneData)
fmt.Fprintf(w, "data: %s\n\n", doneBytes)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
}
}
}
}
if err != nil {
if err == io.EOF {
break
}
return err
}
}
fmt.Printf("[CODEX] Stream complete: %d input tokens, %d output tokens (%d reasoning)\n", inputTokens, outputTokens, reasoningTokens)
return nil
}
// setChatGPTCodexHeaders sets the required headers for ChatGPT backend API calls.
func setChatGPTCodexHeaders(req *http.Request, accessToken, accountID string) {
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("chatgpt-account-id", accountID)
req.Header.Set("accept", "text/event-stream")
req.Header.Set("User-Agent", fmt.Sprintf("Glowbom (%s %s)", runtime.GOOS, runtime.GOARCH))
}
================================================
FILE: backend/elevenlabs_audio.go
================================================
package main
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
neturl "net/url"
"os"
"path/filepath"
"sort"
"strings"
"time"
)
const (
elevenLabsBaseURL = "https://api.elevenlabs.io"
defaultElevenVoiceID = "JBFqnCBsd6RMkjVDRZzb"
defaultElevenVoiceModel = "eleven_multilingual_v2"
defaultElevenOutputFormat = "mp3_44100_128"
)
type ElevenLabsAudioRequest struct {
Prompt string `json:"prompt"`
AudioType string `json:"audioType"` // "voice" | "sound" | "music"
ElevenLabsKey string `json:"elevenLabsKey,omitempty"`
VoiceID string `json:"voiceId,omitempty"`
VoiceModel string `json:"voiceModel,omitempty"`
SoundModel string `json:"soundModel,omitempty"`
MusicModel string `json:"musicModel,omitempty"`
OutputFormat string `json:"outputFormat,omitempty"`
DurationSeconds float64 `json:"durationSeconds,omitempty"`
PromptInfluence *float64 `json:"promptInfluence,omitempty"`
Loop bool `json:"loop,omitempty"`
ForceInstrumental bool `json:"forceInstrumental,omitempty"`
}
type ElevenLabsAudioResponse struct {
Prompt string `json:"prompt"`
AudioType string `json:"audioType"`
Filename string `json:"filename"`
SavedPath string `json:"saved_path"`
Audio string `json:"audio"`
MimeType string `json:"mimeType"`
}
type ElevenLabsVoicesRequest struct {
ElevenLabsKey string `json:"elevenLabsKey"`
}
type ElevenLabsVoiceOption struct {
VoiceID string `json:"voiceId"`
Name string `json:"name"`
Category string `json:"category,omitempty"`
Description string `json:"description,omitempty"`
PreviewURL string `json:"previewUrl,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
}
type ElevenLabsVoicesResponse struct {
Voices []ElevenLabsVoiceOption `json:"voices"`
}
func listElevenLabsVoicesHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var req ElevenLabsVoicesRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid JSON body", http.StatusBadRequest)
return
}
apiKey := strings.TrimSpace(req.ElevenLabsKey)
if apiKey == "" {
http.Error(w, "elevenLabsKey is required", http.StatusBadRequest)
return
}
voices, err := fetchElevenLabsVoices(apiKey)
if err != nil {
msg := fmt.Sprintf("[ERROR] ElevenLabs voices fetch failed: %v", err)
fmt.Println(msg)
http.Error(w, msg, http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(ElevenLabsVoicesResponse{Voices: voices})
}
func generateAudioHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var req ElevenLabsAudioRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid JSON body", http.StatusBadRequest)
return
}
req.Prompt = strings.TrimSpace(req.Prompt)
audioType := normalizeAudioType(req.AudioType)
apiKey := strings.TrimSpace(req.ElevenLabsKey)
log.Printf("[ELEVENLABS] generation request type=%s prompt=%q", audioType, req.Prompt)
if req.Prompt == "" {
http.Error(w, "prompt is required", http.StatusBadRequest)
return
}
if apiKey == "" {
http.Error(w, "elevenLabsKey is required", http.StatusBadRequest)
return
}
var (
audioBytes []byte
mimeType string
err error
)
switch audioType {
case "voice":
audioBytes, mimeType, err = callElevenLabsVoice(req, apiKey)
case "sound":
audioBytes, mimeType, err = callElevenLabsSound(req, apiKey)
case "music":
audioBytes, mimeType, err = callElevenLabsMusic(req, apiKey)
default:
http.Error(w, "audioType must be one of: voice, sound, music", http.StatusBadRequest)
return
}
if err != nil {
msg := fmt.Sprintf("[ERROR] ElevenLabs %s generation failed: %v", audioType, err)
fmt.Println(msg)
log.Printf("[ELEVENLABS] generation failed type=%s err=%s", audioType, sanitizeElevenLabsError(err))
http.Error(w, msg, http.StatusInternalServerError)
return
}
audioOutputDir := filepath.Join("saved_images", "audio")
if err := os.MkdirAll(audioOutputDir, 0755); err != nil {
fmt.Printf("[WARN] Failed creating %s folder: %v\n", audioOutputDir, err)
}
ext := extensionForAudioMimeType(mimeType)
filename := fmt.Sprintf("%s_%d.%s", audioType, time.Now().UnixNano(), ext)
savedPath := filepath.Join(audioOutputDir, filename)
if err := os.WriteFile(savedPath, audioBytes, 0644); err != nil {
msg := fmt.Sprintf("[ERROR] writing audio file: %v", err)
fmt.Println(msg)
http.Error(w, msg, http.StatusInternalServerError)
return
}
resp := ElevenLabsAudioResponse{
Prompt: req.Prompt,
AudioType: audioType,
Filename: filename,
SavedPath: savedPath,
Audio: fmt.Sprintf("data:%s;base64,%s", mimeType, base64.StdEncoding.EncodeToString(audioBytes)),
MimeType: mimeType,
}
log.Printf("[ELEVENLABS] generation success type=%s bytes=%d mime=%s file=%s", audioType, len(audioBytes), mimeType, filename)
json.NewEncoder(w).Encode(resp)
}
func callElevenLabsVoice(req ElevenLabsAudioRequest, apiKey string) ([]byte, string, error) {
voiceID := strings.TrimSpace(req.VoiceID)
if voiceID == "" {
voiceID = defaultElevenVoiceID
}
modelID := normalizeElevenLabsVoiceModel(req.VoiceModel)
if modelID == "" {
modelID = defaultElevenVoiceModel
}
params := neturl.Values{}
outputFormat := strings.TrimSpace(req.OutputFormat)
if outputFormat == "" {
outputFormat = defaultElevenOutputFormat
}
params.Set("output_format", outputFormat)
body := map[string]interface{}{
"text": req.Prompt,
"model_id": modelID,
}
endpoint := fmt.Sprintf("%s/v1/text-to-speech/%s", elevenLabsBaseURL, neturl.PathEscape(voiceID))
audioBytes, mimeType, err := callElevenLabsBinaryAPI(endpoint, params, body, apiKey)
if err != nil && modelID != defaultElevenVoiceModel && isElevenLabsModelNotFound(err) {
log.Printf("[ELEVENLABS][VOICE] model=%s not found, retrying with %s", modelID, defaultElevenVoiceModel)
body["model_id"] = defaultElevenVoiceModel
return callElevenLabsBinaryAPI(endpoint, params, body, apiKey)
}
return audioBytes, mimeType, err
}
func callElevenLabsSound(req ElevenLabsAudioRequest, apiKey string) ([]byte, string, error) {
params := neturl.Values{}
outputFormat := strings.TrimSpace(req.OutputFormat)
if outputFormat == "" {
outputFormat = defaultElevenOutputFormat
}
params.Set("output_format", outputFormat)
body := map[string]interface{}{
"text": req.Prompt,
}
if modelID := strings.TrimSpace(req.SoundModel); modelID != "" {
body["model_id"] = modelID
}
if req.DurationSeconds > 0 {
body["duration_seconds"] = req.DurationSeconds
}
if req.PromptInfluence != nil {
body["prompt_influence"] = *req.PromptInfluence
}
if req.Loop {
body["loop"] = true
}
return callElevenLabsBinaryAPI(elevenLabsBaseURL+"/v1/sound-generation", params, body, apiKey)
}
func callElevenLabsMusic(req ElevenLabsAudioRequest, apiKey string) ([]byte, string, error) {
params := neturl.Values{}
outputFormat := strings.TrimSpace(req.OutputFormat)
if outputFormat == "" {
outputFormat = defaultElevenOutputFormat
}
params.Set("output_format", outputFormat)
body := map[string]interface{}{
"prompt": req.Prompt,
}
if modelID := strings.TrimSpace(req.MusicModel); modelID != "" {
body["model_id"] = modelID
}
if req.DurationSeconds > 0 {
body["music_length_ms"] = int(req.DurationSeconds * 1000.0)
}
if req.ForceInstrumental {
body["is_instrumental"] = true
}
return callElevenLabsBinaryAPI(elevenLabsBaseURL+"/v1/music", params, body, apiKey)
}
func callElevenLabsBinaryAPI(endpoint string, query neturl.Values, body map[string]interface{}, apiKey string) ([]byte, string, error) {
url := endpoint
if len(query) > 0 {
url += "?" + query.Encode()
}
reqBody, err := json.Marshal(body)
if err != nil {
return nil, "", fmt.Errorf("failed to marshal request: %w", err)
}
request, err := http.NewRequest("POST", url, bytes.NewReader(reqBody))
if err != nil {
return nil, "", fmt.Errorf("failed to create request: %w", err)
}
request.Header.Set("Content-Type", "application/json")
request.Header.Set("xi-api-key", apiKey)
modelID, _ := body["model_id"].(string)
promptText := ""
if text, ok := body["text"].(string); ok {
promptText = text
} else if prompt, ok := body["prompt"].(string); ok {
promptText = prompt
}
log.Printf("[ELEVENLABS] request endpoint=%s model=%s prompt=%q", endpoint, strings.TrimSpace(modelID), promptText)
response, err := http.DefaultClient.Do(request)
if err != nil {
return nil, "", fmt.Errorf("failed to call ElevenLabs API: %w", err)
}
defer response.Body.Close()
data, err := io.ReadAll(response.Body)
if err != nil {
return nil, "", fmt.Errorf("failed to read ElevenLabs response: %w", err)
}
if response.StatusCode < 200 || response.StatusCode >= 300 {
log.Printf("[ELEVENLABS] error endpoint=%s status=%d body=%s", endpoint, response.StatusCode, truncateElevenLabsLog(string(data), 320))
return nil, "", fmt.Errorf("ElevenLabs API error (status %d): %s", response.StatusCode, string(data))
}
mimeType := inferAudioMimeType(response.Header.Get("Content-Type"), data)
log.Printf("[ELEVENLABS] success endpoint=%s status=%d bytes=%d mime=%s", endpoint, response.StatusCode, len(data), mimeType)
return data, mimeType, nil
}
func fetchElevenLabsVoices(apiKey string) ([]ElevenLabsVoiceOption, error) {
request, err := http.NewRequest("GET", elevenLabsBaseURL+"/v1/voices", nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
request.Header.Set("xi-api-key", apiKey)
response, err := http.DefaultClient.Do(request)
if err != nil {
return nil, fmt.Errorf("failed to call ElevenLabs API: %w", err)
}
defer response.Body.Close()
data, err := io.ReadAll(response.Body)
if err != nil {
return nil, fmt.Errorf("failed to read ElevenLabs response: %w", err)
}
if response.StatusCode < 200 || response.StatusCode >= 300 {
return nil, fmt.Errorf("ElevenLabs API error (status %d): %s", response.StatusCode, string(data))
}
var decoded struct {
Voices []struct {
VoiceID string `json:"voice_id"`
Name string `json:"name"`
Category string `json:"category"`
Description string `json:"description"`
PreviewURL string `json:"preview_url"`
Labels map[string]string `json:"labels"`
} `json:"voices"`
}
if err := json.Unmarshal(data, &decoded); err != nil {
return nil, fmt.Errorf("failed to parse ElevenLabs response: %w", err)
}
voices := make([]ElevenLabsVoiceOption, 0, len(decoded.Voices))
for _, v := range decoded.Voices {
if strings.TrimSpace(v.VoiceID) == "" {
continue
}
voices = append(voices, ElevenLabsVoiceOption{
VoiceID: v.VoiceID,
Name: v.Name,
Category: v.Category,
Description: v.Description,
PreviewURL: v.PreviewURL,
Labels: v.Labels,
})
}
sort.SliceStable(voices, func(i, j int) bool {
left := strings.ToLower(strings.TrimSpace(voices[i].Name))
right := strings.ToLower(strings.TrimSpace(voices[j].Name))
if left == right {
return voices[i].VoiceID < voices[j].VoiceID
}
return left < right
})
return voices, nil
}
func normalizeAudioType(value string) string {
switch strings.ToLower(strings.TrimSpace(value)) {
case "", "voice", "speech", "tts":
return "voice"
case "sound", "sfx", "sound_effect":
return "sound"
case "music", "song":
return "music"
default:
return strings.ToLower(strings.TrimSpace(value))
}
}
func normalizeElevenLabsVoiceModel(value string) string {
normalized := strings.ToLower(strings.TrimSpace(value))
if normalized == "" {
return ""
}
switch normalized {
case "default", "standard", "base", "multilingual", "multilingual_v2":
return defaultElevenVoiceModel
default:
return strings.TrimSpace(value)
}
}
func isElevenLabsModelNotFound(err error) bool {
if err == nil {
return false
}
lower := strings.ToLower(err.Error())
return strings.Contains(lower, "model_not_found") ||
(strings.Contains(lower, "model id") && strings.Contains(lower, "does not exist"))
}
func sanitizeElevenLabsError(err error) string {
if err == nil {
return ""
}
return truncateElevenLabsLog(strings.ReplaceAll(strings.TrimSpace(err.Error()), "\n", " "), 320)
}
func truncateElevenLabsLog(value string, max int) string {
trimmed := strings.TrimSpace(value)
if len(trimmed) <= max {
return trimmed
}
if max < 4 {
return trimmed[:max]
}
return trimmed[:max-3] + "..."
}
func inferAudioMimeType(contentType string, data []byte) string {
trimmed := strings.TrimSpace(strings.ToLower(contentType))
if trimmed != "" {
if semi := strings.Index(trimmed, ";"); semi >= 0 {
trimmed = strings.TrimSpace(trimmed[:semi])
}
if trimmed != "" && trimmed != "application/octet-stream" {
return trimmed
}
}
if len(data) >= 3 {
if string(data[:3]) == "ID3" {
return "audio/mpeg"
}
// MPEG frame sync.
if data[0] == 0xFF && (data[1]&0xE0) == 0xE0 {
return "audio/mpeg"
}
}
if len(data) >= 12 {
if string(data[:4]) == "RIFF" && string(data[8:12]) == "WAVE" {
return "audio/wav"
}
}
if len(data) >= 4 {
if string(data[:4]) == "OggS" {
return "audio/ogg"
}
if string(data[:4]) == "fLaC" {
return "audio/flac"
}
}
if len(data) >= 12 && string(data[4:8]) == "ftyp" {
return "audio/mp4"
}
return "audio/mpeg"
}
func extensionForAudioMimeType(mimeType string) string {
switch strings.ToLower(strings.TrimSpace(mimeType)) {
case "audio/wav", "audio/wave", "audio/x-wav":
return "wav"
case "audio/ogg":
return "ogg"
case "audio/flac":
return "flac"
case "audio/mp4", "audio/x-m4a":
return "m4a"
default:
return "mp3"
}
}
================================================
FILE: backend/fireworks_fireworks.go
================================================
package main
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"regexp"
"strings"
"time"
)
const fireworksChatCompletionsURL = "https://api.fireworks.ai/inference/v1/chat/completions"
const (
fireworksChatMaxTokens = 4096
fireworksDrawToCodeMaxTokens = 16384
fireworksDefaultTemperature = 0.6
// Hold back a small tail so normalization can safely fix near-boundary chunks.
fireworksStreamNormalizeHoldback = 256
)
var (
compactCommentCodeRegex = regexp.MustCompile(`(?m)(//[^\n]*?)\s+((?:const|let|var|function|document\.|window\.|if\s*\(|for\s*\(|[A-Za-z_$][A-Za-z0-9_$]*\.|[A-Za-z_$][A-Za-z0-9_$]*\())`)
hexColorPercentCompactRegex = regexp.MustCompile(`(#[0-9A-Fa-f]{3,8})(\d{1,3}%)`)
transitionAllCompactRegex = regexp.MustCompile(`transition:\s*all([0-9]+(?:\.[0-9]+)?s)`)
animationDurationCompactRegex = regexp.MustCompile(`animation:\s*([a-zA-Z_-][a-zA-Z0-9_-]*)([0-9]+(?:\.[0-9]+)?s)`)
boxShadowDoubleZeroCompact = regexp.MustCompile(`box-shadow:\s*00([0-9]+px)`)
boxShadowCompactTwoOffsetRegex = regexp.MustCompile(`box-shadow:\s*0([0-9]+px)([0-9]+px)`)
fireworksGenericDataURIRegex = regexp.MustCompile(`(?is)data:[^"'\s>]+;base64,[A-Za-z0-9+/=\s]{128,}`)
fireworksImgSrcBase64Regex = regexp.MustCompile(`(?is)<img\b[^>]*\bsrc\s*=\s*["'][A-Za-z0-9+/=\s]{256,}["']`)
fireworksImageSigBase64Regex = regexp.MustCompile(`(?is)["'](?:iVBORw0KGgo|/9j/|R0lGOD|UklGR|Qk0)[A-Za-z0-9+/=\s]{256,}["']`)
fireworksFunctionalityConstRE = regexp.MustCompile(`(?i)\bfunctionality\s+const\b`)
fireworksThinkTagRegex = regexp.MustCompile(`(?is)</?think>`)
// Go's regexp (RE2) rejects counted repeats above 1000.
fireworksLongBase64RunRegex = regexp.MustCompile(`[A-Za-z0-9+/]{1000,}={0,2}`)
)
func callFireworksDrawToCodeApiFull(imageBase64, userPrompt, template, imageSource, apiKey, fireworksModel string) (*R1Response, error) {
if strings.TrimSpace(apiKey) == "" {
return &R1Response{
AIResponse: "No Fireworks API key provided. Please add your key in Settings.",
TokenUsage: map[string]int{"inputTokens": 0, "outputTokens": 0, "totalTokens": 0},
Cost: 0,
}, nil
}
messages := buildFireworksDrawToCodeMessages(imageBase64, userPrompt, template, imageSource, fireworksModel, apiKey)
aiResp, inputTokens, outputTokens, err := callFireworksChatCompletions(messages, apiKey, fireworksDrawToCodeMaxTokens, fireworksDefaultTemperature, fireworksModel, true)
if err != nil {
return nil, err
}
return &R1Response{
AIResponse: aiResp,
TokenUsage: map[string]int{
"inputTokens": inputTokens,
"outputTokens": outputTokens,
"totalTokens": inputTokens + outputTokens,
},
Cost: 0,
}, nil
}
func callFireworksDrawToCodeStreaming(w http.ResponseWriter, imageBase64, userPrompt, template, imageSource, apiKey, fireworksModel string) error {
if strings.TrimSpace(apiKey) == "" {
return fmt.Errorf("no Fireworks API key provided")
}
messages := buildFireworksDrawToCodeMessages(imageBase64, userPrompt, template, imageSource, fireworksModel, apiKey)
return callFireworksChatCompletionsStreaming(w, messages, apiKey, fireworksDrawToCodeMaxTokens, fireworksDefaultTemperature, fireworksModel, true)
}
func callFireworksApiGo(prevMsgs []ChatMessage, newMsg, apiKey, fireworksModel, attachmentBase64, attachmentMime string) (*R1Response, error) {
if strings.TrimSpace(apiKey) == "" {
return &R1Response{
AIResponse: "No Fireworks API key provided. Please add your key in Settings.",
TokenUsage: map[string]int{"inputTokens": 0, "outputTokens": 0, "totalTokens": 0},
Cost: 0,
}, nil
}
messages := buildFireworksChatMessages(prevMsgs, newMsg, fireworksModel, attachmentBase64, attachmentMime)
aiResp, inputTokens, outputTokens, err := callFireworksChatCompletions(messages, apiKey, fireworksChatMaxTokens, fireworksDefaultTemperature, fireworksModel, false)
if err != nil {
return nil, err
}
return &R1Response{
AIResponse: aiResp,
TokenUsage: map[string]int{
"inputTokens": inputTokens,
"outputTokens": outputTokens,
"totalTokens": inputTokens + outputTokens,
},
Cost: 0,
}, nil
}
func callFireworksChatStreaming(w http.ResponseWriter, prevMsgs []ChatMessage, newMsg, apiKey, fireworksModel, attachmentBase64, attachmentMime string) error {
if strings.TrimSpace(apiKey) == "" {
return fmt.Errorf("no Fireworks API key provided")
}
messages := buildFireworksChatMessages(prevMsgs, newMsg, fireworksModel, attachmentBase64, attachmentMime)
return callFireworksChatCompletionsStreaming(w, messages, apiKey, fireworksChatMaxTokens, fireworksDefaultTemperature, fireworksModel, false)
}
func buildFireworksChatMessages(prevMsgs []ChatMessage, newMsg, fireworksModel, attachmentBase64, attachmentMime string) []map[string]interface{} {
systemMsg := defaultSystemPrompt
for _, m := range prevMsgs {
if m.Role == "system" {
systemMsg = m.Content
}
}
messages := []map[string]interface{}{
{
"role": "system",
"content": systemMsg,
},
}
for _, m := range prevMsgs {
if m.Role == "user" || m.Role == "assistant" {
messages = append(messages, map[string]interface{}{
"role": m.Role,
"content": m.Content,
})
}
}
userContent := interface{}(newMsg)
trimmedAttachment := strings.TrimSpace(attachmentBase64)
trimmedMime := strings.TrimSpace(attachmentMime)
if trimmedAttachment != "" && trimmedMime != "" {
resolvedModel := normalizeFireworksModelID(fireworksModel)
if fireworksModelSupportsImageInput(resolvedModel) {
if strings.HasPrefix(strings.ToLower(trimmedMime), "image/") {
userContent = []interface{}{
map[string]interface{}{
"type": "text",
"text": newMsg,
},
map[string]interface{}{
"type": "image_url",
"image_url": map[string]interface{}{
"url": fireworksAttachmentDataURI(trimmedAttachment, trimmedMime),
},
},
}
fmt.Printf("[Fireworks DEBUG] inline image attachment enabled model=%s mime=%s\n", resolvedModel, trimmedMime)
} else {
fmt.Printf("[Fireworks DEBUG] attachment ignored for model=%s (non-image mime=%q)\n", resolvedModel, trimmedMime)
}
} else {
fmt.Printf("[Fireworks DEBUG] attachment ignored for model=%s (image input unsupported)\n", resolvedModel)
}
}
messages = append(messages, map[string]interface{}{
"role": "user",
"content": userContent,
})
return messages
}
func callFireworksChatCompletions(messages []map[string]interface{}, apiKey string, maxTokens int, temperature float64, fireworksModel string, enforceNoEmbeddedImageData bool) (string, int, int, error) {
resolvedModel := normalizeFireworksModelID(fireworksModel)
hasImage := fireworksMessagesContainImage(messages)
logFireworksRequestSummary(resolvedModel, false, hasImage, maxTokens, messages)
reqBody := map[string]interface{}{
"model": resolvedModel,
"max_tokens": maxTokens,
"top_p": 1,
"top_k": 40,
"presence_penalty": 0,
"frequency_penalty": 0,
"temperature": temperature,
"messages": messages,
}
if prettyBody, err := json.MarshalIndent(reqBody, "", " "); err == nil {
fmt.Printf("[Fireworks DEBUG] request body stream=false >>>\n%s\n<<< END Fireworks request body\n", string(prettyBody))
}
jsonBytes, _ := json.Marshal(reqBody)
req, err := http.NewRequest("POST", fireworksChatCompletionsURL, bytes.NewReader(jsonBytes))
if err != nil {
return "", 0, 0, err
}
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+apiKey)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", 0, 0, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
bodyText := strings.TrimSpace(string(body))
fmt.Printf("[Fireworks ERROR] model=%s stream=false status=%d hasImage=%v body=%s\n", resolvedModel, resp.StatusCode, hasImage, truncateForLog(bodyText))
return "", 0, 0, fmt.Errorf("Fireworks API error (%d): %s", resp.StatusCode, bodyText)
}
var completion struct {
Choices []struct {
Message struct {
Content interface{} `json:"content"`
ReasoningContent interface{} `json:"reasoning_content"`
} `json:"message"`
ReasoningContent interface{} `json:"reasoning_content"`
FinishReason interface{} `json:"finish_reason"`
} `json:"choices"`
Usage struct {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
TotalTokens int `json:"total_tokens"`
} `json:"usage"`
}
if err := json.NewDecoder(resp.Body).Decode(&completion); err != nil {
return "", 0, 0, fmt.Errorf("failed to parse Fireworks response: %w", err)
}
if len(completion.Choices) == 0 {
return "", 0, 0, fmt.Errorf("no choices in Fireworks response")
}
content := fireworkContentToText(completion.Choices[0].Message.Content)
reasoning := fireworkContentToText(completion.Choices[0].Message.ReasoningContent)
if strings.TrimSpace(reasoning) == "" {
reasoning = fireworkContentToText(completion.Choices[0].ReasoningContent)
}
if !enforceNoEmbeddedImageData && strings.TrimSpace(reasoning) != "" {
content = "<think>" + strings.TrimSpace(reasoning) + "</think>" + content
}
if enforceNoEmbeddedImageData {
content = normalizeFireworksDrawToCodeOutput(content)
}
if enforceNoEmbeddedImageData {
if reason, blocked := detectFireworksEmbeddedImagePayload(content); blocked {
return "", 0, 0, embeddedImagePayloadError(reason)
}
}
finishReason := normalizeFinishReason(completion.Choices[0].FinishReason)
if finishReason != "" {
fmt.Printf("[Fireworks] completion finish_reason=%s model=%s stream=false\n", finishReason, resolvedModel)
}
if strings.EqualFold(finishReason, "length") {
fmt.Printf("[Fireworks WARN] completion reached max_tokens=%d model=%s stream=false\n", maxTokens, resolvedModel)
if enforceNoEmbeddedImageData {
return "", 0, 0, fmt.Errorf("Fireworks draw-to-code output was truncated at max_tokens=%d. Please regenerate with a shorter prompt.", maxTokens)
}
}
inputTokens := completion.Usage.PromptTokens
outputTokens := completion.Usage.CompletionTokens
if inputTokens == 0 && outputTokens == 0 {
inputTokens = estimateTokenCountForMessages(messages)
outputTokens = estimateTokenCount(content)
}
return content, inputTokens, outputTokens, nil
}
func callFireworksChatCompletionsStreaming(w http.ResponseWriter, messages []map[string]interface{}, apiKey string, maxTokens int, temperature float64, fireworksModel string, enforceNoEmbeddedImageData bool) error {
resolvedModel := normalizeFireworksModelID(fireworksModel)
hasImage := fireworksMessagesContainImage(messages)
logFireworksRequestSummary(resolvedModel, true, hasImage, maxTokens, messages)
reqBody := map[string]interface{}{
"model": resolvedModel,
"max_tokens": maxTokens,
"top_p": 1,
"top_k": 40,
"presence_penalty": 0,
"frequency_penalty": 0,
"temperature": temperature,
"messages": messages,
"stream": true,
}
if prettyBody, err := json.MarshalIndent(reqBody, "", " "); err == nil {
fmt.Printf("[Fireworks DEBUG] request body stream=true >>>\n%s\n<<< END Fireworks request body\n", string(prettyBody))
}
jsonBytes, _ := json.Marshal(reqBody)
req, err := http.NewRequest("POST", fireworksChatCompletionsURL, bytes.NewReader(jsonBytes))
if err != nil {
return err
}
req.Header.Set("Accept", "text/event-stream, application/json")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+apiKey)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
bodyText := strings.TrimSpace(string(body))
fmt.Printf("[Fireworks ERROR] model=%s stream=true status=%d hasImage=%v body=%s\n", resolvedModel, resp.StatusCode, hasImage, truncateForLog(bodyText))
return fmt.Errorf("Fireworks API error (%d): %s", resp.StatusCode, bodyText)
}
contentType := strings.ToLower(strings.TrimSpace(resp.Header.Get("Content-Type")))
fmt.Printf("[Fireworks] stream response model=%s hasImage=%v contentType=%q\n", resolvedModel, hasImage, contentType)
// Some models/multimodal paths may ignore stream=true and return a regular JSON completion.
// Handle that gracefully instead of silently returning an empty streamed result.
if !strings.Contains(contentType, "text/event-stream") {
body, _ := io.ReadAll(resp.Body)
bodyText := strings.TrimSpace(string(body))
fmt.Printf("[Fireworks] stream fallback model=%s hasImage=%v contentType=%q body=%s\n", resolvedModel, hasImage, contentType, truncateForLog(bodyText))
var completion struct {
Choices []struct {
Message struct {
Content interface{} `json:"content"`
ReasoningContent interface{} `json:"reasoning_content"`
} `json:"message"`
ReasoningContent interface{} `json:"reasoning_content"`
} `json:"choices"`
Usage struct {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
} `json:"usage"`
}
if err := json.Unmarshal(body, &completion); err != nil {
return fmt.Errorf("Fireworks stream fallback parse error: %w", err)
}
if len(completion.Choices) == 0 {
return fmt.Errorf("Fireworks stream fallback returned no choices")
}
content := fireworkContentToText(completion.Choices[0].Message.Content)
reasoning := fireworkContentToText(completion.Choices[0].Message.ReasoningContent)
if strings.TrimSpace(reasoning) == "" {
reasoning = fireworkContentToText(completion.Choices[0].ReasoningContent)
}
if !enforceNoEmbeddedImageData && strings.TrimSpace(reasoning) != "" {
content = "<think>" + strings.TrimSpace(reasoning) + "</think>" + content
}
if enforceNoEmbeddedImageData {
content = normalizeFireworksDrawToCodeOutput(content)
}
if enforceNoEmbeddedImageData {
if reason, blocked := detectFireworksEmbeddedImagePayload(content); blocked {
return embeddedImagePayloadError(reason)
}
}
if strings.TrimSpace(content) == "" {
return fmt.Errorf("Fireworks stream fallback returned empty content")
}
if err := emitFireworksStreamChunk(w, content); err != nil {
return err
}
promptTokens := completion.Usage.PromptTokens
completionTokens := completion.Usage.CompletionTokens
if promptTokens == 0 && completionTokens == 0 {
promptTokens = estimateTokenCountForMessages(messages)
completionTokens = estimateTokenCount(content)
}
return emitFireworksStreamDone(w, promptTokens, completionTokens)
}
reader := bufio.NewReader(resp.Body)
var responseBuilder strings.Builder
// Keep real-time streaming for draw-to-code UX.
bufferDrawOutput := false
promptTokens := 0
completionTokens := 0
dataEvents := 0
parseErrors := 0
finishReasonLength := false
seenFinishReason := ""
rollingTail := ""
normalizedEmitted := 0
var reasoningBuilder strings.Builder
var reasoningSent bool
var reasoningStartTimeSet bool
var reasoningStartTimestamp int64
var reasoningDurationSent bool
lastThinkingStep := ""
for {
line, err := reader.ReadString('\n')
if len(line) > 0 {
trimmedLine := strings.TrimSpace(line)
if strings.HasPrefix(trimmedLine, "data:") {
dataEvents++
dataStr := strings.TrimSpace(trimmedLine[5:])
if dataStr == "" {
continue
}
if dataStr == "[DONE]" {
break
}
var chunk struct {
Choices []struct {
Delta struct {
Content interface{} `json:"content"`
ReasoningContent interface{} `json:"reasoning_content"`
} `json:"delta"`
Message struct {
Content interface{} `json:"content"`
ReasoningContent interface{} `json:"reasoning_content"`
} `json:"message"`
ReasoningContent interface{} `json:"reasoning_content"`
FinishReason interface{} `json:"finish_reason"`
} `json:"choices"`
Usage struct {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
} `json:"usage"`
}
if unmarshalErr := json.Unmarshal([]byte(dataStr), &chunk); unmarshalErr != nil {
parseErrors++
if parseErrors <= 3 {
fmt.Printf("[Fireworks WARN] stream chunk parse failed model=%s hasImage=%v err=%v raw=%s\n", resolvedModel, hasImage, unmarshalErr, truncateForLog(dataStr))
}
continue
}
if chunk.Usage.PromptTokens > 0 {
promptTokens = chunk.Usage.PromptTokens
completionTokens = chunk.Usage.CompletionTokens
}
if len(chunk.Choices) > 0 {
reasoningChunk := fireworkContentToText(chunk.Choices[0].Delta.ReasoningContent)
if strings.TrimSpace(reasoningChunk) == "" {
reasoningChunk = fireworkContentToText(chunk.Choices[0].Message.ReasoningContent)
}
if strings.TrimSpace(reasoningChunk) == "" {
reasoningChunk = fireworkContentToText(chunk.Choices[0].ReasoningContent)
}
if !enforceNoEmbeddedImageData && strings.TrimSpace(reasoningChunk) != "" {
if !reasoningStartTimeSet {
reasoningStartTimeSet = true
reasoningStartTimestamp = time.Now().UnixMilli()
placeholderStep := "Thinking..."
thinkingStepData := map[string]string{"thinkingStep": placeholderStep}
thinkingStepBytes, _ := json.Marshal(thinkingStepData)
fmt.Fprintf(w, "data: %s\n\n", thinkingStepBytes)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
lastThinkingStep = placeholderStep
}
reasoningBuilder.WriteString(reasoningChunk)
// Emit better step titles as soon as enough reasoning text arrives.
stepTitle := extractFireworksThinkingStepTitle(reasoningBuilder.String())
if isMeaningfulThinkingStepTitle(stepTitle) && stepTitle != lastThinkingStep {
thinkingStepData := map[string]string{"thinkingStep": stepTitle}
thinkingStepBytes, _ := json.Marshal(thinkingStepData)
fmt.Fprintf(w, "data: %s\n\n", thinkingStepBytes)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
lastThinkingStep = stepTitle
}
}
reason := normalizeFinishReason(chunk.Choices[0].FinishReason)
if reason != "" {
seenFinishReason = reason
if strings.EqualFold(reason, "length") {
finishReasonLength = true
}
}
content := fireworkContentToText(chunk.Choices[0].Delta.Content)
if strings.TrimSpace(content) == "" {
content = fireworkContentToText(chunk.Choices[0].Message.Content)
}
if strings.TrimSpace(content) == "" {
continue
}
if !enforceNoEmbeddedImageData && !reasoningSent && reasoningBuilder.Len() > 0 {
thinkingText := "<think>" + strings.TrimSpace(reasoningBuilder.String()) + "</think>"
if err := emitFireworksStreamChunk(w, thinkingText); err != nil {
return err
}
reasoningSent = true
}
if enforceNoEmbeddedImageData {
candidate := rollingTail + content
if reason, blocked := detectFireworksEmbeddedImagePayload(candidate); blocked {
return embeddedImagePayloadError(reason)
}
if len(candidate) > 4096 {
rollingTail = candidate[len(candidate)-4096:]
} else {
rollingTail = candidate
}
}
responseBuilder.WriteString(content)
if !bufferDrawOutput {
chunkToEmit := content
if enforceNoEmbeddedImageData {
normalizedCurrent := normalizeFireworksDrawToCodeOutput(responseBuilder.String())
safeLen := len(normalizedCurrent) - fireworksStreamNormalizeHoldback
if safeLen < 0 {
safeLen = 0
}
if safeLen > normalizedEmitted {
chunkToEmit = normalizedCurrent[normalizedEmitted:safeLen]
normalizedEmitted = safeLen
} else {
chunkToEmit = ""
}
}
if chunkToEmit != "" {
if err := emitFireworksStreamChunk(w, chunkToEmit); err != nil {
return err
}
}
}
if !enforceNoEmbeddedImageData && !reasoningDurationSent && reasoningStartTimeSet {
durationSeconds := float64(time.Now().UnixMilli()-reasoningStartTimestamp) / 1000.0
metaData := map[string]interface{}{
"meta": map[string]float64{
"thinkingSeconds": durationSeconds,
},
}
metaBytes, _ := json.Marshal(metaData)
fmt.Fprintf(w, "data: %s\n\n", metaBytes)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
reasoningDurationSent = true
}
}
}
}
if err != nil {
if err == io.EOF {
break
}
return err
}
}
finalText := responseBuilder.String()
if enforceNoEmbeddedImageData {
finalText = normalizeFireworksDrawToCodeOutput(finalText)
}
if !bufferDrawOutput && enforceNoEmbeddedImageData {
if normalizedEmitted < len(finalText) {
if err := emitFireworksStreamChunk(w, finalText[normalizedEmitted:]); err != nil {
return err
}
normalizedEmitted = len(finalText)
}
responseBuilder.Reset()
responseBuilder.WriteString(finalText)
}
if bufferDrawOutput {
if enforceNoEmbeddedImageData {
finalText = normalizeFireworksDrawToCodeOutput(finalText)
}
responseBuilder.Reset()
responseBuilder.WriteString(finalText)
if strings.TrimSpace(finalText) != "" {
if err := emitFireworksStreamChunk(w, finalText); err != nil {
return err
}
}
}
if promptTokens == 0 && completionTokens == 0 {
promptTokens = estimateTokenCountForMessages(messages)
completionTokens = estimateTokenCount(responseBuilder.String())
}
if strings.TrimSpace(responseBuilder.String()) == "" {
return fmt.Errorf("Fireworks stream ended without content (model=%s, hasImage=%v, events=%d)", resolvedModel, hasImage, dataEvents)
}
if !enforceNoEmbeddedImageData && !reasoningDurationSent && reasoningStartTimeSet {
durationSeconds := float64(time.Now().UnixMilli()-reasoningStartTimestamp) / 1000.0
metaData := map[string]interface{}{
"meta": map[string]float64{
"thinkingSeconds": durationSeconds,
},
}
metaBytes, _ := json.Marshal(metaData)
fmt.Fprintf(w, "data: %s\n\n", metaBytes)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
reasoningDurationSent = true
}
if enforceNoEmbeddedImageData {
if reason, blocked := detectFireworksEmbeddedImagePayload(responseBuilder.String()); blocked {
return embeddedImagePayloadError(reason)
}
}
if seenFinishReason != "" {
fmt.Printf("[Fireworks] stream finish_reason=%s model=%s\n", seenFinishReason, resolvedModel)
}
if finishReasonLength {
fmt.Printf("[Fireworks WARN] stream reached max_tokens=%d model=%s\n", maxTokens, resolvedModel)
if enforceNoEmbeddedImageData {
return fmt.Errorf("Fireworks draw-to-code output was truncated at max_tokens=%d. Please regenerate with a shorter prompt.", maxTokens)
}
}
fmt.Printf("[Fireworks] stream complete model=%s hasImage=%v events=%d parseErrors=%d chars=%d\n",
resolvedModel, hasImage, dataEvents, parseErrors, responseBuilder.Len())
return emitFireworksStreamDone(w, promptTokens, completionTokens)
}
func fireworkContentToText(content interface{}) string {
switch v := content.(type) {
case string:
return v
case map[string]interface{}:
if text, ok := v["text"].(string); ok {
return text
}
if nested, ok := v["content"]; ok {
return fireworkContentToText(nested)
}
return ""
case []interface{}:
var builder strings.Builder
for _, part := range v {
partMap, ok := part.(map[string]interface{})
if !ok {
continue
}
partType, _ := partMap["type"].(string)
if (partType == "text" || partType == "output_text") && partMap["text"] != nil {
if text, ok := partMap["text"].(string); ok {
builder.WriteString(text)
continue
}
}
if text, ok := partMap["text"].(string); ok {
builder.WriteString(text)
continue
}
if nested, ok := partMap["content"]; ok {
builder.WriteString(fireworkContentToText(nested))
}
}
return builder.String()
default:
return ""
}
}
func estimateTokenCountForMessages(messages []map[string]interface{}) int {
totalChars := 0
for _, msg := range messages {
if content, ok := msg["content"]; ok {
totalChars += len(fireworkContentToText(content))
}
}
return estimateTokenCount(strings.Repeat("x", totalChars))
}
func estimateTokenCount(text string) int {
trimmed := strings.TrimSpace(text)
if trimmed == "" {
return 0
}
return (len(trimmed) + 3) / 4
}
func fireworksMessagesContainImage(messages []map[string]interface{}) bool {
for _, msg := range messages {
content, ok := msg["content"]
if !ok {
continue
}
parts, ok := content.([]interface{})
if !ok {
continue
}
for _, part := range parts {
partMap, ok := part.(map[string]interface{})
if !ok {
continue
}
if partType, _ := partMap["type"].(string); strings.EqualFold(partType, "image_url") {
return true
}
}
}
return false
}
func fireworksModelSupportsImageInput(modelID string) bool {
resolved := strings.ToLower(strings.TrimSpace(normalizeFireworksModelID(modelID)))
switch resolved {
case "accounts/fireworks/models/kimi-k2p5":
return true
default:
return false
}
}
func fireworksAttachmentDataURI(attachmentBase64, attachmentMime string) string {
trimmed := strings.TrimSpace(attachmentBase64)
if strings.HasPrefix(strings.ToLower(trimmed), "data:") {
return trimmed
}
mime := strings.TrimSpace(attachmentMime)
if mime == "" {
mime = "image/jpeg"
}
return fmt.Sprintf("data:%s;base64,%s", mime, trimmed)
}
func buildFireworksDrawToCodeMessages(imageBase64, userPrompt, template, imageSource, fireworksModel, apiKey string) []map[string]interface{} {
effectiveImageSource := imageSource
if !strings.EqualFold(strings.TrimSpace(effectiveImageSource), "Glowby Images") {
effectiveImageSource = "Glowby Images"
fmt.Printf("[Fireworks DEBUG] forcing imageSource to Glowby Images for safe placeholder output (was=%q)\n", imageSource)
}
resolvedModel := normalizeFireworksModelID(fireworksModel)
systemPrompt := getSystemPrompt(template, effectiveImageSource)
systemPrompt += " Replace @tailwind placeholders with the Tailwind CSS CDN link to load the real framework."
systemPrompt += " Glowby Images mode is strict: NEVER embed image bytes in output. NEVER output data: URIs, base64 blobs, blob: URLs, object URLs, or inline binary arrays."
systemPrompt += " Use glowbyimage:<prompt> placeholders ONLY. Every <img> must start with src='about:blank', then JavaScript assigns .src from a glowbyimage variable."
systemPrompt += " If you are uncertain about an image, create another descriptive glowbyimage:<prompt> placeholder instead of embedding bytes."
systemPrompt += " Return a COMPLETE, valid HTML document from <!DOCTYPE html> through </html>. Never truncate output."
systemPrompt += " Do NOT minify. Format output for humans with consistent indentation and spacing in CSS/JS (example: transition: all 0.3s ease; and color stops like #1a1a2e 0%)."
systemPrompt += " Never put JavaScript code on the same line as // comments. If comments are used, they must be standalone lines."
systemPrompt += " Glowby image wiring contract: in one plain <script> block, first declare const image variables (each starting with glowbyimage:), then assign each to its matching img id via document.getElementById('...').src = ...."
systemPrompt += " The image wiring script must contain ONLY: const image declarations and document.getElementById(...).src assignments. No other logic, no function definitions, no event handlers, no extra words."
systemPrompt += " Put interactive JavaScript in a separate second <script> block so image assignment always works even if other logic has issues."
systemPrompt += " Do not put declarations or assignments on the same line as comments. Keep one statement per line."
systemPrompt += " Leave one blank line between major HTML sections so the output stays readable in code view."
systemPrompt += " JavaScript must be syntactically valid. Never emit stray tokens before declarations (for example: 'functionality const')."
systemPrompt += " Validate that every glowbyimage variable is assigned to an existing img id via document.getElementById(...).src before finishing."
detailedTask := buildDetailedTaskDescription(template, effectiveImageSource, userPrompt)
lines := []string{
fmt.Sprintf("Detailed Task Description: %s", detailedTask),
fmt.Sprintf("Human: %s", userPrompt),
}
usedImageDescription := false
imageDescriptionSource := "none"
useNativeImageInput := false
if strings.TrimSpace(imageBase64) != "" {
if fireworksModelSupportsImageInput(resolvedModel) {
useNativeImageInput = true
fmt.Printf("[Fireworks DEBUG] draw received image payload (base64_len=%d), using native image_url input for model=%s\n", len(imageBase64), resolvedModel)
} else {
fmt.Printf("[Fireworks DEBUG] draw received image payload (base64_len=%d), deriving image description via Fireworks Kimi K2.5\n", len(imageBase64))
desc, err := describeImageWithFireworksKimi(imageBase64, apiKey)
if err != nil {
// Keep legacy fallback so GLM-5 draw-to-code doesn't hard-fail if Kimi vision is temporarily unavailable.
fmt.Printf("[Fireworks WARN] Kimi image description unavailable; falling back to Ollama llama3.2-vision: %v\n", err)
desc, err = getImageDescription(imageBase64)
if err != nil {
// Models like GLM-5 reject image_url on this endpoint.
// Continue with text-only context instead of failing the whole request.
fmt.Printf("[Fireworks WARN] image description unavailable; proceeding text-only: %v\n", err)
} else if trimmed := strings.TrimSpace(desc); trimmed != "" {
usedImageDescription = true
imageDescriptionSource = "ollama-llama3.2-vision"
fmt.Printf("[Fireworks DEBUG] draw image description (%s) >>>\n%s\n<<< END image description\n", imageDescriptionSource, trimmed)
lines = append([]string{fmt.Sprintf("Image description: %s", trimmed)}, lines...)
}
} else if trimmed := strings.TrimSpace(desc); trimmed != "" {
usedImageDescription = true
imageDescriptionSource = "fireworks-kimi-k2p5"
fmt.Printf("[Fireworks DEBUG] draw image description (%s) >>>\n%s\n<<< END image description\n", imageDescriptionSource, trimmed)
lines = append([]string{fmt.Sprintf("Image description: %s", trimmed)}, lines...)
}
}
}
userContent := strings.Join(lines, "\n")
fmt.Printf("[Fireworks DEBUG] draw prompt usesImageDescription=%v imageDescriptionSource=%s nativeImageInput=%v\n", usedImageDescription, imageDescriptionSource, useNativeImageInput)
fmt.Printf("[Fireworks DEBUG] draw system prompt >>>\n%s\n<<< END system prompt\n", systemPrompt)
fmt.Printf("[Fireworks DEBUG] draw user prompt >>>\n%s\n<<< END user prompt\n", userContent)
finalUserContent := interface{}(userContent)
if useNativeImageInput {
finalUserContent = []interface{}{
map[string]interface{}{
"type": "text",
"text": userContent,
},
map[string]interface{}{
"type": "image_url",
"image_url": map[string]interface{}{
"url": fireworksAttachmentDataURI(imageBase64, "image/jpeg"),
},
},
}
}
return []map[string]interface{}{
{
"role": "system",
"content": systemPrompt,
},
{
"role": "user",
"content": finalUserContent,
},
}
}
func describeImageWithFireworksKimi(imageBase64, apiKey string) (string, error) {
if strings.TrimSpace(apiKey) == "" {
return "", fmt.Errorf("missing Fireworks API key")
}
if strings.TrimSpace(imageBase64) == "" {
return "", fmt.Errorf("missing image payload")
}
const visionModel = "accounts/fireworks/models/kimi-k2p5"
payload := map[string]interface{}{
"model": visionModel,
"max_tokens": 700,
"top_p": 1,
"top_k": 40,
"presence_penalty": 0,
"frequency_penalty": 0,
"temperature": 0.2,
"messages": []map[string]interface{}{
{
"role": "system",
"content": "Describe the image clearly in plain English. " +
"Return only the description text. Do not use markdown, XML, JSON, or <think> tags.",
},
{
"role": "user",
"content": []map[string]interface{}{
{
"type": "text",
"text": "Describe this image in human language. " +
"If text appears in a foreign language, keep it as-is and mention it. " +
"Include people, objects, setting, style, and notable details.",
},
{
"type": "image_url",
"image_url": map[string]interface{}{
"url": fireworksAttachmentDataURI(imageBase64, "image/jpeg"),
},
},
},
},
},
}
jsonBytes, err := json.Marshal(payload)
if err != nil {
return "", err
}
req, err := http.NewRequest("POST", fireworksChatCompletionsURL, bytes.NewReader(jsonBytes))
if err != nil {
return "", err
}
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+apiKey)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return "", fmt.Errorf("Fireworks Kimi vision API error (%d): %s", resp.StatusCode, strings.TrimSpace(string(body)))
}
var completion struct {
Choices []struct {
Message struct {
Content interface{} `json:"content"`
} `json:"message"`
} `json:"choices"`
}
if err := json.NewDecoder(resp.Body).Decode(&completion); err != nil {
return "", fmt.Errorf("failed to parse Fireworks Kimi vision response: %w", err)
}
if len(completion.Choices) == 0 {
return "", fmt.Errorf("Fireworks Kimi vision returned no choices")
}
description := strings.TrimSpace(fireworkContentToText(completion.Choices[0].Message.Content))
description = fireworksThinkTagRegex.ReplaceAllString(description, "")
description = strings.TrimSpace(description)
if description == "" {
return "", fmt.Errorf("Fireworks Kimi vision returned empty description")
}
return description, nil
}
func emitFireworksStreamChunk(w http.ResponseWriter, content string) error {
eventData, err := json.Marshal(map[string]string{"chunk": content})
if err != nil {
return err
}
if _, err := fmt.Fprintf(w, "data: %s\n\n", eventData); err != nil {
return err
}
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
return nil
}
func emitFireworksStreamDone(w http.ResponseWriter, promptTokens, completionTokens int) error {
doneData := map[string]interface{}{
"done": true,
"tokenUsage": map[string]int{
"inputTokens": promptTokens,
"outputTokens": completionTokens,
"totalTokens": promptTokens + completionTokens,
},
"cost": 0.0,
}
doneBytes, err := json.Marshal(doneData)
if err != nil {
return err
}
if _, err := fmt.Fprintf(w, "data: %s\n\n", doneBytes); err != nil {
return err
}
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
return nil
}
func logFireworksRequestSummary(model string, stream bool, hasImage bool, maxTokens int, messages []map[string]interface{}) {
promptChars := 0
for _, message := range messages {
content, ok := message["content"]
if !ok {
continue
}
promptChars += len(fireworkContentToText(content))
}
preview := truncateForLog(fireworksLatestUserPreview(messages))
fmt.Printf("[Fireworks] request model=%s stream=%v hasImage=%v max_tokens=%d messages=%d prompt_chars=%d preview=%q\n",
model, stream, hasImage, maxTokens, len(messages), promptChars, preview)
}
func fireworksLatestUserPreview(messages []map[string]interface{}) string {
for i := len(messages) - 1; i >= 0; i-- {
role, _ := messages[i]["role"].(string)
if strings.ToLower(strings.TrimSpace(role)) != "user" {
continue
}
content := fireworkContentToText(messages[i]["content"])
if strings.TrimSpace(content) != "" {
return content
}
}
return ""
}
func normalizeFinishReason(reason interface{}) string {
switch v := reason.(type) {
case string:
return strings.TrimSpace(v)
default:
return ""
}
}
func detectFireworksEmbeddedImagePayload(content string) (string, bool) {
trimmed := strings.TrimSpace(content)
if trimmed == "" {
return "", false
}
lower := strings.ToLower(trimmed)
if strings.Contains(lower, "data:image/") && strings.Contains(lower, ";base64,") {
return "data:image base64 URI", true
}
if fireworksGenericDataURIRegex.MatchString(trimmed) {
return "generic data URI with base64 payload", true
}
if fireworksImgSrcBase64Regex.MatchString(trimmed) {
return "img src contains inline base64 payload", true
}
if fireworksImageSigBase64Regex.MatchString(trimmed) {
return "quoted base64 image-signature payload", true
}
if strings.Contains(lower, "base64") && fireworksLongBase64RunRegex.MatchString(trimmed) {
return "long base64-like payload", true
}
return "", false
}
func embeddedImagePayloadError(reason string) error {
return fmt.Errorf("generation blocked: model returned embedded image payload (%s). Regenerate using glowbyimage placeholders only", reason)
}
func normalizeFireworksDrawToCodeOutput(content string) string {
normalized := strings.TrimSpace(content)
if normalized == "" {
return normalized
}
// Common GLM-5 compacting artifacts that break JS/CSS execution/readability.
normalized = compactCommentCodeRegex.ReplaceAllString(normalized, "$1\n$2")
normalized = hexColorPercentCompactRegex.ReplaceAllString(normalized, "$1 $2")
normalized = transitionAllCompactRegex.ReplaceAllString(normalized, "transition: all $1")
normalized = animationDurationCompactRegex.ReplaceAllString(normalized, "animation: $1 $2")
normalized = boxShadowDoubleZeroCompact.ReplaceAllString(normalized, "box-shadow: 0 0 $1")
normalized = boxShadowCompactTwoOffsetRegex.ReplaceAllString(normalized, "box-shadow: 0 $1 $2")
normalized = fireworksFunctionalityConstRE.ReplaceAllString(normalized, "const")
normalized = strings.ReplaceAll(normalized, "0%,100%", "0%, 100%")
normalized = strings.ReplaceAll(normalized, "viewBox=\"002424\"", "viewBox=\"0 0 24 24\"")
normalized = strings.ReplaceAll(normalized, "viewBox=\"002020\"", "viewBox=\"0 0 20 20\"")
normalized = strings.ReplaceAll(normalized, "rootMargin: '0px0px -50px0px'", "rootMargin: '0px 0px -50px 0px'")
return normalized
}
func extractFireworksThinkingStepTitle(raw string) string {
trimmed := strings.TrimSpace(raw)
if trimmed == "" {
return ""
}
lines := strings.Split(trimmed, "\n")
for _, line := range lines {
candidate := strings.TrimSpace(line)
if candidate == "" {
continue
}
// Convert "1. Analyze the input: ..." -> "Analyze the input"
if dot := strings.Index(candidate, ". "); dot > 0 {
prefix := candidate[:dot]
isNumber := true
for _, ch := range prefix {
if ch < '0' || ch > '9' {
isNumber = false
break
}
}
if isNumber {
candidate = strings.TrimSpace(candidate[dot+2:])
}
}
if colon := strings.Index(candidate, ":"); colon > 0 {
candidate = strings.TrimSpace(candidate[:colon])
}
if len(candidate) > 96 {
candidate = strings.TrimSpace(candidate[:96]) + "..."
}
return candidate
}
return ""
}
func isMeaningfulThinkingStepTitle(title string) bool {
trimmed := strings.TrimSpace(title)
if trimmed == "" {
return false
}
if strings.EqualFold(trimmed, "thinking...") {
return false
}
// Avoid tiny first-token fragments like "The" while stream is still warming up.
if len(trimmed) < 10 {
return false
}
if !strings.Contains(trimmed, " ") {
return false
}
return true
}
================================================
FILE: backend/gemini_google.go
================================================
package main
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"io"
"math"
"net/http"
"strings"
"time"
)
func isGeminiModelNotFound(statusCode int, body string) bool {
if statusCode != http.StatusNotFound {
return false
}
lower := strings.ToLower(body)
return strings.Contains(lower, "models/") && strings.Contains(lower, "not found")
}
// callGeminiDrawToCodeApiFull handles draw-to-code using Gemini with native vision
// Supports optional attachment (audio/file) inline_data in addition to the image.
func callGeminiDrawToCodeApiFull(imageBase64, userPrompt, template, imageSource, apiKey string, attachmentBase64 string, attachmentMime string, geminiModel string) (*R1Response, error) {
if apiKey == "" {
return &R1Response{
AIResponse: "No Gemini API key provided. Please add your key in Settings.",
TokenUsage: map[string]int{"inputTokens": 0, "outputTokens": 0, "totalTokens": 0},
Cost: 0,
}, nil
}
// 1) Build system prompt
systemPrompt := getSystemPrompt(template, imageSource)
systemPrompt += " Replace @tailwind placeholders with the Tailwind CSS CDN link to load the real framework."
// 2) Build detailed task
detailedTask := buildDetailedTaskDescription(template, imageSource, userPrompt)
// 3) Construct Gemini request with vision
// Gemini uses "contents" array with "role" and "parts"
contents := []map[string]interface{}{
{
"role": "user",
"parts": func() []interface{} {
parts := []interface{}{
map[string]interface{}{
"text": systemPrompt + "\n\n" + detailedTask,
},
}
if strings.TrimSpace(imageBase64) != "" {
parts = append(parts, map[string]interface{}{
"inline_data": map[string]interface{}{
"mime_type": "image/jpeg",
"data": imageBase64,
},
})
}
if strings.TrimSpace(attachmentBase64) != "" && strings.TrimSpace(attachmentMime) != "" {
parts = append(parts, map[string]interface{}{
"inline_data": map[string]interface{}{
"mime_type": attachmentMime,
"data": attachmentBase64,
},
})
}
return parts
}(),
},
}
// 4) Call Gemini API with a larger output budget to avoid truncated long-form code generations.
aiResp, inputTokens, outputTokens, err := callGeminiAPI(contents, apiKey, 32768, "", geminiModel)
if err != nil {
return nil, err
}
// 5) Calculate cost (Gemini 3.1 Pro pricing)
// Input: $1.25 per 1M tokens, Output: $5 per 1M tokens
cost := (float64(inputTokens) / 1_000_000.0 * 1.25) + (float64(outputTokens) / 1_000_000.0 * 5.0)
return &R1Response{
AIResponse: aiResp,
TokenUsage: map[string]int{
"inputTokens": inputTokens,
"outputTokens": outputTokens,
"totalTokens": inputTokens + outputTokens,
},
Cost: cost,
}, nil
}
// callGeminiDrawToCodeStreaming handles draw-to-code with streaming for Gemini.
func callGeminiDrawToCodeStreaming(w http.ResponseWriter, imageBase64, userPrompt, template, imageSource, apiKey, attachmentBase64, attachmentMime, geminiModel string) error {
if apiKey == "" {
return fmt.Errorf("no Gemini API key provided")
}
// DEBUG: Log all input parameters for translation debugging
fmt.Println("\n" + strings.Repeat("=", 80))
fmt.Println("[GEMINI DEBUG] callGeminiDrawToCodeStreaming called")
fmt.Println(strings.Repeat("=", 80))
fmt.Printf("[GEMINI DEBUG] template: %q\n", template)
fmt.Printf("[GEMINI DEBUG] imageSource: %q\n", imageSource)
fmt.Printf("[GEMINI DEBUG] imageBase64 length: %d bytes (not printed)\n", len(imageBase64))
fmt.Printf("[GEMINI DEBUG] attachmentBase64 length: %d bytes (not printed)\n", len(attachmentBase64))
fmt.Printf("[GEMINI DEBUG] attachmentMime: %q\n", attachmentMime)
fmt.Println(strings.Repeat("-", 80))
fmt.Println("[GEMINI DEBUG] userPrompt (truncated to 2000 chars):")
if len(userPrompt) > 2000 {
fmt.Println(userPrompt[:2000] + "\n... [TRUNCATED, total length: " + fmt.Sprintf("%d", len(userPrompt)) + " chars]")
} else {
fmt.Println(userPrompt)
}
fmt.Println(strings.Repeat("-", 80))
// Build system prompt
systemPrompt := getSystemPrompt(template, imageSource)
systemPrompt += " Replace @tailwind placeholders with the Tailwind CSS CDN link to load the real framework."
// Build detailed task
detailedTask := buildDetailedTaskDescription(template, imageSource, userPrompt)
// DEBUG: Log the constructed prompts (truncated)
fmt.Println("[GEMINI DEBUG] systemPrompt length:", len(systemPrompt), "chars")
fmt.Println("[GEMINI DEBUG] detailedTask length:", len(detailedTask), "chars")
fmt.Println(strings.Repeat("-", 80))
finalPrompt := systemPrompt + "\n\n" + detailedTask
fmt.Println("[GEMINI DEBUG] FINAL PROMPT TO GEMINI (truncated to 3000 chars):")
if len(finalPrompt) > 3000 {
fmt.Println(finalPrompt[:3000] + "\n... [TRUNCATED, total length: " + fmt.Sprintf("%d", len(finalPrompt)) + " chars]")
} else {
fmt.Println(finalPrompt)
}
fmt.Println(strings.Repeat("=", 80) + "\n")
// Construct Gemini request with vision
contents := []map[string]interface{}{
{
"role": "user",
"parts": func() []interface{} {
parts := []interface{}{
map[string]interface{}{
"text": systemPrompt + "\n\n" + detailedTask,
},
}
if strings.TrimSpace(imageBase64) != "" {
parts = append(parts, map[string]interface{}{
"inline_data": map[string]interface{}{
"mime_type": "image/jpeg",
"data": imageBase64,
},
})
}
if strings.TrimSpace(attachmentBase64) != "" && strings.TrimSpace(attachmentMime) != "" {
parts = append(parts, map[string]interface{}{
"inline_data": map[string]interface{}{
"mime_type": attachmentMime,
"data": attachmentBase64,
},
})
}
return parts
}(),
},
}
// Call existing streaming function
// Match non-streaming output budget to reduce partial/truncated translated files.
return callGeminiAPIStreaming(w, contents, apiKey, 32768, "", geminiModel)
}
// callGeminiApiGo handles normal chat using Gemini.
func callGeminiApiGo(prevMsgs []ChatMessage, newMsg, apiKey, geminiModel string) (*R1Response, error) {
if apiKey == "" {
return &R1Response{
AIResponse: "No Gemini API key provided. Please add your key in Settings.",
TokenUsage: map[string]int{},
Cost: 0,
}, nil
}
// Gather system message and convert to Gemini format
systemMsg := defaultSystemPrompt
var contents []map[string]interface{}
for _, m := range prevMsgs {
if m.Role == "system" {
systemMsg = m.Content
}
}
// Add system message as first user message (Gemini doesn't have separate system role)
hasUserMsg := false
for _, m := range prevMsgs {
if m.Role == "user" || m.Role == "assistant" {
hasUserMsg = true
break
}
}
if hasUserMsg {
// Add system prompt as first user turn
contents = append(contents, map[string]interface{}{
"role": "user",
"parts": []interface{}{
map[string]interface{}{"text": systemMsg},
},
})
// Add a model response acknowledging the system message
contents = append(contents, map[string]interface{}{
"role": "model",
"parts": []interface{}{
map[string]interface{}{"text": "Understood. I'll follow these instructions."},
},
})
}
// Add conversation history (convert "assistant" to "model" for Gemini)
for _, m := range prevMsgs {
if m.Role == "user" {
contents = append(contents, map[string]interface{}{
"role": "user",
"parts": []interface{}{
map[string]interface{}{"text": m.Content},
},
})
} else if m.Role == "assistant" {
contents = append(contents, map[string]interface{}{
"role": "model",
"parts": []interface{}{
map[string]interface{}{"text": m.Content},
},
})
}
}
// Add new user message
contents = append(contents, map[string]interface{}{
"role": "user",
"parts": []interface{}{
map[string]interface{}{"text": newMsg},
},
})
// Call Gemini API
aiResp, inputTokens, outputTokens, err := callGeminiAPI(contents, apiKey, 12000, "", geminiModel)
if err != nil {
return nil, err
}
// Calculate cost
cost := (float64(inputTokens) / 1_000_000.0 * 1.25) + (float64(outputTokens) / 1_000_000.0 * 5.0)
return &R1Response{
AIResponse: aiResp,
TokenUsage: map[string]int{
"inputTokens": inputTokens,
"outputTokens": outputTokens,
"totalTokens": inputTokens + outputTokens,
},
Cost: cost,
}, nil
}
// callGeminiAPI makes the actual HTTP request to Gemini API (non-streaming).
func callGeminiAPI(contents []map[string]interface{}, apiKey string, maxTokens int, thinkingBudget string, geminiModel string) (string, int, int, error) {
// Build generation config with thinking config
thinkingCfg := map[string]interface{}{
"thinkingBudget": -1, // -1 means dynamic budget (default)
"includeThoughts": true, // Include thought summaries in response
}
// Set thinking budget if provided
if thinkingBudget != "" {
thinkingCfg["thinkingBudget"] = thinkingBudget
}
generationConfig := map[string]interface{}{
"maxOutputTokens": maxTokens,
"temperature": 1.0,
"thinkingConfig": thinkingCfg,
}
reqBody := map[string]interface{}{
"contents": contents,
"generationConfig": generationConfig,
}
jsonBytes, _ := json.Marshal(reqBody)
resolvedModel := normalizeGeminiModelID(geminiModel)
url := fmt.Sprintf("https://generativelanguage.googleapis.com/v1beta/models/%s:generateContent?key=%s", resolvedModel, apiKey)
req, err := http.NewRequest("POST", url, bytes.NewReader(jsonBytes))
if err != nil {
return "", 0, 0, err
}
req.Header.Set("Content-Type", "application/json")
fmt.Printf("[DEBUG] Calling Gemini API model=%s...\n", resolvedModel)
// Request body not printed (contains large base64 images)
resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Printf("[Gemini ERROR] HTTP request failed: %v\n", err)
return "", 0, 0, err
}
defer resp.Body.Close()
fmt.Printf("[DEBUG] Gemini API response status: %d\n", resp.StatusCode)
if resp.StatusCode != 200 {
b, _ := io.ReadAll(resp.Body)
bodyText := string(b)
if isGeminiModelNotFound(resp.StatusCode, bodyText) && resolvedModel != "gemini-3.1-pro-preview" {
fmt.Printf("[Gemini WARN] Model %s not available, retrying with gemini-3.1-pro-preview\n", resolvedModel)
return callGeminiAPI(contents, apiKey, maxTokens, thinkingBudget, "gemini-3.1-pro-preview")
}
errMsg := fmt.Sprintf("Gemini API error (%d): %s", resp.StatusCode, bodyText)
fmt.Println("[Gemini ERROR]", errMsg)
return "", 0, 0, fmt.Errorf("%s", errMsg)
}
// Parse response
var geminiResp struct {
Candidates []struct {
Content struct {
Parts []map[string]interface{} `json:"parts"`
} `json:"content"`
} `json:"candidates"`
UsageMetadata struct {
PromptTokenCount int `json:"promptTokenCount"`
CandidatesTokenCount int `json:"candidatesTokenCount"`
TotalTokenCount int `json:"totalTokenCount"`
} `json:"usageMetadata"`
}
bodyBytes, _ := io.ReadAll(resp.Body)
fmt.Printf("[DEBUG] Gemini raw response: %s\n", string(bodyBytes))
if err := json.Unmarshal(bodyBytes, &geminiResp); err != nil {
return "", 0, 0, fmt.Errorf("failed to parse Gemini response: %w", err)
}
if len(geminiResp.Candidates) == 0 {
return "", 0, 0, fmt.Errorf("no candidates in Gemini response")
}
// Extract text and thoughts
// Gemini 3: thought can be a boolean (true) or string
// When thought is true, the text field contains thinking content
// When thought is false/absent, the text field contains the actual response
var responseText strings.Builder
var thinking strings.Builder
for _, part := range geminiResp.Candidates[0].Content.Parts {
// Check if this part is a thought
// Gemini 3: thought can be boolean true or a string
// When thought is true, the text field contains thinking content
isThought := false
if thoughtVal, hasThought := part["thought"]; hasThought {
if thoughtBool, ok := thoughtVal.(bool); ok && thoughtBool {
isThought = true
} else if thoughtStr, ok := thoughtVal.(string); ok && thoughtStr != "" {
// Legacy: if thought is a non-empty string, treat as thought
isThought = true
}
}
// Extract text field
if textVal, hasText := part["text"]; hasText {
if textStr, ok := textVal.(string); ok && textStr != "" {
if isThought {
// In Gemini 3, when thought is true, text contains the thinking
thinking.WriteString(textStr)
} else {
// Otherwise, text contains the actual response
responseText.WriteString(textStr)
}
}
}
}
// Prepend thinking wrapped in <think> tags if present
fullResponse := responseText.String()
if thinking.Len() > 0 {
fullResponse = "<think>" + thinking.String() + "</think>" + fullResponse
}
inputTokens := geminiResp.UsageMetadata.PromptTokenCount
outputTokens := geminiResp.UsageMetadata.CandidatesTokenCount
fmt.Printf("[DEBUG] Gemini response: %d input tokens, %d output tokens\n", inputTokens, outputTokens)
return fullResponse, inputTokens, outputTokens, nil
}
// getGeminiMagicEditTool returns the tool definition for magic_edit in Gemini format
func getGeminiMagicEditTool() map[string]interface{} {
return map[string]interface{}{
"function_declarations": []map[string]interface{}{
{
"name": "magic_edit",
"description": "Trigger a visual code edit on the currently loaded project. Use this when the user asks you to make visual or code changes to their project (like changing colors, adding elements, modifying layouts, etc.).",
"parameters": map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"edit_description": map[string]interface{}{
"type": "string",
"description": "A clear, specific description of what changes to make to the code/design. Be detailed about colors, positions, sizes, etc.",
},
},
"required": []string{"edit_description"},
},
},
},
}
}
// callGeminiChatStreaming streams Gemini response via SSE to the writer (for chat)
func callGeminiChatStreaming(w http.ResponseWriter, prevMsgs []ChatMessage, newMsg, apiKey string, attachmentBase64 string, attachmentMime string, geminiModel string) error {
return callGeminiChatStreamingWithTools(w, prevMsgs, newMsg, apiKey, attachmentBase64, attachmentMime, nil, geminiModel)
}
// callGeminiChatStreamingWithTools streams Gemini response with optional tools
func callGeminiChatStreamingWithTools(w http.ResponseWriter, prevMsgs []ChatMessage, newMsg, apiKey string, attachmentBase64 string, attachmentMime string, tools []map[string]interface{}, geminiModel string) error {
if apiKey == "" {
return fmt.Errorf("no Gemini API key provided")
}
// Gather system message and convert to Gemini format
systemMsg := defaultSystemPrompt
var contents []map[string]interface{}
for _, m := range prevMsgs {
if m.Role == "system" {
systemMsg = m.Content
}
}
// Add system message as first user message
hasUserMsg := false
for _, m := range prevMsgs {
if m.Role == "user" || m.Role == "assistant" {
hasUserMsg = true
break
}
}
if hasUserMsg {
contents = append(contents, map[string]interface{}{
"role": "user",
"parts": []interface{}{
map[string]interface{}{"text": systemMsg},
},
})
contents = append(contents, map[string]interface{}{
"role": "model",
"parts": []interface{}{
map[string]interface{}{"text": "Understood. I'll follow these instructions."},
},
})
}
// Add conversation history
for _, m := range prevMsgs {
if m.Role == "user" {
contents = append(contents, map[string]interface{}{
"role": "user",
"parts": []interface{}{
map[string]interface{}{"text": m.Content},
},
})
} else if m.Role == "assistant" {
contents = append(contents, map[string]interface{}{
"role": "model",
"parts": []interface{}{
map[string]interface{}{"text": m.Content},
},
})
}
}
// Add new user message
parts := []interface{}{
map[string]interface{}{"text": newMsg},
}
if strings.TrimSpace(attachmentBase64) != "" && strings.TrimSpace(attachmentMime) != "" {
parts = append(parts, map[string]interface{}{
"inline_data": map[string]interface{}{
"mime_type": attachmentMime,
"data": attachmentBase64,
},
})
}
contents = append(contents, map[string]interface{}{
"role": "user",
"parts": parts,
})
// Call streaming Gemini API with optional tools
return callGeminiAPIStreamingWithTools(w, contents, apiKey, 12000, "", tools, geminiModel)
}
// callGeminiAPIStreaming streams Gemini responses via SSE directly to the HTTP response writer
func callGeminiAPIStreaming(w http.ResponseWriter, contents []map[string]interface{}, apiKey string, maxTokens int, thinkingBudget string, geminiModel string) error {
return callGeminiAPIStreamingWithTools(w, contents, apiKey, maxTokens, thinkingBudget, nil, geminiModel)
}
// callGeminiAPIStreamingWithTools streams Gemini responses with optional tools support
func callGeminiAPIStreamingWithTools(w http.ResponseWriter, contents []map[string]interface{}, apiKey string, maxTokens int, thinkingBudget string, tools []map[string]interface{}, geminiModel string) error {
// Build generation config with thinking config
thinkingCfg := map[string]interface{}{
"thinkingBudget": -1, // -1 means dynamic budget (default)
"includeThoughts": true, // Include thought summaries in response
}
// Set thinking budget if provided
if thinkingBudget != "" {
thinkingCfg["thinkingBudget"] = thinkingBudget
}
generationConfig := map[string]interface{}{
"maxOutputTokens": maxTokens,
"temperature": 1.0,
"thinkingConfig": thinkingCfg,
}
reqBody := map[string]interface{}{
"contents": contents,
"generationConfig": generationConfig,
}
// Add tools if provided
if len(tools) > 0 {
reqBody["tools"] = tools
}
jsonBytes, _ := json.Marshal(reqBody)
resolvedModel := normalizeGeminiModelID(geminiModel)
url := fmt.Sprintf("https://generativelanguage.googleapis.com/v1beta/models/%s:streamGenerateContent?alt=sse&key=%s", resolvedModel, apiKey)
req, err := http.NewRequest("POST", url, bytes.NewReader(jsonBytes))
if err != nil {
fmt.Printf("[Gemini ERROR] Creating request: %v\n", err)
return err
}
req.Header.Set("Content-Type", "application/json")
fmt.Printf("[DEBUG] Calling Gemini API with streaming (SSE) model=%s...\n", resolvedModel)
// Request body not printed (contains large base64 images)
resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Printf("[Gemini ERROR] HTTP request failed: %v\n", err)
return err
}
defer resp.Body.Close()
fmt.Printf("[DEBUG] Gemini API response status: %d\n", resp.StatusCode)
if resp.StatusCode != 200 {
b, _ := io.ReadAll(resp.Body)
bodyText := string(b)
if isGeminiModelNotFound(resp.StatusCode, bodyText) && resolvedModel != "gemini-3.1-pro-preview" {
fmt.Printf("[Gemini WARN] Model %s not available for streaming, retrying with gemini-3.1-pro-preview\n", resolvedModel)
return callGeminiAPIStreamingWithTools(w, contents, apiKey, maxTokens, thinkingBudget, tools, "gemini-3.1-pro-preview")
}
errMsg := fmt.Sprintf("Gemini API error (%d): %s", resp.StatusCode, bodyText)
fmt.Println("[Gemini ERROR]", errMsg)
return fmt.Errorf("%s", errMsg)
}
// Stream response chunks to client
var responseBuilder strings.Builder
var thinkingBuilder strings.Builder
var thinkingSent bool
var thinkingStart time.Time
var thinkingDurationSent bool
var inputTokens, outputTokens int
reader := bufio.NewReader(resp.Body)
for {
line, err := reader.ReadString('\n')
if len(line) > 0 {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "data:") {
dataStr := strings.TrimSpace(line[5:])
// Skip empty data lines
if dataStr == "" {
continue
}
var chunk struct {
Candidates []struct {
Content struct {
Parts []map[string]interface{} `json:"parts"`
} `json:"content"`
} `json:"candidates"`
UsageMetadata struct {
PromptTokenCount int `json:"promptTokenCount"`
CandidatesTokenCount int `json:"candidatesTokenCount"`
} `json:"usageMetadata"`
}
if e := json.Unmarshal([]byte(dataStr), &chunk); e == nil {
// Debug: print the raw chunk to see structure
fmt.Printf("[Gemini DEBUG] Raw chunk: %s\n", dataStr)
if len(chunk.Candidates) > 0 {
for _, part := range chunk.Candidates[0].Content.Parts {
// Check if this part is a thought (has "thought" key and it's true/string)
if thoughtVal, hasThought := part["thought"]; hasThought {
// Could be a boolean flag or a string content
var thoughtText string
// If thought is true (boolean), use text field
if thoughtBool, ok := thoughtVal.(bool); ok && thoughtBool {
if textVal, hasText := part["text"]; hasText {
thoughtText = textVal.(string)
}
} else if thoughtStr, ok := thoughtVal.(string); ok {
// If thought is a string, use it directly
thoughtText = thoughtStr
}
if thoughtText != "" {
if thinkingStart.IsZero() {
thinkingStart = time.Now()
}
fmt.Printf("[Gemini DEBUG] Found thought: %s\n", thoughtText)
cleanThought := strings.Trim(thoughtText, "\n")
firstLineSource := strings.TrimSpace(cleanThought)
if firstLineSource != "" {
// Preserve the full thought (including newlines) for the final <think> block
if thinkingBuilder.Len() > 0 {
thinkingBuilder.WriteString("\n\n")
}
thinkingBuilder.WriteString(cleanThought)
// Send only the first line (e.g., "Framing the Response") as the visible thinking step
firstLine := firstLineSource
if idx := strings.Index(firstLine, "\n"); idx != -1 {
firstLine = firstLine[:idx]
}
firstLine = strings.TrimSpace(firstLine)
firstLine = strings.Trim(firstLine, "* ")
if firstLine != "" {
thinkingStepData := map[string]string{"thinkingStep": firstLine}
thinkingStepBytes, _ := json.Marshal(thinkingStepData)
fmt.Fprintf(w, "data: %s\n\n", thinkingStepBytes)
fmt.Println("[Gemini] Sending thinking step:", firstLine)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
}
}
}
}
// Handle function call (tool use)
if funcCall, hasFuncCall := part["functionCall"]; hasFuncCall {
if fcMap, ok := funcCall.(map[string]interface{}); ok {
funcName, _ := fcMap["name"].(string)
funcArgs, _ := fcMap["args"].(map[string]interface{})
fmt.Printf("[Gemini] Function call detected: %s, args: %v\n", funcName, funcArgs)
// Convert args to JSON string for consistency with Claude
argsJSON, _ := json.Marshal(funcArgs)
// Send tool_use event to client (same format as Claude)
toolUseData := map[string]interface{}{
"tool_use": map[string]interface{}{
"id": fmt.Sprintf("gemini-%d", time.Now().UnixNano()), // Generate unique ID
"name": funcName,
"input": string(argsJSON),
},
}
toolUseBytes, _ := json.Marshal(toolUseData)
fmt.Fprintf(w, "data: %s\n\n", toolUseBytes)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
}
}
// Handle text content (only if not a thought)
if textVal, hasText := part["text"]; hasText {
// Skip if this is a thought part (thought is true boolean or non-empty string)
isThoughtPart := false
if thoughtVal, hasThought := part["thought"]; hasThought {
if thoughtBool, ok := thoughtVal.(bool); ok && thoughtBool {
isThoughtPart = true
} else if thoughtStr, ok := thoughtVal.(string); ok && thoughtStr != "" {
isThoughtPart = true
}
}
if isThoughtPart {
continue
}
content := textVal.(string)
responseBuilder.WriteString(content)
// Send accumulated thinking before first content chunk
if !thinkingSent && thinkingBuilder.Len() > 0 {
thinkingText := "<think>" + thinkingBuilder.String() + "</think>"
chunkData := map[string]string{"chunk": thinkingText}
chunkBytes, _ := json.Marshal(chunkData)
fmt.Fprintf(w, "data: %s\n\n", chunkBytes)
fmt.Println("[Gemini] Sending thinking:", thinkingBuilder.String())
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
thinkingSent = true
}
// Send content chunk
chunkData := map[string]string{"chunk": content}
chunkBytes, _ := json.Marshal(chunkData)
fmt.Fprintf(w, "data: %s\n\n", chunkBytes)
fmt.Println("[Gemini] Sending chunk:", content)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
// Send thinking duration after first content
if !thinkingDurationSent && !thinkingStart.IsZero() {
duration := time.Since(thinkingStart).Seconds()
duration = math.Round(duration*10) / 10
metaData := map[string]any{
"meta": map[string]any{
"thinkingSeconds": duration,
},
}
metaBytes, _ := json.Marshal(metaData)
fmt.Fprintf(w, "data: %s\n\n", metaBytes)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
thinkingDurationSent = true
}
}
}
}
// Extract usage metadata (just store it, don't send done yet)
if chunk.UsageMetadata.PromptTokenCount > 0 {
inputTokens = chunk.UsageMetadata.PromptTokenCount
outputTokens = chunk.UsageMetadata.CandidatesTokenCount
}
}
}
}
if err != nil {
if err == io.EOF {
break
}
return err
}
}
// Send thinking duration if not sent yet
if !thinkingDurationSent && !thinkingStart.IsZero() {
duration := time.Since(thinkingStart).Seconds()
duration = math.Round(duration*10) / 10
metaData := map[string]any{
"meta": map[string]any{
"thinkingSeconds": duration,
},
}
metaBytes, _ := json.Marshal(metaData)
fmt.Fprintf(w, "data: %s\n\n", metaBytes)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
}
// Calculate cost (Gemini 3.1 Pro: $1.25 per 1M input, $5 per 1M output)
cost := (float64(inputTokens) / 1_000_000.0 * 1.25) + (float64(outputTokens) / 1_000_000.0 * 5.0)
// Send done event with usage after all chunks are sent
doneData := map[string]interface{}{
"done": true,
"tokenUsage": map[string]int{
"inputTokens": inputTokens,
"outputTokens": outputTokens,
"totalTokens": inputTokens + outputTokens,
},
"cost": cost,
}
doneBytes, _ := json.Marshal(doneData)
fmt.Fprintf(w, "data: %s\n\n", doneBytes)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
fmt.Printf("[Gemini] Stream complete: %d input tokens, %d output tokens\n", inputTokens, outputTokens)
return nil
}
================================================
FILE: backend/gemini_image.go
================================================
package main
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
)
const nanoBanana2ModelID = "gemini-3.1-flash-image-preview"
// callGeminiImageGeneration calls Gemini image generation via generativelanguage.googleapis.com
// Returns base64 data URI on success
func callGeminiImageGeneration(prompt string, aspectRatio string, outputFormat string, apiKey string) (string, error) {
url := fmt.Sprintf("https://generativelanguage.googleapis.com/v1beta/models/%s:generateContent?key=%s", nanoBanana2ModelID, apiKey)
// Build request with text prompt
contents := []map[string]interface{}{
{
"parts": []map[string]interface{}{
{
"text": prompt,
},
},
},
}
// Build request with aspect ratio in correct structure (based on official example)
generationConfig := map[string]interface{}{
"temperature": 1.0,
"maxOutputTokens": 8192,
"responseModalities": []string{"IMAGE"},
}
// Add imageConfig with aspectRatio ONLY if specified
// Note: outputFormat is not supported by the Gemini API (only Vertex AI)
if strings.TrimSpace(aspectRatio) != "" {
generationConfig["imageConfig"] = map[string]interface{}{
"aspectRatio": strings.TrimSpace(aspectRatio),
}
}
reqBody := map[string]interface{}{
"contents": contents,
"generationConfig": generationConfig,
}
bodyBytes, err := json.Marshal(reqBody)
if err != nil {
return "", fmt.Errorf("failed to marshal request: %w", err)
}
req, err := http.NewRequest("POST", url, bytes.NewReader(bodyBytes))
if err != nil {
return "", fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
fmt.Println("[DEBUG] Calling Gemini image generation API...")
fmt.Printf("[DEBUG] Request body: %s\n", string(bodyBytes))
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", fmt.Errorf("failed to call Gemini API: %w", err)
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("failed to read response: %w", err)
}
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("Gemini API error (status %d): %s", resp.StatusCode, string(respBody))
}
// Parse response
var result struct {
Candidates []struct {
Content struct {
Parts []struct {
Text string `json:"text,omitempty"`
InlineData struct {
MimeType string `json:"mimeType"`
Data string `json:"data"`
} `json:"inlineData,omitempty"`
} `json:"parts"`
} `json:"content"`
} `json:"candidates"`
}
if err := json.Unmarshal(respBody, &result); err != nil {
return "", fmt.Errorf("failed to parse response: %w", err)
}
if len(result.Candidates) == 0 {
return "", fmt.Errorf("no candidates in response")
}
// Find the image data in parts
var textFallback string
for _, part := range result.Candidates[0].Content.Parts {
if part.InlineData.Data != "" {
mimePrefix := "image/png"
if part.InlineData.MimeType != "" {
mimePrefix = part.InlineData.MimeType
}
dataURI := fmt.Sprintf("data:%s;base64,%s", mimePrefix, part.InlineData.Data)
return dataURI, nil
}
if part.Text != "" && textFallback == "" {
textFallback = part.Text
}
}
if textFallback != "" {
return "", fmt.Errorf("no image generated — model responded with text: %s", textFallback)
}
return "", fmt.Errorf("no image data in response")
}
// callGeminiImageGenerationWithReference calls Gemini image generation with a reference image
// for personalized generation (e.g., user photo, product image)
//
// PRIVACY NOTE: This function does not store or cache the reference image.
// The image is only sent to Gemini's API and immediately discarded after the request.
//
// Returns base64 data URI on success
func callGeminiImageGenerationWithReference(prompt string, referenceImageBase64 string, aspectRatio string, outputFormat string, apiKey string) (string, error) {
url := fmt.Sprintf("https://generativelanguage.googleapis.com/v1beta/models/%s:generateContent?key=%s", nanoBanana2ModelID, apiKey)
geminiAspectRatio := strings.TrimSpace(aspectRatio)
// Detect image format from base64 data (first few characters after decoding)
mimeType := "image/jpeg"
imageBytes, err := base64.StdEncoding.DecodeString(referenceImageBase64)
if err == nil && len(imageBytes) > 2 {
// PNG magic bytes: 89 50 4E 47
if imageBytes[0] == 0x89 && imageBytes[1] == 0x50 && imageBytes[2] == 0x4E && imageBytes[3] == 0x47 {
mimeType = "image/png"
} else if len(imageBytes) > 11 && string(imageBytes[8:12]) == "WEBP" {
mimeType = "image/webp"
}
}
// Build request with both text prompt and reference image
contents := []map[string]interface{}{
{
"parts": []map[string]interface{}{
{
"text": prompt,
},
{
"inline_data": map[string]interface{}{
"mime_type": mimeType,
"data": referenceImageBase64,
},
},
},
},
}
// Build request with aspect ratio in correct structure (based on official example)
generationConfig := map[string]interface{}{
"temperature": 1.0,
"maxOutputTokens": 8192,
"responseModalities": []string{"IMAGE"},
}
// Add imageConfig with aspectRatio if specified
// Note: outputFormat is not supported by the Gemini API (only Vertex AI)
if geminiAspectRatio != "" {
generationConfig["imageConfig"] = map[string]interface{}{
"aspectRatio": geminiAspectRatio,
}
}
reqBody := map[string]interface{}{
"contents": contents,
"generationConfig": generationConfig,
}
bodyBytes, err := json.Marshal(reqBody)
if err != nil {
return "", fmt.Errorf("failed to marshal request: %w", err)
}
req, err := http.NewRequest("POST", url, bytes.NewReader(bodyBytes))
if err != nil {
return "", fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
fmt.Println("[DEBUG] Calling Gemini image generation API with reference image...")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", fmt.Errorf("failed to call Gemini API: %w", err)
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("failed to read response: %w", err)
}
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("Gemini API error (status %d): %s", resp.StatusCode, string(respBody))
}
// Parse response
var result struct {
Candidates []struct {
Content struct {
Parts []struct {
Text string `json:"text,omitempty"`
InlineData struct {
MimeType string `json:"mimeType"`
Data string `json:"data"`
} `json:"inlineData,omitempty"`
} `json:"parts"`
} `json:"content"`
} `json:"candidates"`
}
if err := json.Unmarshal(respBody, &result); err != nil {
return "", fmt.Errorf("failed to parse response: %w", err)
}
if len(result.Candidates) == 0 {
return "", fmt.Errorf("no candidates in response")
}
// Find the image data in parts
var textFallback string
for _, part := range result.Candidates[0].Content.Parts {
if part.InlineData.Data != "" {
mimePrefix := "image/png"
if part.InlineData.MimeType != "" {
mimePrefix = part.InlineData.MimeType
}
dataURI := fmt.Sprintf("data:%s;base64,%s", mimePrefix, part.InlineData.Data)
return dataURI, nil
}
if part.Text != "" && textFallback == "" {
textFallback = part.Text
}
}
if textFallback != "" {
return "", fmt.Errorf("no image generated — model responded with text: %s", textFallback)
}
return "", fmt.Errorf("no image data in response")
}
================================================
FILE: backend/gemini_text_with_attachment.go
================================================
package main
// callGeminiApiGoWithAttachment builds a Gemini chat request that includes inline_data attachment.
func callGeminiApiGoWithAttachment(prevMsgs []ChatMessage, newMsg, attachmentBase64, mimeType, apiKey, geminiModel string) (*R1Response, error) {
if apiKey == "" {
return &R1Response{
AIResponse: "No Gemini API key provided. Please add your key in Settings.",
TokenUsage: map[string]int{},
Cost: 0,
}, nil
}
// Gather system message and convert to Gemini format
systemMsg := defaultSystemPrompt
var contents []map[string]interface{}
for _, m := range prevMsgs {
if m.Role == "system" {
systemMsg = m.Content
}
}
// Add system message as first user message (Gemini doesn't have separate system role)
hasUserMsg := false
for _, m := range prevMsgs {
if m.Role == "user" || m.Role == "assistant" {
hasUserMsg = true
break
}
}
if hasUserMsg {
// Add system prompt as first user turn
contents = append(contents, map[string]interface{}{
"role": "user",
"parts": []interface{}{
map[string]interface{}{"text": systemMsg},
},
})
// Add a model response acknowledging the system message
contents = append(contents, map[string]interface{}{
"role": "model",
"parts": []interface{}{
map[string]interface{}{"text": "Understood. I'll follow these instructions."},
},
})
}
// Add conversation history (convert "assistant" to "model" for Gemini)
for _, m := range prevMsgs {
if m.Role == "user" {
contents = append(contents, map[string]interface{}{
"role": "user",
"parts": []interface{}{
map[string]interface{}{"text": m.Content},
},
})
} else if m.Role == "assistant" {
contents = append(contents, map[string]interface{}{
"role": "model",
"parts": []interface{}{
map[string]interface{}{"text": m.Content},
},
})
}
}
// Add new user message with inline attachment
contents = append(contents, map[string]interface{}{
"role": "user",
"parts": []interface{}{
map[string]interface{}{"text": newMsg},
map[string]interface{}{
"inline_data": map[string]interface{}{
"mime_type": mimeType,
"data": attachmentBase64,
},
},
},
})
// Call Gemini API
resolvedModel := normalizeGeminiModelID(geminiModel)
respText, inputTokens, outputTokens, err := callGeminiAPIWithModel(contents, apiKey, 12000, "", resolvedModel)
if err != nil {
return nil, err
}
// Calculate cost
cost := (float64(inputTokens) / 1_000_000.0 * 1.25) + (float64(outputTokens) / 1_000_000.0 * 5.0)
return &R1Response{
AIResponse: respText,
TokenUsage: map[string]int{
"inputTokens": inputTokens,
"outputTokens": outputTokens,
"totalTokens": inputTokens + outputTokens,
},
Cost: cost,
}, nil
}
================================================
FILE: backend/gemini_veo.go
================================================
package main
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"log"
"strings"
"google.golang.org/genai"
)
// MARK: - Request/Response Types
type VeoImageInput struct {
Data string `json:"data"`
MimeType string `json:"mimeType"`
}
type VeoVideoAsset struct {
URI string `json:"uri"`
AspectRatio string `json:"aspectRatio"`
}
type VeoGenerationRequest struct {
Prompt string `json:"prompt"`
Images []VeoImageInput `json:"images"`
AspectRatio string `json:"aspectRatio"`
UseKeyframes bool `json:"useKeyframes"`
ExtensionSource *VeoVideoAsset `json:"extensionSource"`
DurationSeconds int `json:"durationSeconds,omitempty"`
Resolution string `json:"resolution,omitempty"`
VideoSource string `json:"videoSource,omitempty"`
GeminiKey string `json:"geminiKey"`
XaiKey string `json:"xaiKey,omitempty"`
}
type VeoGenerationResponse struct {
OperationID string `json:"operationId"`
Message string `json:"message"`
}
type VeoPollRequest struct {
OperationID string `json:"operationId"`
VideoSource string `json:"videoSource,omitempty"`
GeminiKey string `json:"geminiKey"`
XaiKey string `json:"xaiKey,omitempty"`
}
type VeoPollResponse struct {
Done bool `json:"done"`
Status string `json:"status"`
VideoURL string `json:"videoUrl,omitempty"`
VideoAsset *VeoVideoAsset `json:"videoAsset,omitempty"`
Error string `json:"error,omitempty"`
}
// MARK: - Core Functions (Direct translation from TypeScript)
// startVeoVideoGeneration - Direct translation of generateVeoVideo from veoService.ts
func startVeoVideoGeneration(req VeoGenerationRequest) (*VeoGenerationResponse, error) {
if req.GeminiKey == "" {
return nil, fmt.Errorf("no Gemini API key provided")
}
if len(req.Images) == 0 && req.ExtensionSource == nil {
return nil, fmt.Errorf("at least one image or extensionSource is required")
}
ctx := context.Background()
// Create client with API key
client, err := genai.NewClient(ctx, &genai.ClientConfig{
APIKey: req.GeminiKey,
Backend: genai.BackendGeminiAPI,
})
if err != nil {
return nil, fmt.Errorf("failed to create client: %v", err)
}
isMultiImage := len(req.Images) > 1
useKeyframesMode := req.UseKeyframes && len(req.Images) == 2
// Choose model based on feature requirements (same logic as TS)
model := "veo-3.1-fast-generate-preview"
if len(req.Images) > 2 || req.ExtensionSource != nil {
model = "veo-3.1-generate-preview"
}
var operation *genai.GenerateVideosOperation
// VIDEO EXTENSION (Video-to-Video flow)
if req.ExtensionSource != nil {
config := &genai.GenerateVideosConfig{
NumberOfVideos: 1,
Resolution: "720p",
AspectRatio: req.AspectRatio,
}
source := &genai.GenerateVideosSource{
Prompt: req.Prompt,
Video: &genai.Video{
URI: req.ExtensionSource.URI,
},
}
operation, err = client.Models.GenerateVideosFromSource(ctx, "veo-3.1-generate-preview", source, config)
} else if useKeyframesMode {
// START -> END KEYFRAME INTERPOLATION
imageData, decodeErr := base64.StdEncoding.DecodeString(req.Images[0].Data)
if decodeErr != nil {
return nil, fmt.Errorf("failed to decode image: %v", decodeErr)
}
lastFrameData, decodeErr := base64.StdEncoding.DecodeString(req.Images[1].Data)
if decodeErr != nil {
return nil, fmt.Errorf("failed to decode last frame: %v", decodeErr)
}
image := &genai.Image{
ImageBytes: imageData,
MIMEType: req.Images[0].MimeType,
}
config := &genai.GenerateVideosConfig{
NumberOfVideos: 1,
Resolution: "720p",
AspectRatio: req.AspectRatio,
LastFrame: &genai.Image{
ImageBytes: lastFrameData,
MIMEType: req.Images[1].MimeType,
},
}
operation, err = client.Models.GenerateVideos(ctx, "veo-3.1-fast-generate-preview", req.Prompt, image, config)
} else if isMultiImage {
// MULTI-IMAGE ASSET REFERENCE (Max 3)
var referenceImages []*genai.VideoGenerationReferenceImage
for i := 0; i < len(req.Images) && i < 3; i++ {
imageData, decodeErr := base64.StdEncoding.DecodeString(req.Images[i].Data)
if decodeErr != nil {
return nil, fmt.Errorf("failed to decode reference image: %v", decodeErr)
}
referenceImages = append(referenceImages, &genai.VideoGenerationReferenceImage{
Image: &genai.Image{
ImageBytes: imageData,
MIMEType: req.Images[i].MimeType,
},
ReferenceType: "ASSET",
})
}
config := &genai.GenerateVideosConfig{
NumberOfVideos: 1,
Resolution: "720p",
AspectRatio: "16:9", // Requirement for multi-image
ReferenceImages: referenceImages,
}
source := &genai.GenerateVideosSource{
Prompt: req.Prompt,
}
operation, err = client.Models.GenerateVideosFromSource(ctx, "veo-3.1-generate-preview", source, config)
} else {
// SINGLE IMAGE START FRAME
imageData, decodeErr := base64.StdEncoding.DecodeString(req.Images[0].Data)
if decodeErr != nil {
return nil, fmt.Errorf("failed to decode image: %v", decodeErr)
}
image := &genai.Image{
ImageBytes: imageData,
MIMEType: req.Images[0].MimeType,
}
config := &genai.GenerateVideosConfig{
NumberOfVideos: 1,
Resolution: "720p",
AspectRatio: req.AspectRatio,
}
operation, err = client.Models.GenerateVideos(ctx, model, req.Prompt, image, config)
}
if err != nil {
log.Printf("[VEO Start] Generation error: %v", err)
if strings.Contains(err.Error(), "Requested entity was not found") {
return nil, fmt.Errorf("KEY_RESET_REQUIRED")
}
// Check for rate limit at generation start
if strings.Contains(err.Error(), "429") || strings.Contains(err.Error(), "RESOURCE_EXHAUSTED") || strings.Contains(err.Error(), "rate") || strings.Contains(err.Error(), "quota") {
log.Printf("[VEO Start] Rate limit detected: %v", err)
return nil, fmt.Errorf("Rate limit exceeded. Please wait a few minutes and try again. Details: %v", err)
}
return nil, fmt.Errorf("API error: %v", err)
}
log.Printf("[VEO Start] Generation started successfully. Operation: %s", operation.Name)
// Extract operation name (format: "operations/{operationId}")
return &VeoGenerationResponse{
OperationID: operation.Name,
Message: "Video generation started successfully",
}, nil
}
// pollVeoOperation - Direct translation of polling logic from veoService.ts
func pollVeoOperation(operationID, apiKey string) (*VeoPollResponse, error) {
if apiKey == "" {
return nil, fmt.Errorf("no API key provided")
}
log.Printf("[VEO Poll] Starting poll for operation: %s", operationID)
ctx := context.Background()
// Create client with API key
client, err := genai.NewClient(ctx, &genai.ClientConfig{
APIKey: apiKey,
Backend: genai.BackendGeminiAPI,
})
if err != nil {
log.Printf("[VEO Poll] Failed to create client: %v", err)
return nil, fmt.Errorf("failed to create client: %v", err)
}
// Poll operation status
operation, err := client.Operations.GetVideosOperation(ctx, &genai.GenerateVideosOperation{
Name: operationID,
}, nil)
if err != nil {
log.Printf("[VEO Poll] GetVideosOperation error: %v", err)
if strings.Contains(err.Error(), "Requested entity was not found") {
return &VeoPollResponse{
Done: true,
Status: "failed",
Error: "KEY_RESET_REQUIRED",
}, nil
}
// Check for rate limit errors
if strings.Contains(err.Error(), "429") || strings.Contains(err.Error(), "RESOURCE_EXHAUSTED") || strings.Contains(err.Error(), "rate") {
log.Printf("[VEO Poll] Rate limit detected in error: %v", err)
return &VeoPollResponse{
Done: true,
Status: "failed",
Error: fmt.Sprintf("Rate limit exceeded. Please wait a few minutes and try again. Details: %v", err),
}, nil
}
return nil, fmt.Errorf("poll failed: %v", err)
}
// Log full operation state for debugging
log.Printf("[VEO Poll] Operation Done: %v, Has Error: %v, Has Response: %v",
operation.Done, operation.Error != nil, operation.Response != nil)
// Check for errors
if operation.Error != nil {
// Log the full error object for debugging
errorJSON, _ := json.MarshalIndent(operation.Error, "", " ")
log.Printf("[VEO Poll] Operation error (full): %s", string(errorJSON))
errorMsg := "Generation failed"
// Try to extract more detailed error info
if msg, ok := operation.Error["message"].(string); ok {
errorMsg = msg
log.Printf("[VEO Poll] Error message: %s", msg)
}
if code, ok := operation.Error["code"].(float64); ok {
log.Printf("[VEO Poll] Error code: %v", code)
// HTTP 429 = rate limit
if int(code) == 429 {
errorMsg = "Rate limit exceeded. Please wait a few minutes and try again."
}
}
if status, ok := operation.Error["status"].(string); ok {
log.Printf("[VEO Poll] Error status: %s", status)
if status == "RESOURCE_EXHAUSTED" {
errorMsg = "Rate limit exceeded. Please wait a few minutes and try again."
}
}
// Check for details array
if details, ok := operation.Error["details"].([]interface{}); ok {
detailsJSON, _ := json.MarshalIndent(details, "", " ")
log.Printf("[VEO Poll] Error details: %s", string(detailsJSON))
}
return &VeoPollResponse{
Done: true,
Status: "failed",
Error: errorMsg,
}, nil
}
// Check if operation is done
if !operation.Done {
log.Printf("[VEO Poll] Operation still processing...")
return &VeoPollResponse{
Done: false,
Status: "processing",
}, nil
}
// Operation completed - extract video info
// Debug: log full response
responseJSON, _ := json.MarshalIndent(operation.Response, "", " ")
log.Printf("[VEO Poll] Full response: %s", string(responseJSON))
if operation.Response == nil || len(operation.Response.GeneratedVideos) == 0 {
log.Printf("[VEO Poll] Operation completed but no video returned. Response nil: %v, GeneratedVideos count: %d",
operation.Response == nil,
func() int {
if operation.Response != nil {
return len(operation.Response.GeneratedVideos)
}
return 0
}())
// Check for RAI (Responsible AI) filter reasons
errorMsg := "Generation completed but no video was returned. This may be due to safety filters or content policy."
if operation.Response != nil && len(operation.Response.RAIMediaFilteredReasons) > 0 {
errorMsg = operation.Response.RAIMediaFilteredReasons[0]
log.Printf("[VEO Poll] RAI filter reason: %s", errorMsg)
}
return &VeoPollResponse{
Done: true,
Status: "failed",
Error: errorMsg,
}, nil
}
videoAsset := operation.Response.GeneratedVideos[0].Video
log.Printf("[VEO Poll] Success! Video URI: %s", videoAsset.URI)
return &VeoPollResponse{
Done: true,
Status: "completed",
VideoURL: videoAsset.URI,
VideoAsset: &VeoVideoAsset{
URI: videoAsset.URI,
AspectRatio: "16:9", // Default, SDK might not provide this field
},
}, nil
}
================================================
FILE: backend/gemini_video.go
================================================
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
)
// callGeminiVideoAnalysis sends a recorded video (base64) to Gemini for understanding.
// It returns a short analysis that can be attached to the user's chat message.
func callGeminiVideoAnalysis(videoBase64, mimeType, prompt, apiKey string) (*R1Response, error) {
if strings.TrimSpace(apiKey) == "" {
return &R1Response{
AIResponse: "No Gemini API key provided. Add it in Settings to analyze video.",
TokenUsage: map[string]int{},
Cost: 0,
}, nil
}
if mimeType == "" {
mimeType = "video/mp4"
}
contents := []map[string]interface{}{
{
"role": "user",
"parts": []interface{}{
map[string]interface{}{"text": prompt},
map[string]interface{}{
"inline_data": map[string]interface{}{
"mime_type": mimeType,
"data": videoBase64,
},
},
},
},
}
// Use Gemini 3 Flash preview by default for faster multimodal analysis.
model := "gemini-3-flash-preview"
aiResp, inputTokens, outputTokens, err := callGeminiAPIWithModel(contents, apiKey, 12000, "", model)
if err != nil {
return nil, err
}
cost := (float64(inputTokens) / 1_000_000.0 * 1.25) + (float64(outputTokens) / 1_000_000.0 * 5.0)
return &R1Response{
AIResponse: aiResp,
TokenUsage: map[string]int{
"inputTokens": inputTokens,
"outputTokens": outputTokens,
"totalTokens": inputTokens + outputTokens,
},
Cost: cost,
}, nil
}
// callGeminiAPIWithModel mirrors callGeminiAPI but lets callers choose the model (needed for video support).
func callGeminiAPIWithModel(contents []map[string]interface{}, apiKey string, maxTokens int, thinkingBudget string, modelName string) (string, int, int, error) {
start := time.Now()
fmt.Printf("[GeminiVideo] request model=%s\n", modelName)
thinkingCfg := map[string]interface{}{
"thinkingBudget": -1,
"includeThoughts": true,
}
if thinkingBudget != "" {
thinkingCfg["thinkingBudget"] = thinkingBudget
}
generationConfig := map[string]interface{}{
"maxOutputTokens": maxTokens,
"temperature": 0.6,
"thinkingConfig": thinkingCfg,
}
reqBody := map[string]interface{}{
"contents": contents,
"generationConfig": generationConfig,
}
jsonBytes, _ := json.Marshal(reqBody)
if modelName == "" {
modelName = "gemini-3-flash-preview"
}
url := fmt.Sprintf("https://generativelanguage.googleapis.com/v1beta/models/%s:generateContent?key=%s", modelName, apiKey)
req, err := http.NewRequest("POST", url, bytes.NewReader(jsonBytes))
if err != nil {
return "", 0, 0, err
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{
Timeout: 120 * time.Second,
}
resp, err := client.Do(req)
if err != nil {
return "", 0, 0, err
}
defer resp.Body.Close()
elapsed := time.Since(start)
fmt.Printf("[GeminiVideo] response status=%d elapsed=%s\n", resp.StatusCode, elapsed)
if resp.StatusCode != 200 {
b, _ := io.ReadAll(resp.Body)
errMsg := fmt.Sprintf("Gemini API error (%d): %s", resp.StatusCode, string(b))
return "", 0, 0, fmt.Errorf("%s", errMsg)
}
var geminiResp struct {
Candidates []struct {
Content struct {
Parts []map[string]interface{} `json:"parts"`
} `json:"content"`
} `json:"candidates"`
UsageMetadata struct {
PromptTokenCount int `json:"promptTokenCount"`
CandidatesTokenCount int `json:"candidatesTokenCount"`
TotalTokenCount int `json:"totalTokenCount"`
} `json:"usageMetadata"`
}
bodyBytes, _ := io.ReadAll(resp.Body)
if err := json.Unmarshal(bodyBytes, &geminiResp); err != nil {
return "", 0, 0, fmt.Errorf("failed to parse Gemini response: %w", err)
}
if len(geminiResp.Candidates) == 0 {
return "", 0, 0, fmt.Errorf("no candidates in Gemini response")
}
var responseText strings.Builder
var thinking strings.Builder
for _, part := range geminiResp.Candidates[0].Content.Parts {
isThought := false
if thoughtVal, hasThought := part["thought"]; hasThought {
if thoughtBool, ok := thoughtVal.(bool); ok && thoughtBool {
isThought = true
} else if thoughtStr, ok := thoughtVal.(string); ok && thoughtStr != "" {
isThought = true
}
}
if text, ok := part["text"].(string); ok {
if isThought {
fmt.Fprintf(&thinking, "<think>%s</think>\n", text)
} else {
responseText.WriteString(text)
}
}
}
final := strings.TrimSpace(thinking.String() + "\n" + responseText.String())
inputTokens := geminiResp.UsageMetadata.PromptTokenCount
outputTokens := geminiResp.UsageMetadata.CandidatesTokenCount
return final, inputTokens, outputTokens, nil
}
================================================
FILE: backend/go.mod
================================================
module glowbom-backend
go 1.24
toolchain go1.24.11
require (
cloud.google.com/go v0.116.0 // indirect
cloud.google.com/go/auth v0.9.3 // indirect
cloud.google.com/go/compute/metadata v0.5.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/s2a-go v0.1.8 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/sst/opencode-sdk-go v0.19.2 // indirect
github.com/tidwall/gjson v1.14.4 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/text v0.23.0 // indirect
google.golang.org/genai v1.40.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/grpc v1.66.2 // indirect
google.golang.org/protobuf v1.34.2 // indirect
)
================================================
FILE: backend/go.sum
================================================
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U=
cloud.google.com/go/auth v0.9.3 h1:VOEUIAADkkLtyfr3BLa3R8Ed/j6w1jTBmARx+wb5w5U=
cloud.google.com/go/auth v0.9.3/go.mod h1:7z6VY+7h3KUdRov5F1i8NDP5ZzWKYmEPO842BgCsmTk=
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/sst/opencode-sdk-go v0.19.2 h1:ffgQpE+ms4F0Wop/tT4tqTvFAbocyWYM8iy543b3Ous=
github.com/sst/opencode-sdk-go v0.19.2/go.mod h1:rrpo5n0Be43y6tJ29TeMxH1/zeoDcB0D43nJh6gnL34=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genai v1.40.0 h1:kYxyQSH+vsib8dvsgyLJzsVEIv5k3ZmHJyVqdvGncmc=
google.golang.org/genai v1.40.0/go.mod h1:A3kkl0nyBjyFlNjgxIwKq70julKbIxpSxqKO5gw/gmk=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo=
google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
================================================
FILE: backend/gpt5_openai.go
================================================
package main
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"io"
"math"
"net/http"
"strings"
"time"
)
// callGPT5DrawToCodeApiFull handles draw-to-code using GPT-5 with native vision
func callGPT5DrawToCodeApiFull(imageBase64, userPrompt, template, imageSource, apiKey, openAIModel string) (*R1Response, error) {
if apiKey == "" {
return &R1Response{
AIResponse: "No OpenAI API key provided. Please add your key in Settings.",
TokenUsage: map[string]int{"inputTokens": 0, "outputTokens": 0, "totalTokens": 0},
Cost: 0,
}, nil
}
resolvedModel := normalizeOpenAIModelID(openAIModel)
// 1) Build system prompt (developer role)
systemPrompt := getSystemPrompt(template, imageSource)
systemPrompt += " Replace @tailwind placeholders with the Tailwind CSS CDN link to load the real framework."
// 2) Build detailed task
detailedTask := buildDetailedTaskDescription(template, imageSource, userPrompt)
// 3) Construct GPT-5.2 request with vision
// GPT-5.2 uses "developer" role for system messages and supports vision in user messages
messages := []map[string]interface{}{
{
"role": "developer",
"content": systemPrompt,
},
{
"role": "user",
"content": []interface{}{
map[string]interface{}{
"type": "text",
"text": detailedTask,
},
map[string]interface{}{
"type": "image_url",
"image_url": map[string]interface{}{
"url": fmt.Sprintf("data:image/jpeg;base64,%s", imageBase64),
},
},
},
},
}
// 4) Call OpenAI API with GPT-5.2
aiResp, inputTokens, outputTokens, err := callGPT5API(messages, apiKey, 32768, "low", resolvedModel)
if err != nil {
return nil, err
}
cost := estimateOpenAITextCost(resolvedModel, inputTokens, outputTokens)
return &R1Response{
AIResponse: aiResp,
TokenUsage: map[string]int{
"inputTokens": inputTokens,
"outputTokens": outputTokens,
"totalTokens": inputTokens + outputTokens,
},
Cost: cost,
}, nil
}
// callGPT5DrawToCodeStreaming handles draw-to-code with streaming for GPT-5.2
func callGPT5DrawToCodeStreaming(w http.ResponseWriter, imageBase64, userPrompt, template, imageSource, apiKey, openAIModel string) error {
if apiKey == "" {
return fmt.Errorf("no OpenAI API key provided")
}
resolvedModel := normalizeOpenAIModelID(openAIModel)
// Build system prompt (developer role)
systemPrompt := getSystemPrompt(template, imageSource)
systemPrompt += " Replace @tailwind placeholders with the Tailwind CSS CDN link to load the real framework."
// Build detailed task
detailedTask := buildDetailedTaskDescription(template, imageSource, userPrompt)
// Construct GPT-5.2 request with vision
messages := []map[string]interface{}{
{
"role": "developer",
"content": systemPrompt,
},
{
"role": "user",
"content": []interface{}{
map[string]interface{}{
"type": "text",
"text": detailedTask,
},
map[string]interface{}{
"type": "image_url",
"image_url": map[string]interface{}{
"url": fmt.Sprintf("data:image/jpeg;base64,%s", imageBase64),
},
},
},
},
}
// Use the same high output budget as non-streaming draw-to-code to avoid truncated translations.
return callGPT5APIStreaming(w, messages, apiKey, 32768, "low", resolvedModel)
}
// callGPT5ApiGo handles normal chat using GPT-5.2
func callGPT5ApiGo(prevMsgs []ChatMessage, newMsg, apiKey, openAIModel string) (*R1Response, error) {
if apiKey == "" {
return &R1Response{
AIResponse: "No OpenAI API key provided. Please add your key in Settings.",
TokenUsage: map[string]int{},
Cost: 0,
}, nil
}
resolvedModel := normalizeOpenAIModelID(openAIModel)
// Gather system message and convert to GPT-5.2 format
systemMsg := defaultSystemPrompt
var messages []map[string]interface{}
for _, m := range prevMsgs {
if m.Role == "system" {
systemMsg = m.Content
}
}
// Add developer message (GPT-5.2's system role)
messages = append(messages, map[string]interface{}{
"role": "developer",
"content": systemMsg,
})
// Add conversation history
for _, m := range prevMsgs {
if m.Role == "user" || m.Role == "assistant" {
messages = append(messages, map[string]interface{}{
"role": m.Role,
"content": m.Content,
})
}
}
// Add new user message
messages = append(messages, map[string]interface{}{
"role": "user",
"content": newMsg,
})
// Call GPT-5.2 API
aiResp, inputTokens, outputTokens, err := callGPT5API(messages, apiKey, 8192, "low", resolvedModel)
if err != nil {
return nil, err
}
cost := estimateOpenAITextCost(resolvedModel, inputTokens, outputTokens)
return &R1Response{
AIResponse: aiResp,
TokenUsage: map[string]int{
"inputTokens": inputTokens,
"outputTokens": outputTokens,
"totalTokens": inputTokens + outputTokens,
},
Cost: cost,
}, nil
}
// callGPT5API makes the actual HTTP request to OpenAI API with streaming support
func callGPT5API(messages []map[string]interface{}, apiKey string, maxTokens int, reasoningEffort, openAIModel string) (string, int, int, error) {
resolvedModel := normalizeOpenAIModelID(openAIModel)
reqBody := map[string]interface{}{
"model": resolvedModel,
"reasoning_effort": reasoningEffort, // "low", "medium", or "high"
"max_completion_tokens": maxTokens,
"messages": messages,
"stream": true,
"stream_options": map[string]interface{}{
"include_usage": true,
},
}
jsonBytes, _ := json.Marshal(reqBody)
req, err := http.NewRequest("POST", "https://api.openai.com/v1/chat/completions", bytes.NewReader(jsonBytes))
if err != nil {
return "", 0, 0, err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+apiKey)
fmt.Println("[DEBUG] Calling GPT-5 API with streaming...")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", 0, 0, err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
b, _ := io.ReadAll(resp.Body)
bodyText := string(b)
if isOpenAIMissingModelScope(bodyText) {
return "", 0, 0, fmt.Errorf("%s", openAIModelScopeHelpMessage())
}
return "", 0, 0, fmt.Errorf("OpenAI API error (%d): %s", resp.StatusCode, bodyText)
}
// Parse streaming response
var responseText strings.Builder
var reasoning strings.Builder
var inputTokens, outputTokens int
reader := bufio.NewReader(resp.Body)
for {
line, err := reader.ReadString('\n')
if len(line) > 0 {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "data:") {
dataStr := strings.TrimSpace(line[5:])
if dataStr == "[DONE]" {
break
}
var chunk struct {
Choices []struct {
Delta struct {
Content string `json:"content"`
Reasoning struct {
Summary []struct {
Type string `json:"type"`
Text string `json:"text"`
} `json:"summary"`
} `json:"reasoning"`
} `json:"delta"`
} `json:"choices"`
Usage struct {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
} `json:"usage"`
}
if e := json.Unmarshal([]byte(dataStr), &chunk); e == nil {
if len(chunk.Choices) > 0 {
// Extract content
responseText.WriteString(chunk.Choices[0].Delta.Content)
// Extract reasoning summaries
for _, summary := range chunk.Choices[0].Delta.Reasoning.Summary {
if summary.Type == "summary_text" {
reasoning.WriteString(summary.Text)
}
}
}
// Extract usage from final chunk
if chunk.Usage.PromptTokens > 0 {
inputTokens = chunk.Usage.PromptTokens
outputTokens = chunk.Usage.CompletionTokens
}
}
}
}
if err != nil {
if err == io.EOF {
break
}
return "", 0, 0, err
}
}
// Prepend reasoning wrapped in <think> tags if present (matching Ollama format)
fullResponse := responseText.String()
if reasoning.Len() > 0 {
fullResponse = "<think>" + reasoning.String() + "</think>" + fullResponse
}
fmt.Printf("[DEBUG] GPT-5 response: %d input tokens, %d output tokens\n", inputTokens, outputTokens)
return fullResponse, inputTokens, outputTokens, nil
}
// callGPT5ChatStreaming streams GPT-5.2 response via SSE to the writer (for chat)
func callGPT5ChatStreaming(w http.ResponseWriter, prevMsgs []ChatMessage, newMsg, apiKey, openAIModel string) error {
if apiKey == "" {
return fmt.Errorf("no OpenAI API key provided")
}
resolvedModel := normalizeOpenAIModelID(openAIModel)
// Gather system message and convert to GPT-5.2 format
systemMsg := defaultSystemPrompt
var messages []map[string]interface{}
for _, m := range prevMsgs {
if m.Role == "system" {
systemMsg = m.Content
}
}
// Add developer message (GPT-5.2's system role)
messages = append(messages, map[string]interface{}{
"role": "developer",
"content": systemMsg,
})
// Add conversation history
for _, m := range prevMsgs {
if m.Role == "user" || m.Role == "assistant" {
messages = append(messages, map[string]interface{}{
"role": m.Role,
"content": m.Content,
})
}
}
// Add new user message
messages = append(messages, map[string]interface{}{
"role": "user",
"content": newMsg,
})
// Call streaming GPT-5.2 API
return callGPT5APIStreaming(w, messages, apiKey, 8192, "low", resolvedModel)
}
// callGPT5APIStreaming streams GPT-5.2 responses via SSE directly to the HTTP response writer
func callGPT5APIStreaming(w http.ResponseWriter, messages []map[string]interface{}, apiKey string, maxTokens int, reasoningEffort, openAIModel string) error {
resolvedModel := normalizeOpenAIModelID(openAIModel)
reqBody := map[string]interface{}{
"model": resolvedModel,
"reasoning_effort": reasoningEffort,
"max_completion_tokens": maxTokens,
"messages": messages,
"stream": true,
"stream_options": map[string]interface{}{
"include_usage": true,
},
}
jsonBytes, _ := json.Marshal(reqBody)
req, err := http.NewRequest("POST", "https://api.openai.com/v1/chat/completions", bytes.NewReader(jsonBytes))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+apiKey)
fmt.Println("[DEBUG] Calling GPT-5 API with streaming (SSE)...")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
b, _ := io.ReadAll(resp.Body)
bodyText := string(b)
if isOpenAIMissingModelScope(bodyText) {
return fmt.Errorf("%s", openAIModelScopeHelpMessage())
}
return fmt.Errorf("OpenAI API error (%d): %s", resp.StatusCode, bodyText)
}
// Stream response chunks to client
var responseBuilder strings.Builder
var reasoningBuilder strings.Builder
var reasoningSent bool
var reasoningStart time.Time
var reasoningDurationSent bool
var inputTokens, outputTokens int
reader := bufio.NewReader(resp.Body)
for {
line, err := reader.ReadString('\n')
if len(line) > 0 {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "data:") {
dataStr := strings.TrimSpace(line[5:])
if dataStr == "[DONE]" {
break
}
var chunk struct {
Choices []struct {
Delta struct {
Content string `json:"content"`
Reasoning struct {
Summary []struct {
Type string `json:"type"`
Text string `json:"text"`
} `json:"summary"`
} `json:"reasoning"`
} `json:"delta"`
} `json:"choices"`
Usage struct {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
} `json:"usage"`
}
if e := json.Unmarshal([]byte(dataStr), &chunk); e == nil {
// Debug: print raw chunk to see what we're getting
if len(chunk.Choices) > 0 {
fmt.Printf("[GPT-5 DEBUG] Chunk delta: %+v\n", chunk.Choices[0].Delta)
}
if len(chunk.Choices) > 0 {
// Accumulate reasoning content
for _, summary := range chunk.Choices[0].Delta.Reasoning.Summary {
if summary.Type == "summary_text" {
if reasoningStart.IsZero() {
reasoningStart = time.Now()
}
fmt.Printf("[GPT-5 DEBUG] Found reasoning summary: %s\n", summary.Text)
reasoningBuilder.WriteString(summary.Text)
// Send individual thinking step to client immediately
thinkingStepData := map[string]string{"thinkingStep": summary.Text}
thinkingStepBytes, _ := json.Marshal(thinkingStepData)
fmt.Fprintf(w, "data: %s\n\n", thinkingStepBytes)
fmt.Println("[GPT-5] Sending thinking step:", summary.Text)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
}
}
content := chunk.Choices[0].Delta.Content
responseBuilder.WriteString(content)
// Send accumulated reasoning before first content chunk
if !reasoningSent && reasoningBuilder.Len() > 0 && (content != "" || chunk.Usage.PromptTokens > 0) {
thinkingText := "<think>" + reaso
gitextract_gwqak2n4/
├── .claude/
│ └── settings.json
├── .github/
│ └── workflows/
│ └── glowby-release.yml
├── .gitignore
├── AGENTS.md
├── CLI.md
├── LICENSE
├── README.md
├── backend/
│ ├── backend_info_page.go
│ ├── claude_anthropic.go
│ ├── codex_chatgpt.go
│ ├── elevenlabs_audio.go
│ ├── fireworks_fireworks.go
│ ├── gemini_google.go
│ ├── gemini_image.go
│ ├── gemini_text_with_attachment.go
│ ├── gemini_veo.go
│ ├── gemini_video.go
│ ├── go.mod
│ ├── go.sum
│ ├── gpt5_openai.go
│ ├── gpt5_responses_api.go
│ ├── grok_image.go
│ ├── grok_video.go
│ ├── grok_xai.go
│ ├── handlers.go
│ ├── main.go
│ ├── model_helpers.go
│ ├── o3_openai.go
│ ├── openai_image.go
│ ├── opencode_driver.go
│ ├── opencode_folder_picker.go
│ ├── opencode_media_postpass.go
│ ├── opencode_project_history.go
│ ├── opencode_project_open.go
│ ├── opencode_zen.go
│ ├── openrouter_openrouter.go
│ ├── prompts.go
│ ├── r1_groq.go
│ ├── r1_ollama.go
│ ├── search.go
│ ├── security.go
│ ├── stack_specs.go
│ └── types.go
├── cli/
│ ├── code.go
│ ├── doctor.go
│ ├── go.mod
│ ├── main.go
│ └── version.go
├── docs/
│ ├── .dockerignore
│ ├── .gitignore
│ ├── Dockerfile
│ ├── README.md
│ ├── app/
│ │ ├── app.css
│ │ ├── components/
│ │ │ ├── brand-title.tsx
│ │ │ ├── docs-route-view.tsx
│ │ │ ├── docs-search.tsx
│ │ │ └── nav-actions.tsx
│ │ ├── lib/
│ │ │ ├── docs.ts
│ │ │ ├── meta.ts
│ │ │ ├── site.ts
│ │ │ ├── source.browser.tsx
│ │ │ └── source.server.ts
│ │ ├── root.tsx
│ │ ├── routes/
│ │ │ ├── docs-page.tsx
│ │ │ └── home.tsx
│ │ └── routes.ts
│ ├── content/
│ │ └── docs/
│ │ ├── desktop.mdx
│ │ ├── glowbom.mdx
│ │ ├── glowby-oss.mdx
│ │ ├── index.mdx
│ │ ├── meta.json
│ │ └── quickstart.mdx
│ ├── package.json
│ ├── react-router.config.ts
│ ├── server.log
│ ├── source.config.ts
│ ├── test-browser.js
│ ├── test-search.js
│ ├── test.ts
│ ├── tsconfig.json
│ └── vite.config.ts
├── legacy/
│ ├── GlowbyGenius.md
│ ├── README.md
│ ├── app/
│ │ ├── .gitignore
│ │ ├── .metadata
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── analysis_options.yaml
│ │ ├── android/
│ │ │ ├── .gitignore
│ │ │ ├── app/
│ │ │ │ ├── build.gradle
│ │ │ │ └── src/
│ │ │ │ ├── debug/
│ │ │ │ │ └── AndroidManifest.xml
│ │ │ │ ├── main/
│ │ │ │ │ ├── AndroidManifest.xml
│ │ │ │ │ ├── kotlin/
│ │ │ │ │ │ └── com/
│ │ │ │ │ │ └── example/
│ │ │ │ │ │ └── pwa/
│ │ │ │ │ │ └── MainActivity.kt
│ │ │ │ │ └── res/
│ │ │ │ │ ├── drawable/
│ │ │ │ │ │ └── launch_background.xml
│ │ │ │ │ └── values/
│ │ │ │ │ └── styles.xml
│ │ │ │ └── profile/
│ │ │ │ └── AndroidManifest.xml
│ │ │ ├── build.gradle
│ │ │ ├── gradle/
│ │ │ │ └── wrapper/
│ │ │ │ └── gradle-wrapper.properties
│ │ │ ├── gradle.properties
│ │ │ └── settings.gradle
│ │ ├── assets/
│ │ │ ├── talk.glowbom
│ │ │ ├── test.html
│ │ │ └── website.html
│ │ ├── ios/
│ │ │ ├── .gitignore
│ │ │ ├── Flutter/
│ │ │ │ ├── .last_build_id
│ │ │ │ ├── AppFrameworkInfo.plist
│ │ │ │ ├── Debug.xcconfig
│ │ │ │ └── Release.xcconfig
│ │ │ ├── Podfile
│ │ │ ├── Runner/
│ │ │ │ ├── AppDelegate.swift
│ │ │ │ ├── Assets.xcassets/
│ │ │ │ │ ├── AppIcon.appiconset/
│ │ │ │ │ │ └── Contents.json
│ │ │ │ │ └── LaunchImage.imageset/
│ │ │ │ │ ├── Contents.json
│ │ │ │ │ └── README.md
│ │ │ │ ├── Base.lproj/
│ │ │ │ │ ├── LaunchScreen.storyboard
│ │ │ │ │ └── Main.storyboard
│ │ │ │ ├── Glowby.swift
│ │ │ │ ├── Info.plist
│ │ │ │ └── Runner-Bridging-Header.h
│ │ │ ├── Runner.xcodeproj/
│ │ │ │ ├── project.pbxproj
│ │ │ │ ├── project.xcworkspace/
│ │ │ │ │ ├── contents.xcworkspacedata
│ │ │ │ │ └── xcshareddata/
│ │ │ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ │ │ └── WorkspaceSettings.xcsettings
│ │ │ │ └── xcshareddata/
│ │ │ │ └── xcschemes/
│ │ │ │ └── Runner.xcscheme
│ │ │ └── Runner.xcworkspace/
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata/
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ │ ├── lib/
│ │ │ ├── main.dart
│ │ │ ├── models/
│ │ │ │ └── ai.dart
│ │ │ ├── services/
│ │ │ │ ├── hugging_face_api.dart
│ │ │ │ ├── openai_api.dart
│ │ │ │ └── pulze_ai_api.dart
│ │ │ ├── utils/
│ │ │ │ ├── color_utils.dart
│ │ │ │ ├── text_to_speech.dart
│ │ │ │ ├── timestamp.dart
│ │ │ │ ├── utils.dart
│ │ │ │ ├── utils_desktop.dart
│ │ │ │ ├── utils_stub.dart
│ │ │ │ └── utils_web.dart
│ │ │ └── views/
│ │ │ ├── dialogs/
│ │ │ │ ├── ai_error_dialog.dart
│ │ │ │ ├── ai_settings_dialog.dart
│ │ │ │ └── api_key_dialog.dart
│ │ │ ├── html/
│ │ │ │ ├── html_view_screen.dart
│ │ │ │ ├── html_view_screen_desktop.dart
│ │ │ │ ├── html_view_screen_interface.dart
│ │ │ │ ├── html_view_screen_mobile.dart
│ │ │ │ ├── html_view_screen_stub.dart
│ │ │ │ └── html_view_screen_web.dart
│ │ │ ├── screens/
│ │ │ │ ├── chat_screen.dart
│ │ │ │ ├── global_settings.dart
│ │ │ │ ├── magical_loading_view.dart
│ │ │ │ └── talk_screen.dart
│ │ │ └── widgets/
│ │ │ ├── message.dart
│ │ │ ├── message_bubble.dart
│ │ │ ├── messages.dart
│ │ │ ├── new_message.dart
│ │ │ ├── paint_window.dart
│ │ │ └── tasks_view.dart
│ │ ├── linux/
│ │ │ ├── .gitignore
│ │ │ ├── CMakeLists.txt
│ │ │ ├── flutter/
│ │ │ │ ├── CMakeLists.txt
│ │ │ │ ├── generated_plugin_registrant.cc
│ │ │ │ ├── generated_plugin_registrant.h
│ │ │ │ └── generated_plugins.cmake
│ │ │ ├── main.cc
│ │ │ ├── my_application.cc
│ │ │ └── my_application.h
│ │ ├── macos/
│ │ │ ├── .gitignore
│ │ │ ├── Flutter/
│ │ │ │ ├── Flutter-Debug.xcconfig
│ │ │ │ ├── Flutter-Release.xcconfig
│ │ │ │ └── GeneratedPluginRegistrant.swift
│ │ │ ├── Podfile
│ │ │ ├── Runner/
│ │ │ │ ├── AppDelegate.swift
│ │ │ │ ├── Assets.xcassets/
│ │ │ │ │ └── AppIcon.appiconset/
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Base.lproj/
│ │ │ │ │ └── MainMenu.xib
│ │ │ │ ├── Configs/
│ │ │ │ │ ├── AppInfo.xcconfig
│ │ │ │ │ ├── Debug.xcconfig
│ │ │ │ │ ├── Release.xcconfig
│ │ │ │ │ └── Warnings.xcconfig
│ │ │ │ ├── DebugProfile.entitlements
│ │ │ │ ├── Info.plist
│ │ │ │ ├── MainFlutterWindow.swift
│ │ │ │ └── Release.entitlements
│ │ │ ├── Runner.xcodeproj/
│ │ │ │ ├── project.pbxproj
│ │ │ │ ├── project.xcworkspace/
│ │ │ │ │ └── xcshareddata/
│ │ │ │ │ └── IDEWorkspaceChecks.plist
│ │ │ │ └── xcshareddata/
│ │ │ │ └── xcschemes/
│ │ │ │ └── Runner.xcscheme
│ │ │ ├── Runner.xcworkspace/
│ │ │ │ ├── contents.xcworkspacedata
│ │ │ │ └── xcshareddata/
│ │ │ │ └── IDEWorkspaceChecks.plist
│ │ │ └── RunnerTests/
│ │ │ └── RunnerTests.swift
│ │ ├── pubspec.yaml
│ │ ├── web/
│ │ │ ├── index.html
│ │ │ ├── manifest.json
│ │ │ └── tv.js
│ │ └── windows/
│ │ ├── .gitignore
│ │ ├── CMakeLists.txt
│ │ ├── flutter/
│ │ │ ├── CMakeLists.txt
│ │ │ ├── generated_plugin_registrant.cc
│ │ │ ├── generated_plugin_registrant.h
│ │ │ └── generated_plugins.cmake
│ │ └── runner/
│ │ ├── CMakeLists.txt
│ │ ├── Runner.rc
│ │ ├── flutter_window.cpp
│ │ ├── flutter_window.h
│ │ ├── main.cpp
│ │ ├── resource.h
│ │ ├── runner.exe.manifest
│ │ ├── utils.cpp
│ │ ├── utils.h
│ │ ├── win32_window.cpp
│ │ └── win32_window.h
│ ├── backend/
│ │ └── aws/
│ │ └── fetchImageAsBase64/
│ │ └── index.mjs
│ ├── dist/
│ │ ├── assets/
│ │ │ ├── AssetManifest.bin.json
│ │ │ ├── AssetManifest.json
│ │ │ ├── FontManifest.json
│ │ │ ├── NOTICES
│ │ │ ├── assets/
│ │ │ │ └── talk.glowbom
│ │ │ ├── fonts/
│ │ │ │ └── MaterialIcons-Regular.otf
│ │ │ └── shaders/
│ │ │ └── ink_sparkle.frag
│ │ ├── canvaskit/
│ │ │ ├── canvaskit.js
│ │ │ ├── canvaskit.js.symbols
│ │ │ ├── canvaskit.wasm
│ │ │ ├── chromium/
│ │ │ │ ├── canvaskit.js
│ │ │ │ ├── canvaskit.js.symbols
│ │ │ │ └── canvaskit.wasm
│ │ │ ├── skwasm.js
│ │ │ ├── skwasm.js.symbols
│ │ │ ├── skwasm.wasm
│ │ │ └── skwasm.worker.js
│ │ ├── flutter.js
│ │ ├── flutter_bootstrap.js
│ │ ├── flutter_service_worker.js
│ │ ├── index.html
│ │ ├── manifest.json
│ │ ├── tv.js
│ │ └── version.json
│ └── docs/
│ └── README.md
├── project/
│ ├── AGENTS.md
│ ├── android/
│ │ ├── .gitignore
│ │ ├── .idea/
│ │ │ ├── .gitignore
│ │ │ ├── .name
│ │ │ ├── compiler.xml
│ │ │ ├── deploymentTargetDropDown.xml
│ │ │ ├── gradle.xml
│ │ │ ├── inspectionProfiles/
│ │ │ │ └── Project_Default.xml
│ │ │ ├── kotlinc.xml
│ │ │ ├── migrations.xml
│ │ │ ├── misc.xml
│ │ │ └── vcs.xml
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── .gitignore
│ │ │ ├── build.gradle.kts
│ │ │ ├── proguard-rules.pro
│ │ │ └── src/
│ │ │ └── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── assets/
│ │ │ │ └── custom.glowbom
│ │ │ ├── java/
│ │ │ │ └── com/
│ │ │ │ └── glowbom/
│ │ │ │ └── custom/
│ │ │ │ ├── AiExtensions.kt
│ │ │ │ ├── MainActivity.kt
│ │ │ │ └── ui/
│ │ │ │ └── theme/
│ │ │ │ ├── Color.kt
│ │ │ │ ├── Theme.kt
│ │ │ │ └── Type.kt
│ │ │ └── res/
│ │ │ ├── drawable/
│ │ │ │ └── ic_launcher_background.xml
│ │ │ ├── drawable-v24/
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── mipmap-anydpi-v26/
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── values/
│ │ │ │ ├── colors.xml
│ │ │ │ ├── strings.xml
│ │ │ │ └── themes.xml
│ │ │ └── xml/
│ │ │ ├── backup_rules.xml
│ │ │ └── data_extraction_rules.xml
│ │ ├── build.gradle.kts
│ │ ├── gradle/
│ │ │ └── wrapper/
│ │ │ └── gradle-wrapper.properties
│ │ ├── gradle.properties
│ │ ├── gradlew
│ │ ├── gradlew.bat
│ │ └── settings.gradle.kts
│ ├── apple/
│ │ ├── Custom/
│ │ │ ├── AiExtensions.swift
│ │ │ ├── Assets.xcassets/
│ │ │ │ ├── AccentColor.colorset/
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── AppIcon.appiconset/
│ │ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ │ ├── ContentView.swift
│ │ │ ├── Custom.entitlements
│ │ │ ├── CustomApp.swift
│ │ │ ├── Preview Content/
│ │ │ │ └── Preview Assets.xcassets/
│ │ │ │ └── Contents.json
│ │ │ └── custom.glowbom
│ │ ├── Custom.xcodeproj/
│ │ │ ├── project.pbxproj
│ │ │ ├── project.xcworkspace/
│ │ │ │ ├── contents.xcworkspacedata
│ │ │ │ ├── xcshareddata/
│ │ │ │ │ └── IDEWorkspaceChecks.plist
│ │ │ │ └── xcuserdata/
│ │ │ │ └── jacobilin.xcuserdatad/
│ │ │ │ └── UserInterfaceState.xcuserstate
│ │ │ └── xcuserdata/
│ │ │ └── jacobilin.xcuserdatad/
│ │ │ └── xcschemes/
│ │ │ └── xcschememanagement.plist
│ │ └── README.md
│ ├── glowbom.json
│ ├── prototype/
│ │ ├── assets.json
│ │ └── index.html
│ └── web/
│ ├── .gitignore
│ ├── README.md
│ ├── components.json
│ ├── eslint.config.mjs
│ ├── next.config.mjs
│ ├── package.json
│ ├── postcss.config.js
│ ├── public/
│ │ └── custom.glowbom
│ ├── src/
│ │ ├── app/
│ │ │ ├── components/
│ │ │ │ ├── AiExtensions.tsx
│ │ │ │ └── Custom.tsx
│ │ │ ├── globals.css
│ │ │ ├── layout.tsx
│ │ │ └── page.tsx
│ │ ├── components/
│ │ │ └── ui/
│ │ │ ├── button.tsx
│ │ │ ├── card.tsx
│ │ │ ├── dialog.tsx
│ │ │ ├── dropdown-menu.tsx
│ │ │ ├── form.tsx
│ │ │ ├── input.tsx
│ │ │ ├── label.tsx
│ │ │ ├── pagination.tsx
│ │ │ ├── progress.tsx
│ │ │ ├── tabs.tsx
│ │ │ ├── toast.tsx
│ │ │ ├── toaster.tsx
│ │ │ ├── toggle.tsx
│ │ │ ├── tooltip.tsx
│ │ │ └── use-toast.ts
│ │ ├── lib/
│ │ │ └── utils.ts
│ │ └── models/
│ │ └── types.ts
│ ├── tailwind.config.ts
│ └── tsconfig.json
├── scripts/
│ └── install.sh
└── web/
├── LICENSE
├── index.html
├── package.json
├── src/
│ ├── App.tsx
│ ├── hooks/
│ │ └── useRefineRun.ts
│ ├── lib/
│ │ ├── api.ts
│ │ ├── console-render.ts
│ │ ├── model-catalog.ts
│ │ ├── server-auth.ts
│ │ └── sse.ts
│ ├── main.tsx
│ ├── styles.css
│ └── types/
│ └── opencode.ts
├── tsconfig.json
└── vite.config.ts
SYMBOL INDEX (1709 symbols across 119 files)
FILE: backend/backend_info_page.go
constant glowbyRepositoryURL (line 14) | glowbyRepositoryURL = "https://github.com/glowbom/glowby"
constant glowbomWebsiteURL (line 15) | glowbomWebsiteURL = "https://glowbom.com"
constant codexAppServerURL (line 16) | codexAppServerURL = "https://developers.openai.com/codex/app-server/"
constant glowbomDesktopPDFPath (line 17) | glowbomDesktopPDFPath = "docs/2026/Glowbom_Desktop_A_Sketch_to_Software_...
type backendInfo (line 20) | type backendInfo struct
type backendInfoLink (line 31) | type backendInfoLink struct
type backendDriver (line 36) | type backendDriver struct
function resolveGlowbomDesktopPDFPath (line 43) | func resolveGlowbomDesktopPDFPath() (string, bool) {
function resolveGlowbyPublicAssetPath (line 64) | func resolveGlowbyPublicAssetPath(fileName string) (string, bool) {
function serveGlowbyPublicAsset (line 90) | func serveGlowbyPublicAsset(w http.ResponseWriter, r *http.Request, file...
function glowbyFaviconHandler (line 106) | func glowbyFaviconHandler(w http.ResponseWriter, r *http.Request) {
function glowbyLogoSVGHandler (line 110) | func glowbyLogoSVGHandler(w http.ResponseWriter, r *http.Request) {
function glowbyBackendInfoPayload (line 114) | func glowbyBackendInfoPayload() backendInfo {
function normalizedLinkHref (line 156) | func normalizedLinkHref(raw string) string {
function glowbyBackendHomeHandler (line 164) | func glowbyBackendHomeHandler(w http.ResponseWriter, r *http.Request) {
function openCodeAboutHandler (line 445) | func openCodeAboutHandler(w http.ResponseWriter, r *http.Request) {
function openCodeProjectDescriptionHandler (line 455) | func openCodeProjectDescriptionHandler(w http.ResponseWriter, r *http.Re...
FILE: backend/claude_anthropic.go
constant claudeSonnetDefaultModel (line 15) | claudeSonnetDefaultModel = "claude-sonnet-4-6"
constant claudeOpusDefaultModel (line 16) | claudeOpusDefaultModel = "claude-opus-4-6"
function isClaudeOpusModel (line 18) | func isClaudeOpusModel(modelID string) bool {
function claudeModelTokenRates (line 22) | func claudeModelTokenRates(modelID string) (inputCostPer1M float64, outp...
function callClaudeDrawToCodeApiFull (line 30) | func callClaudeDrawToCodeApiFull(imageBase64, userPrompt, template, imag...
function callClaudeDrawToCodeApiFullWithModel (line 35) | func callClaudeDrawToCodeApiFullWithModel(imageBase64, userPrompt, templ...
function callClaudeDrawToCodeStreaming (line 101) | func callClaudeDrawToCodeStreaming(w http.ResponseWriter, imageBase64, u...
function callClaudeApiGo (line 141) | func callClaudeApiGo(prevMsgs []ChatMessage, newMsg, apiKey string) (*R1...
function callClaudeApiGoWithModel (line 146) | func callClaudeApiGoWithModel(prevMsgs []ChatMessage, newMsg, apiKey, mo...
function callClaudeAPI (line 198) | func callClaudeAPI(messages []map[string]interface{}, systemPrompt, apiK...
function callClaudeAPIWithThinkingBudget (line 203) | func callClaudeAPIWithThinkingBudget(messages []map[string]interface{}, ...
function callClaudeAPIWithThinkingBudgetAndModel (line 208) | func callClaudeAPIWithThinkingBudgetAndModel(messages []map[string]inter...
function getMagicEditTool (line 304) | func getMagicEditTool() map[string]interface{} {
function callClaudeChatStreaming (line 322) | func callClaudeChatStreaming(w http.ResponseWriter, prevMsgs []ChatMessa...
function callClaudeChatStreamingWithModel (line 327) | func callClaudeChatStreamingWithModel(w http.ResponseWriter, prevMsgs []...
function callClaudeChatStreamingWithModelAndTools (line 332) | func callClaudeChatStreamingWithModelAndTools(w http.ResponseWriter, pre...
function callClaudeAPIStreaming (line 363) | func callClaudeAPIStreaming(w http.ResponseWriter, messages []map[string...
function callClaudeAPIStreamingWithModel (line 368) | func callClaudeAPIStreamingWithModel(w http.ResponseWriter, messages []m...
function callClaudeAPIStreamingWithModelAndTools (line 373) | func callClaudeAPIStreamingWithModelAndTools(w http.ResponseWriter, mess...
FILE: backend/codex_chatgpt.go
function buildCodexInput (line 19) | func buildCodexInput(prevMsgs []ChatMessage, newMsg string) []interface{} {
function buildCodexRequestBody (line 51) | func buildCodexRequestBody(instructions string, input []interface{}, rea...
function callChatGPTCodexStreaming (line 74) | func callChatGPTCodexStreaming(w http.ResponseWriter, prevMsgs []ChatMes...
function callChatGPTCodexDrawToCodeStreaming (line 98) | func callChatGPTCodexDrawToCodeStreaming(w http.ResponseWriter, imageBas...
function callChatGPTCodexNonStreaming (line 126) | func callChatGPTCodexNonStreaming(prevMsgs []ChatMessage, newMsg, access...
function doChatGPTCodexRequest (line 213) | func doChatGPTCodexRequest(w http.ResponseWriter, reqBody map[string]int...
function setChatGPTCodexHeaders (line 428) | func setChatGPTCodexHeaders(req *http.Request, accessToken, accountID st...
FILE: backend/elevenlabs_audio.go
constant elevenLabsBaseURL (line 20) | elevenLabsBaseURL = "https://api.elevenlabs.io"
constant defaultElevenVoiceID (line 21) | defaultElevenVoiceID = "JBFqnCBsd6RMkjVDRZzb"
constant defaultElevenVoiceModel (line 22) | defaultElevenVoiceModel = "eleven_multilingual_v2"
constant defaultElevenOutputFormat (line 23) | defaultElevenOutputFormat = "mp3_44100_128"
type ElevenLabsAudioRequest (line 26) | type ElevenLabsAudioRequest struct
type ElevenLabsAudioResponse (line 41) | type ElevenLabsAudioResponse struct
type ElevenLabsVoicesRequest (line 50) | type ElevenLabsVoicesRequest struct
type ElevenLabsVoiceOption (line 54) | type ElevenLabsVoiceOption struct
type ElevenLabsVoicesResponse (line 63) | type ElevenLabsVoicesResponse struct
function listElevenLabsVoicesHandler (line 67) | func listElevenLabsVoicesHandler(w http.ResponseWriter, r *http.Request) {
function generateAudioHandler (line 98) | func generateAudioHandler(w http.ResponseWriter, r *http.Request) {
function callElevenLabsVoice (line 182) | func callElevenLabsVoice(req ElevenLabsAudioRequest, apiKey string) ([]b...
function callElevenLabsSound (line 215) | func callElevenLabsSound(req ElevenLabsAudioRequest, apiKey string) ([]b...
function callElevenLabsMusic (line 243) | func callElevenLabsMusic(req ElevenLabsAudioRequest, apiKey string) ([]b...
function callElevenLabsBinaryAPI (line 268) | func callElevenLabsBinaryAPI(endpoint string, query neturl.Values, body ...
function fetchElevenLabsVoices (line 316) | func fetchElevenLabsVoices(apiKey string) ([]ElevenLabsVoiceOption, erro...
function normalizeAudioType (line 380) | func normalizeAudioType(value string) string {
function normalizeElevenLabsVoiceModel (line 393) | func normalizeElevenLabsVoiceModel(value string) string {
function isElevenLabsModelNotFound (line 407) | func isElevenLabsModelNotFound(err error) bool {
function sanitizeElevenLabsError (line 416) | func sanitizeElevenLabsError(err error) string {
function truncateElevenLabsLog (line 423) | func truncateElevenLabsLog(value string, max int) string {
function inferAudioMimeType (line 434) | func inferAudioMimeType(contentType string, data []byte) string {
function extensionForAudioMimeType (line 477) | func extensionForAudioMimeType(mimeType string) string {
FILE: backend/fireworks_fireworks.go
constant fireworksChatCompletionsURL (line 15) | fireworksChatCompletionsURL = "https://api.fireworks.ai/inference/v1/cha...
constant fireworksChatMaxTokens (line 18) | fireworksChatMaxTokens = 4096
constant fireworksDrawToCodeMaxTokens (line 19) | fireworksDrawToCodeMaxTokens = 16384
constant fireworksDefaultTemperature (line 20) | fireworksDefaultTemperature = 0.6
constant fireworksStreamNormalizeHoldback (line 22) | fireworksStreamNormalizeHoldback = 256
function callFireworksDrawToCodeApiFull (line 41) | func callFireworksDrawToCodeApiFull(imageBase64, userPrompt, template, i...
function callFireworksDrawToCodeStreaming (line 68) | func callFireworksDrawToCodeStreaming(w http.ResponseWriter, imageBase64...
function callFireworksApiGo (line 78) | func callFireworksApiGo(prevMsgs []ChatMessage, newMsg, apiKey, firework...
function callFireworksChatStreaming (line 104) | func callFireworksChatStreaming(w http.ResponseWriter, prevMsgs []ChatMe...
function buildFireworksChatMessages (line 112) | func buildFireworksChatMessages(prevMsgs []ChatMessage, newMsg, firework...
function callFireworksChatCompletions (line 171) | func callFireworksChatCompletions(messages []map[string]interface{}, api...
function callFireworksChatCompletionsStreaming (line 270) | func callFireworksChatCompletionsStreaming(w http.ResponseWriter, messag...
function fireworkContentToText (line 623) | func fireworkContentToText(content interface{}) string {
function estimateTokenCountForMessages (line 663) | func estimateTokenCountForMessages(messages []map[string]interface{}) int {
function estimateTokenCount (line 673) | func estimateTokenCount(text string) int {
function fireworksMessagesContainImage (line 681) | func fireworksMessagesContainImage(messages []map[string]interface{}) bo...
function fireworksModelSupportsImageInput (line 704) | func fireworksModelSupportsImageInput(modelID string) bool {
function fireworksAttachmentDataURI (line 714) | func fireworksAttachmentDataURI(attachmentBase64, attachmentMime string)...
function buildFireworksDrawToCodeMessages (line 726) | func buildFireworksDrawToCodeMessages(imageBase64, userPrompt, template,...
function describeImageWithFireworksKimi (line 821) | func describeImageWithFireworksKimi(imageBase64, apiKey string) (string,...
function emitFireworksStreamChunk (line 911) | func emitFireworksStreamChunk(w http.ResponseWriter, content string) err...
function emitFireworksStreamDone (line 925) | func emitFireworksStreamDone(w http.ResponseWriter, promptTokens, comple...
function logFireworksRequestSummary (line 948) | func logFireworksRequestSummary(model string, stream bool, hasImage bool...
function fireworksLatestUserPreview (line 962) | func fireworksLatestUserPreview(messages []map[string]interface{}) string {
function normalizeFinishReason (line 976) | func normalizeFinishReason(reason interface{}) string {
function detectFireworksEmbeddedImagePayload (line 985) | func detectFireworksEmbeddedImagePayload(content string) (string, bool) {
function embeddedImagePayloadError (line 1011) | func embeddedImagePayloadError(reason string) error {
function normalizeFireworksDrawToCodeOutput (line 1015) | func normalizeFireworksDrawToCodeOutput(content string) string {
function extractFireworksThinkingStepTitle (line 1038) | func extractFireworksThinkingStepTitle(raw string) string {
function isMeaningfulThinkingStepTitle (line 1078) | func isMeaningfulThinkingStepTitle(title string) bool {
FILE: backend/gemini_google.go
function isGeminiModelNotFound (line 15) | func isGeminiModelNotFound(statusCode int, body string) bool {
function callGeminiDrawToCodeApiFull (line 25) | func callGeminiDrawToCodeApiFull(imageBase64, userPrompt, template, imag...
function callGeminiDrawToCodeStreaming (line 95) | func callGeminiDrawToCodeStreaming(w http.ResponseWriter, imageBase64, u...
function callGeminiApiGo (line 175) | func callGeminiApiGo(prevMsgs []ChatMessage, newMsg, apiKey, geminiModel...
function callGeminiAPI (line 268) | func callGeminiAPI(contents []map[string]interface{}, apiKey string, max...
function getGeminiMagicEditTool (line 401) | func getGeminiMagicEditTool() map[string]interface{} {
function callGeminiChatStreaming (line 423) | func callGeminiChatStreaming(w http.ResponseWriter, prevMsgs []ChatMessa...
function callGeminiChatStreamingWithTools (line 428) | func callGeminiChatStreamingWithTools(w http.ResponseWriter, prevMsgs []...
function callGeminiAPIStreaming (line 508) | func callGeminiAPIStreaming(w http.ResponseWriter, contents []map[string...
function callGeminiAPIStreamingWithTools (line 513) | func callGeminiAPIStreamingWithTools(w http.ResponseWriter, contents []m...
FILE: backend/gemini_image.go
constant nanoBanana2ModelID (line 13) | nanoBanana2ModelID = "gemini-3.1-flash-image-preview"
function callGeminiImageGeneration (line 17) | func callGeminiImageGeneration(prompt string, aspectRatio string, output...
function callGeminiImageGenerationWithReference (line 133) | func callGeminiImageGenerationWithReference(prompt string, referenceImag...
FILE: backend/gemini_text_with_attachment.go
function callGeminiApiGoWithAttachment (line 4) | func callGeminiApiGoWithAttachment(prevMsgs []ChatMessage, newMsg, attac...
FILE: backend/gemini_veo.go
type VeoImageInput (line 16) | type VeoImageInput struct
type VeoVideoAsset (line 21) | type VeoVideoAsset struct
type VeoGenerationRequest (line 26) | type VeoGenerationRequest struct
type VeoGenerationResponse (line 39) | type VeoGenerationResponse struct
type VeoPollRequest (line 44) | type VeoPollRequest struct
type VeoPollResponse (line 51) | type VeoPollResponse struct
function startVeoVideoGeneration (line 62) | func startVeoVideoGeneration(req VeoGenerationRequest) (*VeoGenerationRe...
function pollVeoOperation (line 214) | func pollVeoOperation(operationID, apiKey string) (*VeoPollResponse, err...
FILE: backend/gemini_video.go
function callGeminiVideoAnalysis (line 15) | func callGeminiVideoAnalysis(videoBase64, mimeType, prompt, apiKey strin...
function callGeminiAPIWithModel (line 64) | func callGeminiAPIWithModel(contents []map[string]interface{}, apiKey st...
FILE: backend/gpt5_openai.go
function callGPT5DrawToCodeApiFull (line 16) | func callGPT5DrawToCodeApiFull(imageBase64, userPrompt, template, imageS...
function callGPT5DrawToCodeStreaming (line 77) | func callGPT5DrawToCodeStreaming(w http.ResponseWriter, imageBase64, use...
function callGPT5ApiGo (line 118) | func callGPT5ApiGo(prevMsgs []ChatMessage, newMsg, apiKey, openAIModel s...
function callGPT5API (line 180) | func callGPT5API(messages []map[string]interface{}, apiKey string, maxTo...
function callGPT5ChatStreaming (line 294) | func callGPT5ChatStreaming(w http.ResponseWriter, prevMsgs []ChatMessage...
function callGPT5APIStreaming (line 337) | func callGPT5APIStreaming(w http.ResponseWriter, messages []map[string]i...
FILE: backend/gpt5_responses_api.go
function callGPT5ResponsesAPIStreaming (line 16) | func callGPT5ResponsesAPIStreaming(w http.ResponseWriter, prevMsgs []Cha...
function extractThinkingStepTitle (line 305) | func extractThinkingStepTitle(text string) string {
FILE: backend/grok_image.go
constant xAIImageGenerationURL (line 17) | xAIImageGenerationURL = "https://api.x.ai/v1/images/generations"
constant xAIImageEditURL (line 18) | xAIImageEditURL = "https://api.x.ai/v1/images/edits"
constant xAIImageGenerationModel (line 19) | xAIImageGenerationModel = "grok-imagine-image-pro"
constant xAIImageEditModel (line 20) | xAIImageEditModel = "grok-imagine-image"
constant xAIImageEditResolution (line 21) | xAIImageEditResolution = "1k"
function callGrokImageGeneration (line 26) | func callGrokImageGeneration(prompt, apiKey, aspectRatio string) (string...
function callGrokImageGenerationWithReference (line 44) | func callGrokImageGenerationWithReference(prompt, referenceImageBase64, ...
function callXAIImageAPI (line 69) | func callXAIImageAPI(endpointURL, model string, reqBody map[string]inter...
function ensureImageDataURI (line 109) | func ensureImageDataURI(value, defaultMimeType string) string {
function parseXAIImageGenerationResponse (line 120) | func parseXAIImageGenerationResponse(respBody []byte, apiKey string) (st...
function downloadImageURLAsDataURI (line 161) | func downloadImageURLAsDataURI(imageURL, apiKey string) (string, error) {
FILE: backend/grok_video.go
constant xAIVideoGenerationURL (line 14) | xAIVideoGenerationURL = "https://api.x.ai/v1/videos/generations"
constant xAIVideoGenerationModel (line 15) | xAIVideoGenerationModel = "grok-imagine-video"
function startGrokImagineVideoGeneration (line 18) | func startGrokImagineVideoGeneration(req VeoGenerationRequest) (*VeoGene...
function xaiStartVideoGenerationRequest (line 156) | func xaiStartVideoGenerationRequest(payload map[string]interface{}, apiK...
function pollGrokImagineVideoOperation (line 183) | func pollGrokImagineVideoOperation(operationID, apiKey string) (*VeoPoll...
function normalizeXAIVideoDurationSeconds (line 313) | func normalizeXAIVideoDurationSeconds(value int) int {
function normalizeXAIVideoResolution (line 326) | func normalizeXAIVideoResolution(value string) string {
function veoImageInputToDataURI (line 337) | func veoImageInputToDataURI(input VeoImageInput) string {
function normalizeVideoAspectRatio (line 353) | func normalizeVideoAspectRatio(value string) string {
function xaiExtractVideoURL (line 365) | func xaiExtractVideoURL(raw map[string]interface{}) string {
function xaiExtractAspectRatio (line 416) | func xaiExtractAspectRatio(raw map[string]interface{}) string {
function xaiExtractVideoStatus (line 447) | func xaiExtractVideoStatus(raw map[string]interface{}) string {
function xaiExtractErrorMessage (line 473) | func xaiExtractErrorMessage(raw map[string]interface{}) string {
function xaiErrorValue (line 497) | func xaiErrorValue(v interface{}) string {
function xaiMapStringValue (line 508) | func xaiMapStringValue(m map[string]interface{}, keys ...string) string {
function xaiStringValue (line 519) | func xaiStringValue(v interface{}) string {
function xaiAsMap (line 530) | func xaiAsMap(v interface{}) map[string]interface{} {
function xaiAsSlice (line 535) | func xaiAsSlice(v interface{}) []interface{} {
function truncateBodyForLog (line 540) | func truncateBodyForLog(body []byte, maxLen int) string {
FILE: backend/grok_xai.go
function findValidUTF8CutPoint (line 18) | func findValidUTF8CutPoint(s string, maxPos int) int {
function callGrok4DrawToCodeApiFull (line 32) | func callGrok4DrawToCodeApiFull(imageBase64, userPrompt, template, image...
function callGrok4DrawToCodeStreaming (line 92) | func callGrok4DrawToCodeStreaming(w http.ResponseWriter, imageBase64, us...
function callGrok4ApiGo (line 132) | func callGrok4ApiGo(prevMsgs []ChatMessage, newMsg, apiKey string) (*R1R...
function callGrok4API (line 197) | func callGrok4API(messages []map[string]interface{}, apiKey string, maxT...
function callGrok4ChatStreaming (line 295) | func callGrok4ChatStreaming(w http.ResponseWriter, prevMsgs []ChatMessag...
function callGrok4APIStreaming (line 340) | func callGrok4APIStreaming(w http.ResponseWriter, messages []map[string]...
FILE: backend/handlers.go
function extractProjectName (line 18) | func extractProjectName(response string) (name string, cleaned string) {
function chatWithAIHandler (line 26) | func chatWithAIHandler(w http.ResponseWriter, r *http.Request) {
function writeJSON (line 819) | func writeJSON(w http.ResponseWriter, v interface{}) {
function webSearchHandler (line 824) | func webSearchHandler(w http.ResponseWriter, r *http.Request) {
function analyzeVideoHandler (line 852) | func analyzeVideoHandler(w http.ResponseWriter, r *http.Request) {
function generateImageHandler (line 1004) | func generateImageHandler(w http.ResponseWriter, r *http.Request) {
function normalizeImageAspectRatio (line 1371) | func normalizeImageAspectRatio(value string) string {
function isGrokImagineImageSource (line 1392) | func isGrokImagineImageSource(imageSource string) bool {
function normalizeVideoProvider (line 1403) | func normalizeVideoProvider(videoSource string, hasGeminiKey bool, hasXA...
function generateVeoVideoHandler (line 1421) | func generateVeoVideoHandler(w http.ResponseWriter, r *http.Request) {
function pollVeoOperationHandler (line 1470) | func pollVeoOperationHandler(w http.ResponseWriter, r *http.Request) {
FILE: backend/main.go
function main (line 10) | func main() {
FILE: backend/model_helpers.go
constant defaultOpenAIModelID (line 5) | defaultOpenAIModelID = "gpt-5.4"
type openAITextPricing (line 7) | type openAITextPricing struct
function isOpenAIChatAlias (line 12) | func isOpenAIChatAlias(modelID string) bool {
function normalizeOpenAIModelID (line 21) | func normalizeOpenAIModelID(modelID string) string {
function openAITextPricingForModel (line 37) | func openAITextPricingForModel(modelID string) (openAITextPricing, bool) {
function estimateOpenAITextCost (line 50) | func estimateOpenAITextCost(modelID string, inputTokens, outputTokens in...
function normalizeGeminiModelID (line 59) | func normalizeGeminiModelID(modelID string) string {
function normalizeFireworksModelID (line 76) | func normalizeFireworksModelID(modelID string) string {
function normalizeOpenRouterModelID (line 96) | func normalizeOpenRouterModelID(modelID string) string {
function normalizeOpenCodeZenModelID (line 118) | func normalizeOpenCodeZenModelID(modelID string) string {
function normalizeXAIModelID (line 143) | func normalizeXAIModelID(modelID string) string {
FILE: backend/o3_openai.go
function callo3miniOpenAiDrawToCodeApiFull (line 13) | func callo3miniOpenAiDrawToCodeApiFull(imageBase64, userPrompt, prompt, ...
function callo3miniOpenAiApiGo (line 81) | func callo3miniOpenAiApiGo(prevMsgs []ChatMessage, newMsg, openAiKey str...
function getOpenAiImageDescription (line 133) | func getOpenAiImageDescription(imageBase64, openAiKey string) (string, e...
function callOpenAiSSE (line 197) | func callOpenAiSSE(messages []map[string]interface{}, openAiKey string) ...
FILE: backend/openai_image.go
function callOpenAIImageGeneration (line 17) | func callOpenAIImageGeneration(prompt string, aspectRatio string, output...
function callOpenAIImageGenerationWithReference (line 107) | func callOpenAIImageGenerationWithReference(prompt string, referenceImag...
function shouldUseReferenceImage (line 251) | func shouldUseReferenceImage(prompt string) bool {
FILE: backend/opencode_driver.go
type OpenCodeDriver (line 38) | type OpenCodeDriver struct
method createRefineSession (line 654) | func (d *OpenCodeDriver) createRefineSession(ctx context.Context, proj...
method resolveRefineSession (line 665) | func (d *OpenCodeDriver) resolveRefineSession(ctx context.Context, pro...
method TranslateToProduction (line 1911) | func (d *OpenCodeDriver) TranslateToProduction(ctx context.Context, re...
method TranslateToProductionStreaming (line 2108) | func (d *OpenCodeDriver) TranslateToProductionStreaming(ctx context.Co...
method streamEvents (line 2376) | func (d *OpenCodeDriver) streamEvents(ctx context.Context, w http.Resp...
method collectGeneratedFiles (line 2467) | func (d *OpenCodeDriver) collectGeneratedFiles(ctx context.Context, pr...
method CheckHealth (line 2485) | func (d *OpenCodeDriver) CheckHealth(ctx context.Context) error {
method sendSessionPrompt (line 2646) | func (d *OpenCodeDriver) sendSessionPrompt(
method runAssetPlacementReconcilePass (line 5453) | func (d *OpenCodeDriver) runAssetPlacementReconcilePass(
method abortSessionBestEffort (line 6677) | func (d *OpenCodeDriver) abortSessionBestEffort(sessionID, projectDir,...
method streamEventsToSSE (line 6700) | func (d *OpenCodeDriver) streamEventsToSSE(ctx context.Context, w http...
method streamEventsAndWaitForCompletion (line 6956) | func (d *OpenCodeDriver) streamEventsAndWaitForCompletion(
method resolveQuestionEndpoint (line 7544) | func (d *OpenCodeDriver) resolveQuestionEndpoint(_ context.Context) st...
method hasRecentQuestionReply (line 7553) | func (d *OpenCodeDriver) hasRecentQuestionReply(questionID string) bool {
method beginQuestionReply (line 7573) | func (d *OpenCodeDriver) beginQuestionReply(questionID string) bool {
method markQuestionReplied (line 7597) | func (d *OpenCodeDriver) markQuestionReplied(questionID string) {
method clearQuestionReply (line 7606) | func (d *OpenCodeDriver) clearQuestionReply(questionID string) {
method respondToQuestion (line 7615) | func (d *OpenCodeDriver) respondToQuestion(ctx context.Context, sessio...
method resolvePermissionEndpoint (line 7769) | func (d *OpenCodeDriver) resolvePermissionEndpoint(ctx context.Context...
method respondToPermission (line 7778) | func (d *OpenCodeDriver) respondToPermission(ctx context.Context, sess...
type usageLimitHint (line 52) | type usageLimitHint struct
type openCodeOpenAIAuthState (line 72) | type openCodeOpenAIAuthState struct
constant openAIOAuthIssuer (line 95) | openAIOAuthIssuer = "https://auth.openai.com"
constant openAIOAuthDefaultClientID (line 96) | openAIOAuthDefaultClientID = "app_EMoamEEZ73f0CkXaXp7hrann"
constant openAIOAuthDefaultOriginator (line 97) | openAIOAuthDefaultOriginator = "Codex Desktop"
constant openAIOAuthDefaultRedirectURI (line 98) | openAIOAuthDefaultRedirectURI = "http://localhost:1455/auth/callback"
constant openAIOAuthLoopbackListenAddr (line 99) | openAIOAuthLoopbackListenAddr = "127.0.0.1:1455"
type openCodeRuntimePaths (line 102) | type openCodeRuntimePaths struct
type openCodeAuthStatusResponse (line 109) | type openCodeAuthStatusResponse struct
function shouldSuppressOpenCodeServerLogLine (line 129) | func shouldSuppressOpenCodeServerLogLine(line string) bool {
function shouldSuppressOpenCodeServerStderrInfoLine (line 151) | func shouldSuppressOpenCodeServerStderrInfoLine(line string) bool {
type GlowbomProject (line 164) | type GlowbomProject struct
type Target (line 177) | type Target struct
type ProjectPaths (line 185) | type ProjectPaths struct
type OpenCodeTranslateRequest (line 197) | type OpenCodeTranslateRequest struct
type OpenCodeTranslateResponse (line 223) | type OpenCodeTranslateResponse struct
type OpenAIModelsRequest (line 233) | type OpenAIModelsRequest struct
type OpenAIModelOption (line 243) | type OpenAIModelOption struct
type OpenAIModelsDebug (line 248) | type OpenAIModelsDebug struct
type OpenAIModelsResponse (line 259) | type OpenAIModelsResponse struct
type OpenCodeAvailableModelsRequest (line 267) | type OpenCodeAvailableModelsRequest struct
type OpenCodeProviderModelOption (line 281) | type OpenCodeProviderModelOption struct
type OpenCodeProviderOption (line 286) | type OpenCodeProviderOption struct
type OpenCodeAvailableModelsResponse (line 292) | type OpenCodeAvailableModelsResponse struct
type OpenCodeAuthConnectRequest (line 298) | type OpenCodeAuthConnectRequest struct
type OpenCodeAuthDisconnectRequest (line 305) | type OpenCodeAuthDisconnectRequest struct
type OpenCodeAuthConnectionResponse (line 309) | type OpenCodeAuthConnectionResponse struct
type OpenCodeAuthOAuthStartRequest (line 317) | type OpenCodeAuthOAuthStartRequest struct
type OpenCodeAuthOAuthStartResponse (line 321) | type OpenCodeAuthOAuthStartResponse struct
type OpenCodeAuthOAuthStatusResponse (line 330) | type OpenCodeAuthOAuthStatusResponse struct
type openCodeOpenAIOAuthSession (line 340) | type openCodeOpenAIOAuthSession struct
function isOpenAICodexForwardCompatModelID (line 367) | func isOpenAICodexForwardCompatModelID(modelID string) bool {
function appendOpenAICodexForwardCompatModels (line 375) | func appendOpenAICodexForwardCompatModels(models []OpenAIModelOption, pr...
function previewStringList (line 421) | func previewStringList(values []string, max int) []string {
type OpenCodeQuestionRespondRequest (line 429) | type OpenCodeQuestionRespondRequest struct
type QuestionAnswers (line 438) | type QuestionAnswers
method UnmarshalJSON (line 440) | func (qa *QuestionAnswers) UnmarshalJSON(b []byte) error {
type AnswerByQuestionID (line 461) | type AnswerByQuestionID
method UnmarshalJSON (line 463) | func (abq *AnswerByQuestionID) UnmarshalJSON(b []byte) error {
type OpenCodePermissionRespondRequest (line 485) | type OpenCodePermissionRespondRequest struct
function getAgentPort (line 494) | func getAgentPort() string {
function openCodeServerHostname (line 501) | func openCodeServerHostname() string {
function openCodeServerUsername (line 508) | func openCodeServerUsername() string {
function openCodeServerPassword (line 515) | func openCodeServerPassword() string {
function openCodeServerAuthorizationHeader (line 519) | func openCodeServerAuthorizationHeader() string {
function applyOpenCodeServerAuthorization (line 528) | func applyOpenCodeServerAuthorization(req *http.Request) {
function glowbomOpenCodeRuntimePaths (line 534) | func glowbomOpenCodeRuntimePaths() (openCodeRuntimePaths, error) {
function ensureOpenCodeRuntimeDirs (line 555) | func ensureOpenCodeRuntimeDirs(paths openCodeRuntimePaths) error {
function setEnvValue (line 568) | func setEnvValue(env []string, key, value string) []string {
function normalizeOpenAIAuthMode (line 579) | func normalizeOpenAIAuthMode(mode string) string {
function openAIKeyFingerprint (line 590) | func openAIKeyFingerprint(openAIKey string) string {
function getOpenCodeOpenAIAuthState (line 600) | func getOpenCodeOpenAIAuthState() openCodeOpenAIAuthState {
function setOpenCodeOpenAIAuthState (line 606) | func setOpenCodeOpenAIAuthState(mode, openAIKey string) {
function shouldRestartForOpenAIAuthChange (line 622) | func shouldRestartForOpenAIAuthChange(serverWasAlreadyRunning bool, requ...
function isSessionNotFoundError (line 645) | func isSessionNotFoundError(err error) bool {
function openAIOAuthClientID (line 687) | func openAIOAuthClientID() string {
function openAIOAuthOriginator (line 694) | func openAIOAuthOriginator() string {
function openAIOAuthRedirectURI (line 701) | func openAIOAuthRedirectURI() string {
function randomURLSafeBase64 (line 708) | func randomURLSafeBase64(byteCount int) (string, error) {
function sha256Base64URL (line 719) | func sha256Base64URL(value string) string {
function ensureOpenAIOAuthSessionStore (line 724) | func ensureOpenAIOAuthSessionStore() {
function cleanupExpiredOpenAIOAuthSessionsLocked (line 732) | func cleanupExpiredOpenAIOAuthSessionsLocked(now time.Time) {
function ensureOpenAIOAuthLoopbackServer (line 748) | func ensureOpenAIOAuthLoopbackServer() error {
function openCodeOpenAIOAuthLoopbackCallbackHandler (line 788) | func openCodeOpenAIOAuthLoopbackCallbackHandler(w http.ResponseWriter, r...
function openCodeOpenAIOAuthLoopbackCancelHandler (line 803) | func openCodeOpenAIOAuthLoopbackCancelHandler(w http.ResponseWriter, r *...
function writeOpenAIOAuthCallbackHTML (line 816) | func writeOpenAIOAuthCallbackHTML(w http.ResponseWriter, success bool, m...
function exchangeOpenAIOAuthCodeForCredential (line 853) | func exchangeOpenAIOAuthCodeForCredential(code, codeVerifier, redirectUR...
function isServerRunning (line 911) | func isServerRunning(serverURL string) bool {
function startOpenCodeServer (line 934) | func startOpenCodeServer(openAIKey, anthropicKey, geminiKey, fireworksKe...
function syncOpenAIAuth (line 1098) | func syncOpenAIAuth(serverURL, accessToken, refreshToken string, expires...
function clearOpenAIAuth (line 1154) | func clearOpenAIAuth(serverURL string) error {
function applyOpenAIAuthModeToRunningServer (line 1180) | func applyOpenAIAuthModeToRunningServer(serverURL, openAIAuthMode, openA...
function waitForOpenCodeServerReady (line 1208) | func waitForOpenCodeServerReady(serverURL string, timeout time.Duration)...
function stopOpenCodeServerOnPort (line 1221) | func stopOpenCodeServerOnPort(port string) error {
function restartOpenCodeServer (line 1264) | func restartOpenCodeServer(openAIKey, anthropicKey, geminiKey, fireworks...
function shouldAttemptOpenCodeRestartForProvider (line 1285) | func shouldAttemptOpenCodeRestartForProvider(providerID, openAIKey, anth...
function normalizeOpenCodeProviderID (line 1306) | func normalizeOpenCodeProviderID(providerID string) string {
function isStrictModelPreflightProvider (line 1315) | func isStrictModelPreflightProvider(providerID string) bool {
function ensureOpenCodeModelAvailable (line 1324) | func ensureOpenCodeModelAvailable(projectPath, providerID, modelID strin...
type openCodeProviderModelProbe (line 1395) | type openCodeProviderModelProbe struct
function probeOpenCodeProviderModel (line 1402) | func probeOpenCodeProviderModel(projectPath, providerID, modelID string)...
function openCodeZenAgentModelFallbackCandidates (line 1453) | func openCodeZenAgentModelFallbackCandidates(modelID string) []string {
function ollamaAgentModelFallbackCandidates (line 1486) | func ollamaAgentModelFallbackCandidates(modelID string) []string {
function resolveSessionModelForRequest (line 1543) | func resolveSessionModelForRequest(projectPath, requestedModel string) (...
function ensureOpenCodeServerReady (line 1627) | func ensureOpenCodeServerReady(projectPath, model, openAIKey, anthropicK...
function NewOpenCodeDriver (line 1728) | func NewOpenCodeDriver(serverURL string) *OpenCodeDriver {
function GetProjectPaths (line 1760) | func GetProjectPaths(projectRoot string) ProjectPaths {
function InitProject (line 1774) | func InitProject(projectRoot, name string) (*GlowbomProject, error) {
function LoadProject (line 1814) | func LoadProject(manifestPath string) (*GlowbomProject, error) {
function SaveProject (line 1829) | func SaveProject(manifestPath string, project *GlowbomProject) error {
function SavePrototype (line 1845) | func SavePrototype(projectRoot, html string, assets map[string][]byte) e...
function LoadPrototype (line 1875) | func LoadPrototype(projectRoot string) (string, error) {
function GetTargetDir (line 1888) | func GetTargetDir(projectRoot, targetLang string) string {
function buildTranslationPrompt (line 2498) | func buildTranslationPrompt(sourceHTML, targetLang string) string {
function buildProjectTranslationPrompt (line 2518) | func buildProjectTranslationPrompt(sourceHTML, targetLang, projectRoot, ...
function parseModel (line 2551) | func parseModel(model string) (modelID, providerID string) {
function containsEmbeddedImageDataURI (line 2633) | func containsEmbeddedImageDataURI(content string) bool {
function isIgnorablePromptParseError (line 2638) | func isIgnorablePromptParseError(err error) bool {
function sendSSEEvent (line 2672) | func sendSSEEvent(w http.ResponseWriter, flusher http.Flusher, event, da...
function GetOpenCodeDriver (line 2681) | func GetOpenCodeDriver() *OpenCodeDriver {
function openCodeTranslateHandler (line 2693) | func openCodeTranslateHandler(w http.ResponseWriter, r *http.Request) {
function openCodeInitProjectHandler (line 2775) | func openCodeInitProjectHandler(w http.ResponseWriter, r *http.Request) {
function openCodeGetProjectHandler (line 2827) | func openCodeGetProjectHandler(w http.ResponseWriter, r *http.Request) {
function openCodeRenameProjectHandler (line 2867) | func openCodeRenameProjectHandler(w http.ResponseWriter, r *http.Request) {
function openCodeUpdateProjectSettingsHandler (line 2914) | func openCodeUpdateProjectSettingsHandler(w http.ResponseWriter, r *http...
function openCodeGenerateIconHandler (line 2976) | func openCodeGenerateIconHandler(w http.ResponseWriter, r *http.Request) {
function openCodeProjectIconHandler (line 3063) | func openCodeProjectIconHandler(w http.ResponseWriter, r *http.Request) {
type openCodeOpenAIOAuthCredential (line 3092) | type openCodeOpenAIOAuthCredential struct
function parseRawJSONNumber (line 3099) | func parseRawJSONNumber(raw json.RawMessage) float64 {
function normalizeOAuthExpiresToReferenceSeconds (line 3125) | func normalizeOAuthExpiresToReferenceSeconds(raw float64) float64 {
function isFinite (line 3142) | func isFinite(v float64) bool {
function readOpenAIOAuthCredentialFromOpenCodeAuthFile (line 3146) | func readOpenAIOAuthCredentialFromOpenCodeAuthFile(authFilePath string) ...
function readOpenAIOAuthCredentialFromCodexAuthFile (line 3201) | func readOpenAIOAuthCredentialFromCodexAuthFile() (openCodeOpenAIOAuthCr...
function resolveOpenAIOAuthCredentialForConnect (line 3255) | func resolveOpenAIOAuthCredentialForConnect(req OpenCodeAuthConnectReque...
function clearOpenAIAuthFromAuthFile (line 3291) | func clearOpenAIAuthFromAuthFile(authFilePath string) error {
function persistOpenAIOAuthToAuthFile (line 3322) | func persistOpenAIOAuthToAuthFile(authFilePath string, credential openCo...
function openAICredentialTypeFromAuthFile (line 3373) | func openAICredentialTypeFromAuthFile(authFilePath string) string {
function cachedGlowbomOpenAIAuthMode (line 3422) | func cachedGlowbomOpenAIAuthMode() string {
function currentOpenCodeAuthStatus (line 3434) | func currentOpenCodeAuthStatus() openCodeAuthStatusResponse {
function openCodeAuthStatusHandler (line 3451) | func openCodeAuthStatusHandler(w http.ResponseWriter, r *http.Request) {
function openCodeOpenAIOAuthStartHandler (line 3461) | func openCodeOpenAIOAuthStartHandler(w http.ResponseWriter, r *http.Requ...
function openCodeOpenAIOAuthStatusHandler (line 3533) | func openCodeOpenAIOAuthStatusHandler(w http.ResponseWriter, r *http.Req...
function setOpenAIOAuthSessionFailed (line 3572) | func setOpenAIOAuthSessionFailed(state, message string) {
function setOpenAIOAuthSessionSucceeded (line 3584) | func setOpenAIOAuthSessionSucceeded(state string, status openCodeAuthSta...
function finalizeOpenAIOAuthCallback (line 3596) | func finalizeOpenAIOAuthCallback(state, code, oauthError, oauthErrorDesc...
function openCodeOpenAIOAuthCallbackHandler (line 3677) | func openCodeOpenAIOAuthCallbackHandler(w http.ResponseWriter, r *http.R...
function openCodeOpenAIConnectHandler (line 3692) | func openCodeOpenAIConnectHandler(w http.ResponseWriter, r *http.Request) {
function openCodeOpenAIDisconnectHandler (line 3743) | func openCodeOpenAIDisconnectHandler(w http.ResponseWriter, r *http.Requ...
function openCodeHealthHandler (line 3789) | func openCodeHealthHandler(w http.ResponseWriter, r *http.Request) {
function openAIModelsHandler (line 3827) | func openAIModelsHandler(w http.ResponseWriter, r *http.Request) {
function openCodeAvailableModelsHandler (line 3987) | func openCodeAvailableModelsHandler(w http.ResponseWriter, r *http.Reque...
type OpenCodeAgentRequest (line 4083) | type OpenCodeAgentRequest struct
function openCodeRefineHandler (line 4112) | func openCodeRefineHandler(w http.ResponseWriter, r *http.Request) {
function openCodeVerifyHandler (line 4556) | func openCodeVerifyHandler(w http.ResponseWriter, r *http.Request) {
function openCodeQuestionRespondHandler (line 4770) | func openCodeQuestionRespondHandler(w http.ResponseWriter, r *http.Reque...
function openCodePermissionRespondHandler (line 4805) | func openCodePermissionRespondHandler(w http.ResponseWriter, r *http.Req...
constant maxInstructionAttachmentSizeBytes (line 4832) | maxInstructionAttachmentSizeBytes = int64(40 * 1024 * 1024)
constant maxInstructionAttachmentCount (line 4833) | maxInstructionAttachmentCount = 20
type stagedInstructionAttachment (line 4836) | type stagedInstructionAttachment struct
function hasAnyInstructionAttachmentPath (line 4842) | func hasAnyInstructionAttachmentPath(requestedPaths []string) bool {
function resetCurrentInstructionsDirectory (line 4851) | func resetCurrentInstructionsDirectory(projectPath string) error {
function stageInstructionAttachments (line 4862) | func stageInstructionAttachments(projectPath string, requestedPaths []st...
function stageInstructionTextFile (line 4948) | func stageInstructionTextFile(projectPath, instructions string) (string,...
function mergeInstructionAttachmentContext (line 4967) | func mergeInstructionAttachmentContext(
function sanitizeAttachmentFilename (line 4997) | func sanitizeAttachmentFilename(name string) string {
function uniqueAttachmentFilename (line 5030) | func uniqueAttachmentFilename(baseName string, used map[string]struct{})...
function copyLocalAttachmentFile (line 5052) | func copyLocalAttachmentFile(sourcePath, targetPath string) error {
function humanReadableBytes (line 5072) | func humanReadableBytes(size int64) string {
type agentHistoryAttachmentRecord (line 5094) | type agentHistoryAttachmentRecord struct
type agentHistoryEntryRecord (line 5102) | type agentHistoryEntryRecord struct
function persistCurrentInstructionsHistory (line 5112) | func persistCurrentInstructionsHistory(
function inferMimeTypeForFilename (line 5209) | func inferMimeTypeForFilename(name string) string {
function inferHistoryMediaType (line 5220) | func inferHistoryMediaType(mimeType, name string) string {
function normalizeHistoryStatus (line 5242) | func normalizeHistoryStatus(status string) string {
function truncateText (line 5257) | func truncateText(value string, maxLen int) string {
function randomUUIDString (line 5268) | func randomUUIDString() string {
function buildRefinePrompt (line 5298) | func buildRefinePrompt(projectRoot, instructions string) string {
function loadAgentInstructions (line 5380) | func loadAgentInstructions(projectRoot string) (string, string) {
function buildAssetPlacementPrompt (line 5397) | func buildAssetPlacementPrompt(projectRoot string) string {
type openCodeSessionResult (line 5446) | type openCodeSessionResult struct
function buildVerifyPrompt (line 5559) | func buildVerifyPrompt(projectRoot string) string {
function getPropertyValue (line 5613) | func getPropertyValue(p interface{}, path string) interface{} {
function getPropertyString (line 5667) | func getPropertyString(p interface{}, paths ...string) string {
function getPropertyMap (line 5684) | func getPropertyMap(p interface{}, path string) map[string]interface{} {
function getStringSlice (line 5714) | func getStringSlice(p interface{}, path string) []string {
function extractAssistantMessageText (line 5740) | func extractAssistantMessageText(props interface{}) string {
function extractMessageRole (line 5767) | func extractMessageRole(props interface{}) string {
function looksLikeDriverPromptEcho (line 5780) | func looksLikeDriverPromptEcho(content string) bool {
function getMapValueCaseInsensitive (line 5792) | func getMapValueCaseInsensitive(m map[string]interface{}, key string) in...
function getMapStringCaseInsensitive (line 5801) | func getMapStringCaseInsensitive(m map[string]interface{}, keys ...strin...
function summarizeToolInput (line 5822) | func summarizeToolInput(input map[string]interface{}) string {
type toolPartProgress (line 5844) | type toolPartProgress struct
function extractToolPartProgress (line 5851) | func extractToolPartProgress(props interface{}) (toolPartProgress, bool) {
function formatToolPartProgressLine (line 5934) | func formatToolPartProgressLine(progress toolPartProgress) string {
function joinPartTexts (line 5950) | func joinPartTexts(parts interface{}) string {
function extractPartID (line 5994) | func extractPartID(props interface{}) string {
function extractPartMessageID (line 6005) | func extractPartMessageID(props interface{}) string {
function extractPartDelta (line 6017) | func extractPartDelta(props interface{}) string {
function extractPartSnapshotText (line 6027) | func extractPartSnapshotText(props interface{}) string {
function longestCommonPrefix (line 6045) | func longestCommonPrefix(left, right string) string {
function computePartChunk (line 6065) | func computePartChunk(partID, explicitDelta, snapshotText string, snapsh...
function extractPartChunk (line 6109) | func extractPartChunk(props interface{}, snapshots map[string]string) (c...
function parseEventPropertiesFromRaw (line 6126) | func parseEventPropertiesFromRaw(raw string) map[string]interface{} {
function extractQuestionItems (line 6140) | func extractQuestionItems(props interface{}) []map[string]interface{} {
function extractQuestionFields (line 6176) | func extractQuestionFields(props interface{}) (string, string, string, [...
function extractPermissionFields (line 6228) | func extractPermissionFields(props interface{}) (string, string, string,...
function eventSessionID (line 6259) | func eventSessionID(props interface{}) string {
function extractEventSessionID (line 6277) | func extractEventSessionID(event opencode.EventListResponse) string {
function isUsageLimitErrorMessage (line 6293) | func isUsageLimitErrorMessage(msg string) bool {
function latestUsageLimitHint (line 6310) | func latestUsageLimitHint() (usageLimitHint, bool) {
function extractUsageLimitInt (line 6330) | func extractUsageLimitInt(raw string, pattern *regexp.Regexp) (int64, bo...
function extractUsageLimitResetInSeconds (line 6342) | func extractUsageLimitResetInSeconds(raw string) (int64, bool) {
function extractUsageLimitWindowMinutes (line 6346) | func extractUsageLimitWindowMinutes(raw string) (int64, bool) {
function extractUsageLimitUsedPercent (line 6350) | func extractUsageLimitUsedPercent(raw string) (int64, bool) {
function recordUsageLimitHintFromText (line 6354) | func recordUsageLimitHintFromText(raw string) {
function extractUsageLimitResetTime (line 6404) | func extractUsageLimitResetTime(raw string) (time.Time, bool) {
function formatApproxDuration (line 6420) | func formatApproxDuration(d time.Duration) string {
function formatQuotaWindow (line 6451) | func formatQuotaWindow(windowMinutes int64) string {
function isTransientSessionStatus (line 6466) | func isTransientSessionStatus(value string) bool {
function isIgnorableSessionStatus (line 6476) | func isIgnorableSessionStatus(value string) bool {
function usageLimitContext (line 6515) | func usageLimitContext(message, rawEvent string) string {
function userFacingAgentErrorMessage (line 6539) | func userFacingAgentErrorMessage(raw string) string {
function extractSessionErrorMessage (line 6597) | func extractSessionErrorMessage(props interface{}) string {
function extractSessionStatusMessage (line 6615) | func extractSessionStatusMessage(props interface{}) string {
function normalizeChangedFilePath (line 7386) | func normalizeChangedFilePath(projectDir, filePath string) string {
type projectFileSnapshotEntry (line 7401) | type projectFileSnapshotEntry struct
function captureProjectFileSnapshot (line 7406) | func captureProjectFileSnapshot(projectDir string) (map[string]projectFi...
function detectChangedFilesFromSnapshot (line 7443) | func detectChangedFilesFromSnapshot(projectDir string, before map[string...
function shouldSkipSnapshotDirectory (line 7471) | func shouldSkipSnapshotDirectory(name string) bool {
function mergeChangedFiles (line 7480) | func mergeChangedFiles(existing []string, incoming []string) []string {
function detectPrototypeChanged (line 7505) | func detectPrototypeChanged(changedFiles []string) bool {
function prototypeContainsMediaPlaceholders (line 7515) | func prototypeContainsMediaPlaceholders(projectPath string) bool {
function sendSSEData (line 7530) | func sendSSEData(w http.ResponseWriter, flusher http.Flusher, data map[s...
function buildQuestionReplyPayloads (line 7714) | func buildQuestionReplyPayloads(sessionID, questionID, answer string, an...
function copyStringMap (line 7752) | func copyStringMap(src map[string]interface{}) map[string]interface{} {
function mapKeys (line 7760) | func mapKeys(src map[string]interface{}) []string {
FILE: backend/opencode_folder_picker.go
constant folderPickerCanceledToken (line 15) | folderPickerCanceledToken = "__GLOWBOM_PICKER_CANCELED__"
type openCodeProjectPickResponse (line 17) | type openCodeProjectPickResponse struct
type openCodeInstructionPickedFile (line 25) | type openCodeInstructionPickedFile struct
type openCodeInstructionFilesPickResponse (line 32) | type openCodeInstructionFilesPickResponse struct
type nativePickerCommand (line 40) | type nativePickerCommand struct
function openCodePickProjectFolderHandler (line 46) | func openCodePickProjectFolderHandler(w http.ResponseWriter, r *http.Req...
function openCodePickInstructionFilesHandler (line 111) | func openCodePickInstructionFilesHandler(w http.ResponseWriter, r *http....
function pickProjectFolderMacOS (line 191) | func pickProjectFolderMacOS() (string, bool, error) {
function pickInstructionFilesMacOS (line 220) | func pickInstructionFilesMacOS() ([]string, bool, error) {
function pickProjectFolderLinux (line 269) | func pickProjectFolderLinux() (string, bool, error) {
function pickInstructionFilesLinux (line 303) | func pickInstructionFilesLinux() ([]string, bool, error) {
function pickProjectFolderWindows (line 350) | func pickProjectFolderWindows() (string, bool, error) {
function pickInstructionFilesWindows (line 382) | func pickInstructionFilesWindows() ([]string, bool, error) {
function runPickerCommand (line 429) | func runPickerCommand(cmd *exec.Cmd) (string, error) {
function runPickerCommandWithCancel (line 445) | func runPickerCommandWithCancel(cmd *exec.Cmd, cancelExitCodes ...int) (...
function runLinuxPicker (line 472) | func runLinuxPicker(commands []nativePickerCommand, missingMessage strin...
function defaultPickerStartDir (line 492) | func defaultPickerStartDir() string {
function inferMimeTypeForPath (line 500) | func inferMimeTypeForPath(path string) string {
FILE: backend/opencode_media_postpass.go
constant maxGeneratedImageBytes (line 23) | maxGeneratedImageBytes = 25 * 1024 * 1024
constant maxGeneratedVideoBytes (line 24) | maxGeneratedVideoBytes = 120 * 1024 * 1024
constant maxGeneratedAudioBytes (line 25) | maxGeneratedAudioBytes = 40 * 1024 * 1024
type OpenCodeMediaPostPassRequest (line 35) | type OpenCodeMediaPostPassRequest struct
type OpenCodeMediaPostPassResponse (line 50) | type OpenCodeMediaPostPassResponse struct
type OpenCodeMediaAsset (line 59) | type OpenCodeMediaAsset struct
type OpenCodePlatformCopy (line 69) | type OpenCodePlatformCopy struct
type postPassImageRef (line 75) | type postPassImageRef struct
type postPassVideoPlaceholder (line 81) | type postPassVideoPlaceholder struct
type postPassAudioPlaceholder (line 88) | type postPassAudioPlaceholder struct
type studioAssetRecord (line 100) | type studioAssetRecord struct
type prototypeAssetsManifest (line 108) | type prototypeAssetsManifest struct
type prototypeAssetsManifestItem (line 114) | type prototypeAssetsManifestItem struct
type platformAssetsMap (line 122) | type platformAssetsMap struct
type platformAssetsMapRow (line 127) | type platformAssetsMapRow struct
type platformAssetsMapTargetRow (line 133) | type platformAssetsMapTargetRow struct
function openCodeMediaPostPassHandler (line 138) | func openCodeMediaPostPassHandler(w http.ResponseWriter, r *http.Request) {
function runOpenCodeMediaPostPass (line 166) | func runOpenCodeMediaPostPass(ctx context.Context, req OpenCodeMediaPost...
function materializeImagePlaceholder (line 356) | func materializeImagePlaceholder(
function materializeVideoPlaceholder (line 428) | func materializeVideoPlaceholder(
function materializeAudioPlaceholder (line 471) | func materializeAudioPlaceholder(
function generateElevenLabsAudioForPostPass (line 547) | func generateElevenLabsAudioForPostPass(
function generateImageForPostPass (line 631) | func generateImageForPostPass(req OpenCodeMediaPostPassRequest, prompt, ...
function generateVeoVideoFromImage (line 696) | func generateVeoVideoFromImage(ctx context.Context, prompt, aspectRatio,...
function downloadVeoVideoBinary (line 758) | func downloadVeoVideoBinary(videoURL, apiKey string) ([]byte, error) {
function writePrototypeAssetsManifest (line 796) | func writePrototypeAssetsManifest(projectPath string, assets []OpenCodeM...
function writePlatformAssetsMap (line 859) | func writePlatformAssetsMap(projectPath string, copies []OpenCodePlatfor...
function syncAssetsToPlatforms (line 907) | func syncAssetsToPlatforms(projectPath string) ([]OpenCodePlatformCopy, ...
function copyToAppleAssetCatalog (line 1064) | func copyToAppleAssetCatalog(sourcePath, assetCatalogRoot, mediaType str...
function materializeFromStudioAsset (line 1130) | func materializeFromStudioAsset(asset studioAssetRecord, mediaType strin...
function resolveVideoStartFrame (line 1162) | func resolveVideoStartFrame(fromKey string, imageRefsByKey map[string]po...
function registerImageRefKeys (line 1192) | func registerImageRefKeys(index map[string]postPassImageRef, ref postPas...
type imagePlaceholder (line 1198) | type imagePlaceholder struct
function extractImagePlaceholders (line 1203) | func extractImagePlaceholders(content string) []imagePlaceholder {
function extractVideoPlaceholders (line 1227) | func extractVideoPlaceholders(content string) []postPassVideoPlaceholder {
function parseGlowbyVideoPayload (line 1256) | func parseGlowbyVideoPayload(token, payload string) postPassVideoPlaceho...
function extractAudioPlaceholders (line 1279) | func extractAudioPlaceholders(content string) []postPassAudioPlaceholder {
function parseGlowbyAudioPayload (line 1308) | func parseGlowbyAudioPayload(token, payload string) postPassAudioPlaceho...
function normalizePostPassAudioType (line 1367) | func normalizePostPassAudioType(audioType string, prompt string) string {
function inferAudioTypeFromPrompt (line 1380) | func inferAudioTypeFromPrompt(prompt string) string {
function parseBoolOption (line 1431) | func parseBoolOption(value string) bool {
function floatPointerKey (line 1440) | func floatPointerKey(value *float64) string {
function normalizeAspectRatio (line 1447) | func normalizeAspectRatio(value string) string {
function resolveReferenceImage (line 1456) | func resolveReferenceImage(req OpenCodeMediaPostPassRequest, projectPath...
function loadStudioAssets (line 1503) | func loadStudioAssets() ([]studioAssetRecord, error) {
function findStudioAssetByPrompt (line 1548) | func findStudioAssetByPrompt(assets []studioAssetRecord, mediaType, prom...
function findStudioAssetByKey (line 1577) | func findStudioAssetByKey(assets []studioAssetRecord, mediaType, key, pr...
function resolveScanTargetPath (line 1612) | func resolveScanTargetPath(projectPath, rawTarget string) (string, error) {
function writePrototypeAsset (line 1632) | func writePrototypeAsset(projectPath, filename string, data []byte, allo...
function copyFile (line 1665) | func copyFile(sourcePath, destinationPath string, maxSize int64) error {
function safeProjectPath (line 1685) | func safeProjectPath(projectPath string, pathSegments ...string) (string...
function isPathWithin (line 1704) | func isPathWithin(basePath, targetPath string) bool {
function decodeBase64Payload (line 1713) | func decodeBase64Payload(value string, fallbackMime string) ([]byte, str...
function detectMimeType (line 1752) | func detectMimeType(data []byte, fallback string) string {
function imageExtensionForMimeType (line 1766) | func imageExtensionForMimeType(mimeType string) string {
function audioExtensionForMimeType (line 1777) | func audioExtensionForMimeType(mimeType string) string {
function classifyMediaTypeByExt (line 1794) | func classifyMediaTypeByExt(ext string) string {
function maxAssetBytesForMediaType (line 1807) | func maxAssetBytesForMediaType(mediaType string) int64 {
function deterministicAssetFilename (line 1818) | func deterministicAssetFilename(prefix, prompt, ext string) string {
function resourceSafeName (line 1836) | func resourceSafeName(value string) string {
function normalizedLookupKey (line 1866) | func normalizedLookupKey(value string) string {
function sanitizeProviderError (line 1870) | func sanitizeProviderError(err error) string {
function dedupeWarnings (line 1888) | func dedupeWarnings(warnings []string) []string {
function directoryExists (line 1905) | func directoryExists(path string) bool {
FILE: backend/opencode_project_history.go
type openCodeProjectHistoryAttachmentResponse (line 14) | type openCodeProjectHistoryAttachmentResponse struct
type openCodeProjectHistoryEntryResponse (line 24) | type openCodeProjectHistoryEntryResponse struct
type openCodeProjectHistoryResponse (line 36) | type openCodeProjectHistoryResponse struct
function openCodeProjectHistoryHandler (line 43) | func openCodeProjectHistoryHandler(w http.ResponseWriter, r *http.Reques...
function loadProjectHistoryEntries (line 97) | func loadProjectHistoryEntries(projectPath string) ([]openCodeProjectHis...
function historyEntrySortTime (line 227) | func historyEntrySortTime(timestamp string, folderName string) time.Time {
FILE: backend/opencode_project_open.go
constant openCodeIDEFinder (line 16) | openCodeIDEFinder = "finder"
constant openCodeIDEXcode (line 17) | openCodeIDEXcode = "xcode"
constant openCodeIDEAndroidStudio (line 18) | openCodeIDEAndroidStudio = "android-studio"
constant openCodeIDEVSCode (line 19) | openCodeIDEVSCode = "vscode"
type openCodeProjectIDERequest (line 22) | type openCodeProjectIDERequest struct
type openCodeProjectIDEAction (line 26) | type openCodeProjectIDEAction struct
type openCodeProjectIDEStatusResponse (line 34) | type openCodeProjectIDEStatusResponse struct
type openCodeProjectOpenRequest (line 41) | type openCodeProjectOpenRequest struct
type openCodeProjectOpenResponse (line 46) | type openCodeProjectOpenResponse struct
type openCodeLaunchCommand (line 53) | type openCodeLaunchCommand struct
function openCodeProjectIDEStatusHandler (line 58) | func openCodeProjectIDEStatusHandler(w http.ResponseWriter, r *http.Requ...
function openCodeProjectOpenHandler (line 94) | func openCodeProjectOpenHandler(w http.ResponseWriter, r *http.Request) {
function readProjectPathFromRequest (line 151) | func readProjectPathFromRequest(r *http.Request) (string, error) {
function normalizeExistingDirectoryPath (line 172) | func normalizeExistingDirectoryPath(path string) (string, error) {
function inspectProjectIDEActions (line 197) | func inspectProjectIDEActions(projectPath string) []openCodeProjectIDEAc...
function inspectFinderAction (line 206) | func inspectFinderAction(projectPath string) openCodeProjectIDEAction {
function inspectXcodeAction (line 237) | func inspectXcodeAction(projectPath string) openCodeProjectIDEAction {
function inspectAndroidStudioAction (line 282) | func inspectAndroidStudioAction(projectPath string) openCodeProjectIDEAc...
function inspectVSCodeAction (line 327) | func inspectVSCodeAction(projectPath string) openCodeProjectIDEAction {
function destinationPathForProject (line 370) | func destinationPathForProject(projectPath string, outputDirs []string, ...
function preferredXcodePath (line 380) | func preferredXcodePath(basePath string) string {
function projectDirectoryExists (line 403) | func projectDirectoryExists(path string) bool {
function applicationExists (line 408) | func applicationExists(path string) bool {
function openProjectInIDE (line 413) | func openProjectInIDE(projectPath, ide string) (string, error) {
function openProjectFolder (line 428) | func openProjectFolder(projectPath string) (string, error) {
function openInXcode (line 439) | func openInXcode(projectPath string) (string, error) {
function openInAndroidStudio (line 463) | func openInAndroidStudio(projectPath string) (string, error) {
function openInVSCode (line 485) | func openInVSCode(projectPath string) (string, error) {
function runMacOpenWithApp (line 505) | func runMacOpenWithApp(appPath, targetPath string) error {
function runMacOpenCommand (line 516) | func runMacOpenCommand(targetPath string) error {
function folderOpenLaunchCommand (line 526) | func folderOpenLaunchCommand() (openCodeLaunchCommand, bool) {
function androidStudioLaunchCommand (line 551) | func androidStudioLaunchCommand() (openCodeLaunchCommand, bool) {
function vsCodeLaunchCommand (line 595) | func vsCodeLaunchCommand() (openCodeLaunchCommand, bool) {
function folderOpenReason (line 640) | func folderOpenReason() string {
function folderOpenUnavailableReason (line 653) | func folderOpenUnavailableReason() string {
function openFolderWithSystemDefault (line 662) | func openFolderWithSystemDefault(targetPath string) error {
function runLaunchCommand (line 671) | func runLaunchCommand(command openCodeLaunchCommand, targetPath string) ...
function resolveLaunchExecutable (line 687) | func resolveLaunchExecutable(candidate string) string {
function firstResolvedExecutable (line 707) | func firstResolvedExecutable(candidates []string) string {
function isPlatformUnsupportedOpenError (line 716) | func isPlatformUnsupportedOpenError(err error) bool {
FILE: backend/opencode_zen.go
constant openCodeZenChatCompletionsURL (line 15) | openCodeZenChatCompletionsURL = "https://opencode.ai/zen/v1/chat/complet...
constant openCodeZenChatMaxTokens (line 18) | openCodeZenChatMaxTokens = 4096
constant openCodeZenDrawToCodeMaxTokens (line 19) | openCodeZenDrawToCodeMaxTokens = 16384
constant openCodeZenDefaultTemperature (line 20) | openCodeZenDefaultTemperature = 0.6
constant openCodeZenHeaderTimeout (line 21) | openCodeZenHeaderTimeout = 90 * time.Second
type openCodeZenAPIError (line 26) | type openCodeZenAPIError struct
method Error (line 32) | func (e *openCodeZenAPIError) Error() string {
function newOpenCodeZenHTTPClient (line 39) | func newOpenCodeZenHTTPClient() *http.Client {
function openCodeZenModelCandidates (line 48) | func openCodeZenModelCandidates(modelID string, allowCrossModelFallback ...
function openCodeZenShouldTryFallbackModel (line 76) | func openCodeZenShouldTryFallbackModel(err error) bool {
function openCodeZenExtractProviderErrorMessage (line 99) | func openCodeZenExtractProviderErrorMessage(bodyText string) string {
function openCodeZenStatusError (line 160) | func openCodeZenStatusError(status int, model, bodyText string) error {
function setOpenCodeZenRequestHeaders (line 198) | func setOpenCodeZenRequestHeaders(req *http.Request, apiKey string, acce...
function openCodeZenAPIModelID (line 204) | func openCodeZenAPIModelID(modelID string) string {
function callOpenCodeZenDrawToCodeApiFull (line 210) | func callOpenCodeZenDrawToCodeApiFull(imageBase64, userPrompt, template,...
function callOpenCodeZenDrawToCodeStreaming (line 236) | func callOpenCodeZenDrawToCodeStreaming(w http.ResponseWriter, imageBase...
function callOpenCodeZenApiGo (line 245) | func callOpenCodeZenApiGo(prevMsgs []ChatMessage, newMsg, apiKey, openCo...
function callOpenCodeZenChatStreaming (line 271) | func callOpenCodeZenChatStreaming(w http.ResponseWriter, prevMsgs []Chat...
function buildOpenCodeZenChatMessages (line 279) | func buildOpenCodeZenChatMessages(prevMsgs []ChatMessage, newMsg, openCo...
function callOpenCodeZenChatCompletions (line 334) | func callOpenCodeZenChatCompletions(messages []map[string]interface{}, a...
function callOpenCodeZenChatCompletionsSingle (line 358) | func callOpenCodeZenChatCompletionsSingle(messages []map[string]interfac...
function callOpenCodeZenChatCompletionsStreaming (line 462) | func callOpenCodeZenChatCompletionsStreaming(w http.ResponseWriter, mess...
function callOpenCodeZenChatCompletionsStreamingSingle (line 486) | func callOpenCodeZenChatCompletionsStreamingSingle(w http.ResponseWriter...
function openCodeZenMessagesContainImage (line 848) | func openCodeZenMessagesContainImage(messages []map[string]interface{}) ...
function openCodeZenModelSupportsImageInput (line 871) | func openCodeZenModelSupportsImageInput(modelID string) bool {
function openCodeZenAttachmentDataURI (line 881) | func openCodeZenAttachmentDataURI(attachmentBase64, attachmentMime strin...
function buildOpenCodeZenDrawToCodeMessages (line 893) | func buildOpenCodeZenDrawToCodeMessages(imageBase64, userPrompt, templat...
function describeImageWithOpenCodeZenKimi (line 986) | func describeImageWithOpenCodeZenKimi(imageBase64, apiKey string) (strin...
function logOpenCodeZenRequestSummary (line 1070) | func logOpenCodeZenRequestSummary(model string, stream bool, hasImage bo...
FILE: backend/openrouter_openrouter.go
constant openRouterChatCompletionsURL (line 14) | openRouterChatCompletionsURL = "https://openrouter.ai/api/v1/chat/comple...
constant openRouterHTTPReferer (line 15) | openRouterHTTPReferer = "https://glowbom.com"
constant openRouterXTitle (line 16) | openRouterXTitle = "Glowbom"
constant openRouterChatMaxTokens (line 19) | openRouterChatMaxTokens = 4096
constant openRouterDrawToCodeMaxTokens (line 20) | openRouterDrawToCodeMaxTokens = 16384
constant openRouterDefaultTemperature (line 21) | openRouterDefaultTemperature = 0.6
function setOpenRouterRequestHeaders (line 24) | func setOpenRouterRequestHeaders(req *http.Request, apiKey string, accep...
function callOpenRouterDrawToCodeApiFull (line 32) | func callOpenRouterDrawToCodeApiFull(imageBase64, userPrompt, template, ...
function callOpenRouterDrawToCodeStreaming (line 58) | func callOpenRouterDrawToCodeStreaming(w http.ResponseWriter, imageBase6...
function callOpenRouterApiGo (line 67) | func callOpenRouterApiGo(prevMsgs []ChatMessage, newMsg, apiKey, openRou...
function callOpenRouterChatStreaming (line 93) | func callOpenRouterChatStreaming(w http.ResponseWriter, prevMsgs []ChatM...
function buildOpenRouterChatMessages (line 101) | func buildOpenRouterChatMessages(prevMsgs []ChatMessage, newMsg, openRou...
function callOpenRouterChatCompletions (line 156) | func callOpenRouterChatCompletions(messages []map[string]interface{}, ap...
function callOpenRouterChatCompletionsStreaming (line 259) | func callOpenRouterChatCompletionsStreaming(w http.ResponseWriter, messa...
function openRouterMessagesContainImage (line 608) | func openRouterMessagesContainImage(messages []map[string]interface{}) b...
function openRouterModelSupportsImageInput (line 631) | func openRouterModelSupportsImageInput(modelID string) bool {
function openRouterAttachmentDataURI (line 641) | func openRouterAttachmentDataURI(attachmentBase64, attachmentMime string...
function buildOpenRouterDrawToCodeMessages (line 653) | func buildOpenRouterDrawToCodeMessages(imageBase64, userPrompt, template...
function describeImageWithOpenRouterKimi (line 746) | func describeImageWithOpenRouterKimi(imageBase64, apiKey string) (string...
function openRouterReasoningToText (line 830) | func openRouterReasoningToText(reasoning interface{}) string {
function logOpenRouterRequestSummary (line 866) | func logOpenRouterRequestSummary(model string, stream bool, hasImage boo...
function openRouterLatestUserPreview (line 880) | func openRouterLatestUserPreview(messages []map[string]interface{}) stri...
FILE: backend/prompts.go
function isGlowbyImagesSource (line 8) | func isGlowbyImagesSource(imageSource string) bool {
constant systemPromptTranslation (line 15) | systemPromptTranslation = `You are a humorous AI with emotions called Gl...
constant drawToCodeSystemPrompt (line 128) | drawToCodeSystemPrompt = `You are a skilled web developer with expertise...
constant defaultSystemPrompt (line 146) | defaultSystemPrompt = `You are a proffesional AI sofware eng called Glow...
function getImageSourceText (line 221) | func getImageSourceText(imageSource string) string {
function buildDetailedTaskDescription (line 228) | func buildDetailedTaskDescription(template, imageSource, userPrompt stri...
function buildDrawToCodeSystemPrompt (line 275) | func buildDrawToCodeSystemPrompt(template, imageSource string) string {
function getSystemPrompt (line 295) | func getSystemPrompt(template, imageSource string) string {
FILE: backend/r1_groq.go
function callR1GroqDrawToCodeApiFull (line 15) | func callR1GroqDrawToCodeApiFull(imageBase64, userPrompt, prompt, templa...
function callGroqDrawToCodeStreaming (line 83) | func callGroqDrawToCodeStreaming(w http.ResponseWriter, imageBase64, use...
function callR1GroqApiGo (line 126) | func callR1GroqApiGo(prevMsgs []ChatMessage, newMsg, groqKey string) (*R...
function getGroqImageDescription (line 176) | func getGroqImageDescription(imageBase64, groqKey string) (string, error) {
function callGroqSSE (line 240) | func callGroqSSE(messages []map[string]interface{}, groqKey string) (str...
function callGroqGPTOSSChatStreaming (line 323) | func callGroqGPTOSSChatStreaming(w http.ResponseWriter, prevMsgs []ChatM...
FILE: backend/r1_ollama.go
function normalizeOllamaModelName (line 15) | func normalizeOllamaModelName(modelName string) string {
function ollamaModelSupportsVision (line 27) | func ollamaModelSupportsVision(modelName string) bool {
function ollamaModelWantsExplicitThinkingInstructions (line 32) | func ollamaModelWantsExplicitThinkingInstructions(modelName string) bool {
function splitThinkingAndAnswer (line 41) | func splitThinkingAndAnswer(thinkingText string) (thinking string, answe...
function callR1DrawToCodeApiFull (line 70) | func callR1DrawToCodeApiFull(
function callR1ApiGo (line 100) | func callR1ApiGo(prevMsgs []ChatMessage, newMsg string) (*R1Response, er...
function callOllamaApiGeneric (line 128) | func callOllamaApiGeneric(prevMsgs []ChatMessage, newMsg string, modelNa...
function callOllamaDrawToCodeApiFullWithModel (line 157) | func callOllamaDrawToCodeApiFullWithModel(
function getImageDescription (line 187) | func getImageDescription(imageBase64 string) (string, error) {
function callR1Model (line 256) | func callR1Model(prompt, model string, maxTokens int) (*R1Response, erro...
type ollamaChatMsg (line 329) | type ollamaChatMsg struct
type ollamaChatReq (line 336) | type ollamaChatReq struct
type ollamaChatChunk (line 343) | type ollamaChatChunk struct
function ollamaChatAttachmentImages (line 348) | func ollamaChatAttachmentImages(modelName, attachmentBase64, attachmentM...
function buildOllamaChatMessages (line 363) | func buildOllamaChatMessages(
function callOllamaChatWithModel (line 391) | func callOllamaChatWithModel(prevMsgs []ChatMessage, newMsg string, mode...
function callOllamaChatWithModelAndAttachment (line 395) | func callOllamaChatWithModelAndAttachment(
function callOllamaChatWithModelStreaming (line 476) | func callOllamaChatWithModelStreaming(w http.ResponseWriter, prevMsgs []...
function callOllamaChatWithModelStreamingAndAttachment (line 480) | func callOllamaChatWithModelStreamingAndAttachment(
function callOllamaChatDrawToCodeWithModel (line 663) | func callOllamaChatDrawToCodeWithModel(imageBase64, userPrompt, template...
function callOllamaDrawToCodeStreaming (line 692) | func callOllamaDrawToCodeStreaming(w http.ResponseWriter, imageBase64, u...
FILE: backend/search.go
type openAIResponsesRequest (line 14) | type openAIResponsesRequest struct
type openAIInputMessage (line 21) | type openAIInputMessage struct
type openAIInputContentItem (line 26) | type openAIInputContentItem struct
type openAIResponsesOutput (line 31) | type openAIResponsesOutput struct
type openAIContent (line 44) | type openAIContent struct
type openAIAnnotation (line 50) | type openAIAnnotation struct
type chatCompletionRequest (line 58) | type chatCompletionRequest struct
type chatCompletionMessage (line 63) | type chatCompletionMessage struct
type chatCompletionResponse (line 68) | type chatCompletionResponse struct
function callWebSearchMini (line 76) | func callWebSearchMini(prevMsgs []ChatMessage, userQuery, apiKey string)...
function shouldRetrySearch (line 142) | func shouldRetrySearch(err error) bool {
function callGPT5Search (line 165) | func callGPT5Search(prevMsgs []ChatMessage, userQuery, apiKey string) (*...
function performWebSearch (line 333) | func performWebSearch(prevMsgs []ChatMessage, userQuery, apiKey string) ...
function truncateForLog (line 373) | func truncateForLog(text string) string {
function isMissingResponsesWriteScope (line 381) | func isMissingResponsesWriteScope(message string) bool {
function isMissingModelRequestScope (line 389) | func isMissingModelRequestScope(message string) bool {
function isOpenAIMissingModelScope (line 397) | func isOpenAIMissingModelScope(message string) bool {
function openAIModelScopeHelpMessage (line 401) | func openAIModelScopeHelpMessage() string {
function injectSearchContext (line 405) | func injectSearchContext(prevMsgs *[]ChatMessage, searchContext string) {
FILE: backend/security.go
type glowbyHealthResponse (line 13) | type glowbyHealthResponse struct
function backendBindHost (line 18) | func backendBindHost() string {
function backendListenAddr (line 27) | func backendListenAddr(port string) string {
function glowbyServerToken (line 35) | func glowbyServerToken() string {
function glowbyAllowedOrigins (line 44) | func glowbyAllowedOrigins() map[string]struct{} {
function glowbyHealthHandler (line 70) | func glowbyHealthHandler(w http.ResponseWriter, r *http.Request) {
function withGlowbySecurity (line 84) | func withGlowbySecurity(next http.Handler) http.Handler {
function isPublicBackendRoute (line 108) | func isPublicBackendRoute(r *http.Request) bool {
function isAllowedOrigin (line 119) | func isAllowedOrigin(r *http.Request, allowedOrigins map[string]struct{}...
function normalizeHost (line 153) | func normalizeHost(host string) string {
function isLoopbackHost (line 166) | func isLoopbackHost(host string) bool {
function hasValidGlowbyServerToken (line 175) | func hasValidGlowbyServerToken(r *http.Request, expected string) bool {
function bearerToken (line 187) | func bearerToken(raw string) string {
FILE: backend/stack_specs.go
function stackInstructionsFor (line 85) | func stackInstructionsFor(targetLang string, projectMode bool) string {
FILE: backend/types.go
type R1Response (line 6) | type R1Response struct
type ChatMessage (line 12) | type ChatMessage struct
type DrawToCodeRequest (line 17) | type DrawToCodeRequest struct
type ChatWithAIRequest (line 25) | type ChatWithAIRequest struct
type WebSearchRequest (line 56) | type WebSearchRequest struct
type AnalyzeVideoRequest (line 65) | type AnalyzeVideoRequest struct
type AnalyzeVideoResponse (line 73) | type AnalyzeVideoResponse struct
FILE: cli/code.go
constant backendPort (line 27) | backendPort = 4569
constant webPort (line 28) | webPort = 4572
function runCode (line 31) | func runCode(args []string) int {
function findGlowbyRoot (line 242) | func findGlowbyRoot() (string, error) {
function searchGlowbyRoot (line 261) | func searchGlowbyRoot(start string) (string, bool) {
function isDir (line 275) | func isDir(path string) bool {
function waitForServer (line 280) | func waitForServer(ctx context.Context, url string, timeout time.Duratio...
function reclaimManagedService (line 306) | func reclaimManagedService(glowbyRoot, service string, port int) error {
function reclaimManagedAgentService (line 347) | func reclaimManagedAgentService(port int) error {
function recordManagedService (line 360) | func recordManagedService(glowbyRoot, service string, port int) error {
function readManagedService (line 381) | func readManagedService(glowbyRoot, service string) ([]int, error) {
function clearManagedService (line 409) | func clearManagedService(glowbyRoot, service string) {
function stopManagedAgentService (line 413) | func stopManagedAgentService(port int) error {
function managedStateDir (line 424) | func managedStateDir(glowbyRoot string) string {
function managedStatePath (line 429) | func managedStatePath(glowbyRoot, service string) string {
function waitForPortFree (line 433) | func waitForPortFree(port int, timeout time.Duration) bool {
function killProcessesOnPort (line 445) | func killProcessesOnPort(port int, pids []int, label string) error {
function isExpectedGlowbyService (line 461) | func isExpectedGlowbyService(service string, port int) bool {
function areExpectedOpenCodeProcesses (line 490) | func areExpectedOpenCodeProcesses(pids []int) bool {
function resolveOrGenerateSecret (line 506) | func resolveOrGenerateSecret(envKeys ...string) (string, error) {
function printLocalAuth (line 515) | func printLocalAuth(serverToken, opencodePassword string) {
function localAgentPort (line 526) | func localAgentPort() string {
function localOpenCodeUsername (line 533) | func localOpenCodeUsername() string {
function randomHexSecret (line 540) | func randomHexSecret(byteLen int) (string, error) {
function listPortPIDs (line 551) | func listPortPIDs(port int) ([]int, error) {
function processCommandSummary (line 560) | func processCommandSummary(pid int) (string, error) {
function processCommandSummaryUnix (line 569) | func processCommandSummaryUnix(pid int) (string, error) {
function processCommandSummaryWindows (line 577) | func processCommandSummaryWindows(pid int) (string, error) {
function listPortPIDsUnix (line 585) | func listPortPIDsUnix(port int) ([]int, error) {
function listPortPIDsWindows (line 600) | func listPortPIDsWindows(port int) ([]int, error) {
function parsePIDLines (line 632) | func parsePIDLines(text string) ([]int, error) {
function uniqueSortedInts (line 648) | func uniqueSortedInts(values []int) []int {
function isSubset (line 664) | func isSubset(values, allowed []int) bool {
function openBrowser (line 680) | func openBrowser(url string) {
FILE: cli/doctor.go
type depCheck (line 9) | type depCheck struct
function runDoctor (line 15) | func runDoctor() int {
function commandVersion (line 61) | func commandVersion(name string) string {
FILE: cli/main.go
constant usage (line 14) | usage = `glowby - terminal-first local AI coding agent
function main (line 29) | func main() {
function run (line 34) | func run(args []string) int {
FILE: cli/version.go
function runVersion (line 5) | func runVersion() int {
FILE: docs/app/components/brand-title.tsx
function BrandTitle (line 3) | function BrandTitle() {
FILE: docs/app/components/docs-route-view.tsx
function DocsRouteView (line 16) | function DocsRouteView({ loaderData }: { loaderData: LoadedDocPage }) {
FILE: docs/app/components/docs-search.tsx
type DocsSearchContextValue (line 16) | type DocsSearchContextValue = {
function useDocsSearchContext (line 26) | function useDocsSearchContext() {
function normalize (line 36) | function normalize(value: string) {
function scoreRecord (line 40) | function scoreRecord(record: SearchRecord, query: string) {
function useSearchResults (line 66) | function useSearchResults(records: SearchRecord[], search: string) {
function getNextIndex (line 82) | function getNextIndex(currentIndex: number, delta: number, total: number) {
function SearchResultItem (line 90) | function SearchResultItem({
function SearchResultsList (line 122) | function SearchResultsList({
function useGlobalShortcut (line 154) | function useGlobalShortcut(setModalOpen: (value: boolean) => void) {
function DocsSearchProvider (line 182) | function DocsSearchProvider({
function DocsSearchLargeTrigger (line 218) | function DocsSearchLargeTrigger() {
function DocsSearchSmallTrigger (line 312) | function DocsSearchSmallTrigger() {
function DocsSearchDialog (line 327) | function DocsSearchDialog() {
FILE: docs/app/components/nav-actions.tsx
function NavActions (line 9) | function NavActions() {
FILE: docs/app/lib/docs.ts
type SearchRecord (line 3) | interface SearchRecord {
type LoadedDocPage (line 12) | interface LoadedDocPage {
FILE: docs/app/lib/meta.ts
function buildDocMeta (line 3) | function buildDocMeta(data?: LoadedDocPage) {
FILE: docs/app/lib/site.ts
constant GITHUB_URL (line 1) | const GITHUB_URL = "https://github.com/glowbom/glowby";
constant DISCORD_URL (line 2) | const DISCORD_URL = "https://discord.com/invite/jpWW6vB4Jk";
FILE: docs/app/lib/source.browser.tsx
type BrowserDocsCollection (line 4) | type BrowserDocsCollection = {
FILE: docs/app/lib/source.server.ts
function getSearchRecords (line 14) | async function getSearchRecords() {
type StructuredData (line 18) | type StructuredData = {
type SearchPage (line 23) | type SearchPage = {
function getStructuredData (line 34) | async function getStructuredData(page: SearchPage): Promise<StructuredDa...
function normalizeSearchText (line 48) | function normalizeSearchText(value: string) {
function buildSearchRecords (line 52) | async function buildSearchRecords(): Promise<SearchRecord[]> {
function loadDocPage (line 117) | async function loadDocPage(slugs?: string[]): Promise<LoadedDocPage> {
FILE: docs/app/root.tsx
function withBasePath (line 14) | function withBasePath(path: string) {
function Layout (line 35) | function Layout({ children }: { children: React.ReactNode }) {
function App (line 61) | function App() {
function ErrorBoundary (line 65) | function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
FILE: docs/app/routes/docs-page.tsx
function toSlugs (line 6) | function toSlugs(value?: string): string[] | undefined {
function loader (line 15) | async function loader({ params }: Route.LoaderArgs) {
function meta (line 19) | function meta({ data }: Route.MetaArgs) {
function DocsPageRoute (line 23) | function DocsPageRoute({ loaderData }: Route.ComponentProps) {
FILE: docs/app/routes/home.tsx
function loader (line 6) | async function loader() {
function meta (line 10) | function meta({ data }: Route.MetaArgs) {
function Home (line 14) | function Home({ loaderData }: Route.ComponentProps) {
FILE: legacy/app/lib/main.dart
function main (line 7) | void main()
FILE: legacy/app/lib/models/ai.dart
class Ai (line 13) | class Ai {
method getCurrentNetworkOperation (line 18) | CancelableOperation<String>? getCurrentNetworkOperation()
method message (line 29) | Future<List<Message>> message(
method jaroSimilarity (line 83) | double jaroSimilarity(String s1, String s2)
method jaroWinkler (line 135) | double jaroWinkler(String s1, String s2, {double p = 0.1})
method _findMatchingQuestions (line 151) | List<Map<String, Object>> _findMatchingQuestions(String message)
method _sanitizeMessage (line 174) | String _sanitizeMessage(String message)
method _searchForQuestions (line 181) | List<Map<String, Object>> _searchForQuestions(String userMessage)
method _generateResponseMessage (line 209) | Future<List<Message>> _generateResponseMessage(
FILE: legacy/app/lib/services/hugging_face_api.dart
class HuggingFaceApi (line 6) | class HuggingFaceApi {
method oat (line 29) | String oat()
method setOat (line 30) | void setOat(String value)
method resetOat (line 31) | void resetOat()
method _resetOat (line 33) | void _resetOat()
method _oat (line 37) | String _oat()
method _setOat (line 38) | Future<void> _setOat(String value)
method loadOat (line 43) | Future<void> loadOat()
method sendMessages (line 65) | bool sendMessages()
method setSendMessages (line 69) | Future<void> setSendMessages(bool sendMessages)
method systemMessage (line 76) | String systemMessage()
method setSystemMessage (line 80) | void setSystemMessage(systemMessage)
method model (line 85) | String model()
method setModel (line 89) | void setModel(model)
method template (line 94) | String template()
method setTemplate (line 98) | void setTemplate(template)
method _findValueByTemplate (line 103) | String? _findValueByTemplate(dynamic value, dynamic template)
method _processListByTemplate (line 116) | String? _processListByTemplate(
method _processMapByTemplate (line 130) | String? _processMapByTemplate(
method generate (line 142) | Future<String?> generate(String text)
method _generate (line 149) | Future<String?> _generate(
method _isValidTokenAndModel (line 159) | bool _isValidTokenAndModel(String modelId)
method _makeRequest (line 163) | Future<http.Response> _makeRequest(String modelId, String text)
method _processResponse (line 195) | String? _processResponse(http.Response response, String template)
FILE: legacy/app/lib/services/openai_api.dart
class OpenAiApi (line 9) | class OpenAiApi {
method oat (line 17) | String oat()
method setOat (line 18) | void setOat(String value)
method resetOat (line 19) | void resetOat()
method _resetOat (line 21) | void _resetOat()
method _oat (line 25) | String _oat()
method _setOat (line 26) | Future<void> _setOat(String value)
method loadOat (line 45) | Future<void> loadOat()
method setModel (line 63) | Future<void> setModel(String value)
method setSystemPrompt (line 68) | Future<void> setSystemPrompt(String value)
method setSelectedLanguage (line 73) | Future<void> setSelectedLanguage(String value)
method generateImageUrl (line 79) | Future<String?> generateImageUrl(String description)
method isInputSafe (line 124) | Future<bool> isInputSafe(String input)
method getAdjustedMaxTokens (line 173) | int getAdjustedMaxTokens(String inputText,
method containsKeyword (line 193) | bool containsKeyword(String text, List<String> keywords)
method getResponseFromOpenAI (line 206) | CancelableOperation<String> getResponseFromOpenAI(
method formatPrevMessages (line 246) | String formatPrevMessages(
method _getResponseFromHuggingFace (line 253) | Future<void> _getResponseFromHuggingFace(
method _getResponseFromPulzeAI (line 290) | Future<void> _getResponseFromPulzeAI(
method _getResponseFromOpenAI (line 327) | Future<void> _getResponseFromOpenAI(
method getHtmlFromOpenAI (line 449) | Future<String> getHtmlFromOpenAI(
FILE: legacy/app/lib/services/pulze_ai_api.dart
class PulzeAiApi (line 6) | class PulzeAiApi {
method oat (line 13) | String oat()
method setOat (line 14) | void setOat(String value)
method resetOat (line 15) | void resetOat()
method _resetOat (line 17) | void _resetOat()
method _oat (line 21) | String _oat()
method _setOat (line 22) | Future<void> _setOat(String value)
method loadOat (line 44) | Future<void> loadOat()
method sendMessages (line 65) | bool sendMessages()
method setSendMessages (line 69) | Future<void> setSendMessages(bool sendMessages)
method systemMessage (line 75) | String systemMessage()
method setSystemMessage (line 79) | void setSystemMessage(systemMessage)
method model (line 84) | String model()
method lastUsedModel (line 88) | String lastUsedModel()
method setModel (line 92) | void setModel(model)
method template (line 97) | String template()
method setTemplate (line 101) | void setTemplate(template)
method generate (line 106) | Future<String?> generate(String text)
method _generate (line 110) | Future<String?> _generate(
FILE: legacy/app/lib/utils/color_utils.dart
function tintValue (line 15) | int tintValue(int value, double factor)
function tintColor (line 29) | Color tintColor(Color color, double factor)
function shadeValue (line 47) | int shadeValue(int value, double factor)
function shadeColor (line 50) | Color shadeColor(Color color, double factor)
function generateMaterialColor (line 70) | MaterialColor generateMaterialColor(Color color)
FILE: legacy/app/lib/utils/text_to_speech.dart
class TextToSpeech (line 5) | class TextToSpeech {
method setSpeechRate (line 53) | Future<void> setSpeechRate(currentLanguage)
method speakText (line 67) | Future<void> speakText(String text, {String language = 'en-US'})
FILE: legacy/app/lib/utils/timestamp.dart
function _check (line 8) | void _check(bool expr, String name, int value)
class Timestamp (line 24) | class Timestamp implements Comparable<Timestamp> {
method toDate (line 76) | DateTime toDate()
method compareTo (line 86) | int compareTo(Timestamp other)
method toString (line 95) | String toString()
method _validateRange (line 99) | void _validateRange(int seconds, int nanoseconds)
FILE: legacy/app/lib/utils/utils.dart
class Utils (line 14) | abstract class Utils {
method downloadImage (line 15) | Future<void> downloadImage(String url, String description)
method pickImage (line 18) | Future<dynamic> pickImage()
method startFilePicker (line 20) | Future<dynamic> startFilePicker()
method initializeState (line 23) | Future<void> initializeState(dynamic f)
method recordVoice (line 26) | Future<void> recordVoice(String lang)
method convertToBase64JpegWeb (line 29) | Future<String> convertToBase64JpegWeb(
method launchURL (line 33) | Future<void> launchURL(String url)
method isImageGenerationCommand (line 50) | bool isImageGenerationCommand(String input)
method getMatchingPattern (line 57) | String? getMatchingPattern(String input)
method getImageDataFromUrl (line 67) | Future<String> getImageDataFromUrl(String url)
method getRandomImageReadyMessage (line 92) | String getRandomImageReadyMessage()
method getRandomImageGenerationFunnyMessage (line 124) | String getRandomImageGenerationFunnyMessage()
method getRandomMessage (line 166) | String getRandomMessage()
method getRandomMessageForCode (line 206) | String getRandomMessageForCode()
FILE: legacy/app/lib/utils/utils_desktop.dart
class UtilsPlatform (line 11) | class UtilsPlatform {
method downloadImage (line 12) | Future<void> downloadImage(String url, String description)
method pickImage (line 29) | Future<dynamic> pickImage()
method startFilePicker (line 45) | Future<dynamic> startFilePicker()
method initializeState (line 50) | Future<void> initializeState(dynamic f)
method recordVoice (line 55) | Future<void> recordVoice(String lang)
method paintImage (line 58) | void paintImage(
method convertToBase64JpegWeb (line 80) | Future<String> convertToBase64JpegWeb(
FILE: legacy/app/lib/utils/utils_stub.dart
class UtilsPlatform (line 4) | class UtilsPlatform {
method downloadImage (line 5) | Future<void> downloadImage(String url, String description)
method pickImage (line 9) | Future<dynamic> pickImage()
method startFilePicker (line 13) | Future<dynamic> startFilePicker()
method initializeState (line 18) | Future<void> initializeState(dynamic f)
method recordVoice (line 23) | Future<void> recordVoice(String lang)
method convertToBase64JpegWeb (line 28) | Future<String> convertToBase64JpegWeb(
FILE: legacy/app/lib/utils/utils_web.dart
function rv (line 16) | int rv(String lang)
function vr (line 24) | void vr(text)
class UtilsPlatform (line 26) | class UtilsPlatform {
method convertUiImageToHtmlImage (line 27) | Future<html.ImageElement> convertUiImageToHtmlImage(
method convertHtmlImageToUint8List (line 42) | Future<Uint8List> convertHtmlImageToUint8List(
method drawImageOnCanvas (line 53) | Future<void> drawImageOnCanvas(html.CanvasRenderingContext2D ctx,
method convertToBase64JpegWeb (line 87) | Future<String> convertToBase64JpegWeb(
method downloadImage (line 124) | Future<void> downloadImage(String url, String description)
method initializeState (line 130) | Future<void> initializeState(dynamic f)
method recordVoice (line 135) | Future<void> recordVoice(String lang)
method pickImage (line 140) | Future<Uint8List> pickImage()
method startFilePicker (line 156) | Future<dynamic> startFilePicker()
FILE: legacy/app/lib/views/dialogs/ai_error_dialog.dart
class AiErrorDialog (line 4) | class AiErrorDialog extends StatefulWidget {
method createState (line 8) | AiErrorDialogState createState()
class AiErrorDialogState (line 11) | class AiErrorDialogState extends State<AiErrorDialog> {
method initState (line 13) | void initState()
method build (line 18) | Widget build(BuildContext context)
FILE: legacy/app/lib/views/dialogs/ai_settings_dialog.dart
class AiSettingsDialog (line 9) | class AiSettingsDialog extends StatefulWidget {
method createState (line 15) | AiSettingsDialogState createState()
class AiSettingsDialogState (line 18) | class AiSettingsDialogState extends State<AiSettingsDialog> {
method _buildAutonomousModeCheckbox (line 30) | Widget _buildAutonomousModeCheckbox()
method buildLanguageDropdownItems (line 45) | List<DropdownMenuItem<String>> buildLanguageDropdownItems()
method buildPromptDropdownItems (line 58) | List<DropdownMenuItem<String>> buildPromptDropdownItems()
method _promptChanged (line 68) | void _promptChanged(String? value)
method initState (line 85) | void initState()
method _saveOpenAISettings (line 100) | void _saveOpenAISettings()
method _saveHuggingFaceSettings (line 106) | void _saveHuggingFaceSettings()
method _saveSettings (line 114) | void _saveSettings(BuildContext context)
method build (line 127) | Widget build(BuildContext context)
FILE: legacy/app/lib/views/dialogs/api_key_dialog.dart
class ApiKeyDialog (line 8) | class ApiKeyDialog extends StatefulWidget {
method createState (line 12) | ApiKeyDialogState createState()
class ApiKeyDialogState (line 15) | class ApiKeyDialogState extends State<ApiKeyDialog> {
method initState (line 28) | void initState()
method isValidOpenAIKey (line 43) | bool isValidOpenAIKey(String key)
method isValidHuggingFaceKey (line 45) | bool isValidHuggingFaceKey(String key)
method isValidPuzzleAIKey (line 48) | bool isValidPuzzleAIKey(String key)
method _saveApiKey (line 51) | void _saveApiKey(BuildContext context)
method build (line 86) | Widget build(BuildContext context)
FILE: legacy/app/lib/views/html/html_view_screen_desktop.dart
class HtmlViewScreen (line 15) | class HtmlViewScreen extends StatelessWidget
method _openCodeInBrowser (line 23) | void _openCodeInBrowser(context)
method build (line 52) | Widget build(BuildContext context)
FILE: legacy/app/lib/views/html/html_view_screen_interface.dart
class HtmlViewScreenInterface (line 3) | abstract class HtmlViewScreenInterface {
method build (line 4) | Widget build(BuildContext context)
FILE: legacy/app/lib/views/html/html_view_screen_mobile.dart
class HtmlViewScreenMobile (line 4) | class HtmlViewScreenMobile extends StatefulWidget {
method createState (line 12) | HtmlViewScreenState createState()
class HtmlViewScreenState (line 15) | class HtmlViewScreenState extends State<HtmlViewScreenMobile> {
method initState (line 19) | void initState()
method build (line 27) | Widget build(BuildContext context)
FILE: legacy/app/lib/views/html/html_view_screen_stub.dart
class HtmlViewScreen (line 4) | class HtmlViewScreen extends StatelessWidget {
method build (line 11) | Widget build(BuildContext context)
FILE: legacy/app/lib/views/html/html_view_screen_web.dart
class HtmlViewScreen (line 7) | class HtmlViewScreen extends StatelessWidget
method build (line 15) | Widget build(BuildContext context)
method downloadContent (line 33) | void downloadContent()
FILE: legacy/app/lib/views/screens/chat_screen.dart
class ChatScreen (line 20) | class ChatScreen extends StatefulWidget {
method createState (line 48) | ChatScreenState createState()
class ChatScreenState (line 51) | class ChatScreenState extends State<ChatScreen> {
method updateVoiceEnabled (line 55) | void updateVoiceEnabled(bool value)
method initState (line 66) | void initState()
method initializeVoiceEnabled (line 73) | void initializeVoiceEnabled()
method loadGlobalSettings (line 77) | void loadGlobalSettings()
method loadAPIKey (line 86) | void loadAPIKey()
method refreshUI (line 91) | void refreshUI()
method handleTextToSpeech (line 96) | void handleTextToSpeech()
method refresh (line 113) | void refresh()
method _showApiKeyDialog (line 118) | void _showApiKeyDialog()
method _showAiSettingsDialog (line 129) | void _showAiSettingsDialog()
method extractPlanName (line 140) | String extractPlanName(String response, String inputMessage)
method _generateTasks (line 163) | Future<List<String>> _generateTasks(String inputMessage)
method _implementPlan (line 261) | Future<void> _implementPlan()
method insertMessage (line 293) | void insertMessage(String message, String userId, String username)
method fetchResponseFromAPI (line 304) | Future<String> fetchResponseFromAPI(
method _sendMessageOnBehalfOfUser (line 324) | Future<void> _sendMessageOnBehalfOfUser(
method setLoading (line 351) | void setLoading(bool value)
method setStopRequested (line 357) | void setStopRequested(bool value)
method setAutonomousMode (line 363) | void setAutonomousMode(bool value)
method setPlanImplementationInProgress (line 369) | void setPlanImplementationInProgress(bool value)
method cancelCurrentOperation (line 375) | void cancelCurrentOperation()
method _stopAutonomousMode (line 382) | void _stopAutonomousMode()
method _showSocialLinksDialog (line 390) | void _showSocialLinksDialog()
method _buildLinkItems (line 412) | List<Widget> _buildLinkItems(BuildContext context)
method _buildLinkItem (line 444) | Widget _buildLinkItem(String text, String url, BuildContext context)
method build (line 488) | Widget build(BuildContext context)
FILE: legacy/app/lib/views/screens/global_settings.dart
class GlobalSettings (line 4) | class GlobalSettings {
method languageChanged (line 24) | void languageChanged(String? value)
method selectPrompt (line 141) | void selectPrompt(String userInput)
method loadDialogValues (line 150) | void loadDialogValues(selectedModelInput, selectedLanguageInput,
FILE: legacy/app/lib/views/screens/magical_loading_view.dart
class MagicalLoadingView (line 4) | class MagicalLoadingView extends StatefulWidget {
method createState (line 8) | MagicalLoadingViewState createState()
class MagicalLoadingViewState (line 11) | class MagicalLoadingViewState extends State<MagicalLoadingView>
method getRandomMessage (line 16) | String getRandomMessage()
method initState (line 22) | void initState()
method dispose (line 31) | void dispose()
method build (line 37) | Widget build(BuildContext context)
class _MagicalLoadingPainter (line 80) | class _MagicalLoadingPainter extends CustomPainter {
method paint (line 85) | void paint(Canvas canvas, Size size)
method shouldRepaint (line 102) | bool shouldRepaint(covariant _MagicalLoadingPainter oldDelegate)
FILE: legacy/app/lib/views/screens/talk_screen.dart
class TalkState (line 14) | class TalkState extends State<Talk> {
method loadContentFromAssets (line 33) | Future<dynamic> loadContentFromAssets()
method loadAPIKeys (line 39) | Future<void> loadAPIKeys()
method initState (line 46) | void initState()
method resetApiKeys (line 52) | void resetApiKeys()
method loadApiKeysAndInitializeState (line 58) | void loadApiKeysAndInitializeState()
method initializeTalkState (line 62) | void initializeTalkState()
method buildQuestions (line 99) | List<Map<String, Object>> buildQuestions(List<dynamic> questionsData)
method _pressed100 (line 122) | void _pressed100()
method _startFilePicker (line 143) | void _startFilePicker()
method build (line 153) | Widget build(BuildContext context)
class Talk (line 312) | class Talk extends StatefulWidget {
method createState (line 316) | State<StatefulWidget> createState()
class TalkApp (line 321) | class TalkApp extends StatefulWidget {
method createState (line 325) | TalkAppState createState()
class TalkAppState (line 328) | class TalkAppState extends State<TalkApp> {
method initState (line 330) | void initState()
method build (line 338) | Widget build(BuildContext context)
FILE: legacy/app/lib/views/widgets/message.dart
class Message (line 4) | class Message {
method toString (line 29) | String toString()
FILE: legacy/app/lib/views/widgets/message_bubble.dart
class MessageBubble (line 8) | class MessageBubble extends StatelessWidget {
method _launchLink (line 18) | void _launchLink({String l = ""})
method _buildMessageBubbleContainer (line 28) | Container _buildMessageBubbleContainer(BuildContext context)
method _buildMessageContent (line 55) | Column _buildMessageContent(BuildContext context)
method _buildUsernameText (line 67) | Text _buildUsernameText(BuildContext context)
method _buildMessageOrLink (line 82) | Widget _buildMessageOrLink(BuildContext context)
method _buildMessageText (line 168) | Widget _buildMessageText(BuildContext context)
method _buildLinkButton (line 240) | ElevatedButton _buildLinkButton(BuildContext context)
method build (line 255) | Widget build(BuildContext context)
FILE: legacy/app/lib/views/widgets/messages.dart
class Messages (line 8) | class Messages extends StatefulWidget {
method createState (line 14) | MessagesState createState()
class MessagesState (line 17) | class MessagesState extends State<Messages> {
method initState (line 21) | void initState()
method dispose (line 27) | void dispose()
method _processMessageText (line 33) | String _processMessageText(String messageText)
method build (line 63) | Widget build(BuildContext context)
FILE: legacy/app/lib/views/widgets/new_message.dart
class NewMessage (line 18) | class NewMessage extends StatefulWidget {
method createState (line 31) | NewMessageState createState()
class NewMessageState (line 34) | class NewMessageState extends State<NewMessage> {
method _onVoiceReady (line 46) | void _onVoiceReady(text)
method initState (line 64) | void initState()
method dispose (line 79) | void dispose()
method _voiceMessage (line 86) | void _voiceMessage()
method _sendMessage (line 112) | void _sendMessage()
method _resetMessageInput (line 133) | void _resetMessageInput()
method _addUserMessageToChat (line 140) | void _addUserMessageToChat()
method _processUserMessage (line 151) | Future<void> _processUserMessage(String message)
method handleAutonomousMode (line 210) | Future<void> handleAutonomousMode(String message)
method handleImageGenerationCommand (line 214) | Future<void> handleImageGenerationCommand(String message)
method _stopProcessing (line 274) | void _stopProcessing()
method _openPaintWindow (line 298) | void _openPaintWindow()
method build (line 315) | Widget build(BuildContext context)
class MediaType (line 390) | class MediaType {}
FILE: legacy/app/lib/views/widgets/paint_window.dart
class PaintWindow (line 15) | class PaintWindow extends StatefulWidget {
method createState (line 19) | PaintWindowState createState()
class PaintWindowState (line 22) | class PaintWindowState extends State<PaintWindow> {
method dispose (line 34) | void dispose()
method convertToBase64JpegMobile (line 40) | Future<String> convertToBase64JpegMobile(List<Offset?> points)
method convertToBase64Png (line 88) | Future<String> convertToBase64Png(List<Offset?> points)
method callOpenAI (line 122) | Future<void> callOpenAI()
method _showAiErrorDialog (line 185) | void _showAiErrorDialog()
method clear (line 196) | void clear()
method loadImage (line 204) | Future<void> loadImage(Uint8List imageBytes)
method downloadImage (line 215) | Future<void> downloadImage()
method uploadImage (line 217) | Future<void> uploadImage()
method build (line 244) | Widget build(BuildContext context)
class DrawingPainter (line 339) | class DrawingPainter extends CustomPainter {
method paintImage (line 345) | void paintImage(
method paint (line 368) | void paint(Canvas canvas, Size size)
method shouldRepaint (line 397) | bool shouldRepaint(covariant CustomPainter oldDelegate)
FILE: legacy/app/lib/views/widgets/tasks_view.dart
class TasksView (line 3) | class TasksView extends StatefulWidget {
method createState (line 19) | TasksViewState createState()
class TasksViewState (line 22) | class TasksViewState extends State<TasksView>
method initState (line 32) | void initState()
method dispose (line 42) | void dispose()
method _buildTaskItem (line 50) | Widget _buildTaskItem(int index)
method _confirmDeletion (line 74) | void _confirmDeletion(BuildContext context, int index)
method _buildTaskList (line 103) | Widget _buildTaskList()
method _buildAddTaskForm (line 126) | Widget _buildAddTaskForm()
method build (line 148) | Widget build(BuildContext context)
FILE: legacy/app/linux/flutter/generated_plugin_registrant.cc
function fl_register_plugins (line 13) | void fl_register_plugins(FlPluginRegistry* registry) {
FILE: legacy/app/linux/main.cc
function main (line 3) | int main(int argc, char** argv) {
FILE: legacy/app/linux/my_application.cc
type _MyApplication (line 10) | struct _MyApplication {
function my_application_activate (line 18) | static void my_application_activate(GApplication* application) {
function gboolean (line 66) | static gboolean my_application_local_command_line(GApplication* applicat...
function my_application_dispose (line 85) | static void my_application_dispose(GObject* object) {
function my_application_class_init (line 91) | static void my_application_class_init(MyApplicationClass* klass) {
function my_application_init (line 97) | static void my_application_init(MyApplication* self) {}
function MyApplication (line 99) | MyApplication* my_application_new() {
FILE: legacy/app/web/tv.js
function rv (line 21) | function rv(lang) {
FILE: legacy/app/windows/flutter/generated_plugin_registrant.cc
function RegisterPlugins (line 14) | void RegisterPlugins(flutter::PluginRegistry* registry) {
FILE: legacy/app/windows/runner/flutter_window.cpp
function LRESULT (line 50) | LRESULT
FILE: legacy/app/windows/runner/flutter_window.h
function class (line 12) | class FlutterWindow : public Win32Window {
FILE: legacy/app/windows/runner/main.cpp
function wWinMain (line 8) | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
FILE: legacy/app/windows/runner/utils.cpp
function CreateAndAttachConsole (line 10) | void CreateAndAttachConsole() {
function GetCommandLineArguments (line 24) | std::vector<std::string> GetCommandLineArguments() {
function Utf8FromUtf16 (line 44) | std::string Utf8FromUtf16(const wchar_t* utf16_string) {
FILE: legacy/app/windows/runner/win32_window.cpp
function Scale (line 36) | int Scale(int source, double scale_factor) {
function EnableFullDpiSupportIfAvailable (line 42) | void EnableFullDpiSupportIfAvailable(HWND hwnd) {
class WindowClassRegistrar (line 59) | class WindowClassRegistrar {
method WindowClassRegistrar (line 64) | static WindowClassRegistrar* GetInstance() {
method WindowClassRegistrar (line 80) | WindowClassRegistrar() = default;
function wchar_t (line 89) | const wchar_t* WindowClassRegistrar::GetWindowClass() {
function LRESULT (line 157) | LRESULT CALLBACK Win32Window::WndProc(HWND const window,
function LRESULT (line 176) | LRESULT
function Win32Window (line 236) | Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
function RECT (line 252) | RECT Win32Window::GetClientArea() {
function HWND (line 258) | HWND Win32Window::GetHandle() {
FILE: legacy/app/windows/runner/win32_window.h
type Size (line 21) | struct Size {
FILE: legacy/backend/aws/fetchImageAsBase64/index.mjs
function handler (line 4) | async function handler(event) {
FILE: legacy/dist/canvaskit/canvaskit.js
function b (line 12) | function b(m,p,w){return m&&m.hasOwnProperty(p)?m[p]:w}
function c (line 12) | function c(m){var p=da(ea);ea[p]=m;return p}
function d (line 12) | function d(m){return m.naturalHeight||m.videoHeight||m.displayHeight||m....
function f (line 12) | function f(m){return m.naturalWidth||m.videoWidth||m.displayWidth||m.width}
function k (line 12) | function k(m,p,w,y){m.bindTexture(m.TEXTURE_2D,p);y||w.alphaType!==a.Alp...
function l (line 12) | function l(m,p,w){w||p.alphaType!==a.AlphaType.Premul||
function b (line 23) | function b(g){return(f(255*g[3])<<24|f(255*g[0])<<16|f(255*g[1])<<8|f(25...
function c (line 23) | function c(g){if(g&&g._ck)return g;if(g instanceof Float32Array){for(var...
function d (line 23) | function d(g){if(void 0===g)return 1;var e=parseFloat(g);return g&&-1!==...
function f (line 23) | function f(g){return Math.round(Math.max(0,
function k (line 24) | function k(g,e){e&&e._ck||a._free(g)}
function l (line 24) | function l(g,e,h){if(!g||!g.length)return M;if(g&&g._ck)return g.byteOff...
function m (line 24) | function m(g){var e={Rd:M,count:g.length,colorType:a.ColorType.RGBA_F32}...
function p (line 25) | function p(g){if(!g)return M;var e=T.toTypedArray();if(g.length){if(6===...
function w (line 26) | function w(g){if(!g)return M;var e=Y.toTypedArray();if(g.length){if(16!=...
function y (line 27) | function y(g,e){return l(g,"HEAPF32",e||va)}
function B (line 27) | function B(g,e,h,n){var t=Ma.toTypedArray();t[0]=g;t[1]=e;t[2]=h;t[3]=n;...
function D (line 27) | function D(g){for(var e=new Float32Array(4),h=0;4>h;h++)e[h]=a.HEAPF32[g...
function u (line 27) | function u(g,e){return l(g,"HEAPF32",e||X)}
function F (line 27) | function F(g,e){return l(g,
function g (line 32) | function g(e,h,n,t,x,z,E){z||(z=4*t.width,t.colorType===a.ColorType.RGBA...
function e (line 65) | function e(q){q&&(q.dir=0===q.dir?g.TextDirection.RTL:g.TextDirection.LT...
function h (line 65) | function h(q){if(!q||!q.length)return[];
function n (line 66) | function n(q){q=q||{};void 0===q.weight&&(q.weight=g.FontWeight.Normal);...
function t (line 66) | function t(q){if(!q||!q.length)return M;for(var A=[],P=0;P<q.length;P++)...
function x (line 66) | function x(q){if(J[q])return J[q];var A=
function z (line 67) | function z(q){q._colorPtr=y(q.color);q._foregroundColorPtr=M;q._backgrou...
function E (line 70) | function E(q){g._free(q._fontFamiliesPtr);g._free(q._shadowColorsPtr);g....
function La (line 92) | function La(){var a=Fa.buffer;r.HEAP8=Ha=new Int8Array(a);r.HEAP16=Ia=ne...
function Ra (line 92) | function Ra(){var a=r.preRun.shift();Oa.unshift(a)}
function Ea (line 93) | function Ea(a){if(r.onAbort)r.onAbort(a);a="Aborted("+a+")";Ca(a);Ga=!0;...
function Xa (line 93) | function Xa(a){return a.startsWith("data:application/octet-stream;base64...
function $a (line 93) | function $a(a){if(a==Ya&&Da)return new Uint8Array(Da);if(xa)return xa(a)...
function ab (line 94) | function ab(a){if(!Da&&(pa||ra)){if("function"==typeof fetch&&!a.startsW...
function bb (line 94) | function bb(a,b,c){return ab(a).then(d=>WebAssembly.instantiate(d,b)).th...
function cb (line 95) | function cb(a,b){var c=Ya;return Da||"function"!=typeof WebAssembly.inst...
function db (line 95) | function db(a){this.name="ExitStatus";this.message=`Program terminated w...
function fb (line 96) | function fb(a){this.Kd=a-24;this.Pe=function(b){L[this.Kd+4>>2]=b};this....
function mb (line 98) | function mb(a){for(;a.length;){var b=a.pop();a.pop()(b)}}
function nb (line 98) | function nb(a){return this.fromWireType(K[a>>2])}
function sb (line 98) | function sb(a){throw new rb(a);}
function tb (line 99) | function tb(a,b,c){function d(m){m=c(m);m.length!==a.length&&sb("Mismatc...
function vb (line 100) | function vb(a){switch(a){case 1:return 0;case 2:return 1;case 4:return 2...
function O (line 100) | function O(a){for(var b="";C[a];)b+=wb[C[a++]];return b}
function Q (line 100) | function Q(a){throw new xb(a);}
function yb (line 101) | function yb(a,b,c={}){var d=b.name;a||Q(`type "${d}" must have a positiv...
function ub (line 101) | function ub(a,b,c={}){if(!("argPackAdvance"in b))throw new TypeError("re...
function zb (line 101) | function zb(a){Q(a.kd.Nd.Ld.name+" instance already deleted")}
function Bb (line 101) | function Bb(){}
function Cb (line 102) | function Cb(a){--a.count.value;0===a.count.value&&(a.Pd?a.Td.Xd(a.Pd):a....
function Db (line 102) | function Db(a,b,c){if(b===c)return a;if(void 0===c.Qd)return null;a=Db(a...
function Lb (line 102) | function Lb(){for(;Kb.length;){var a=Kb.pop();a.kd.ee=!1;a["delete"]()}}
function Ob (line 102) | function Ob(a,b){for(void 0===b&&Q("ptr should not be undefined");a.Qd;)...
function Pb (line 103) | function Pb(a,b){b.Nd&&b.Kd||sb("makeClassHandle requires ptr and ptrTyp...
function Qb (line 103) | function Qb(a){if("undefined"===typeof FinalizationRegistry)return Qb=b=...
function Rb (line 103) | function Rb(){}
function Sb (line 104) | function Sb(a){if(void 0===a)return"_unknown";a=a.replace(/[^a-zA-Z0-9_]...
function Tb (line 104) | function Tb(a,b){a=Sb(a);return{[a]:function(){return b.apply(this,argum...
function Ub (line 105) | function Ub(a,b,c){if(void 0===a[b].Od){var d=a[b];a[b]=function(){a[b]....
function Vb (line 106) | function Vb(a,b,c){r.hasOwnProperty(a)?((void 0===c||void 0!==r[a].Od&&v...
function Wb (line 106) | function Wb(a,b,c,d,f,k,l,m){this.name=a;this.constructor=b;this.fe=c;th...
function Xb (line 107) | function Xb(a,b,c){for(;b!==c;)b.ke||Q(`Expected null or instance of ${c...
function Yb (line 107) | function Yb(a,b){if(null===b)return this.Ae&&Q(`null is not a valid ${th...
function $b (line 108) | function $b(a,b){if(null===b){this.Ae&&Q(`null is not a valid ${this.nam...
function bc (line 110) | function bc(a,b){if(null===b)return this.Ae&&Q(`null is not a valid ${th...
function cc (line 111) | function cc(a,b,c,d,f,k,l,m,p,w,y){this.name=a;this.Ld=b;this.Ae=c;this....
function dc (line 111) | function dc(a,b,c){r.hasOwnProperty(a)||sb("Replacing nonexistant public...
function mc (line 112) | function mc(a,b){a=O(a);var c=a.includes("j")?ec(a,b):Na.get(b);"functio...
function oc (line 112) | function oc(a){a=pc(a);var b=O(a);qc(a);return b}
function rc (line 113) | function rc(a,b){function c(k){f[k]||pb[k]||(qb[k]?qb[k].forEach(c):(d.p...
function sc (line 114) | function sc(a,b,c,d,f){var k=b.length;2>k&&Q("argTypes array size mismat...
function tc (line 115) | function tc(a,b){for(var c=[],d=0;d<a;d++)c.push(L[b+4*d>>2]);return c}
function uc (line 115) | function uc(){this.Wd=[void 0];this.Ie=[]}
function wc (line 115) | function wc(a){a>=vc.Zd&&0===--vc.get(a).Le&&vc.we(a)}
function yc (line 116) | function yc(a,b,c){switch(b){case 0:return function(d){return this.fromW...
function zc (line 117) | function zc(a,b){var c=pb[a];void 0===c&&Q(b+" has unknown type "+oc(a))...
function Zb (line 117) | function Zb(a){if(null===a)return"null";var b=typeof a;return"object"===...
function Ac (line 117) | function Ac(a,b){switch(b){case 2:return function(c){return this.fromWir...
function Bc (line 118) | function Bc(a,b,c){switch(b){case 0:return c?function(d){return Ha[d]}:f...
function Kc (line 122) | function Kc(a){var b=Jc[a];return void 0===b?O(a):b}
function Mc (line 123) | function Mc(){function a(b){b.$$$embind_global$$$=b;var c="object"==type...
function Nc (line 124) | function Nc(a){var b=Lc.length;Lc.push(a);return b}
function Oc (line 124) | function Oc(a,b){for(var c=Array(a),d=0;d<a;++d)c[d]=zc(L[b+4*d>>2],"par...
function Qc (line 124) | function Qc(a){var b=Array(a+1);return function(c,d,f){b[0]=c;for(var k=...
function Sc (line 125) | function Sc(a){var b=a.getExtension("ANGLE_instanced_arrays");b&&(a.vert...
function Tc (line 126) | function Tc(a){var b=a.getExtension("OES_vertex_array_object");b&&(a.cre...
function Uc (line 126) | function Uc(a){var b=a.getExtension("WEBGL_draw_buffers");b&&(a.drawBuff...
function R (line 127) | function R(a){hd||(hd=a)}
function da (line 127) | function da(a){for(var b=Vc++,c=a.length;c<b;c++)a[c]=null;return b}
function fa (line 127) | function fa(a,b){a.Zd||(a.Zd=a.getContext,a.getContext=function(d,f){f=a...
function jd (line 128) | function jd(a,b){var c=da(ia),d={handle:c,attributes:b,version:b.majorVe...
function ha (line 128) | function ha(a){v=ia[a];r.vf=S=v&&v.Ud;return!(a&&!S)}
function kd (line 129) | function kd(a){a||(a=v);if(!a.gf){a.gf=!0;var b=a.Ud;Sc(b);Tc(b);Uc(b);b...
function pd (line 130) | function pd(a){S.bindVertexArray(ad[a])}
function qd (line 131) | function qd(a,b){for(var c=0;c<a;c++){var d=K[b+4*c>>2];S.deleteVertexAr...
function sd (line 131) | function sd(a,b,c,d){S.drawElements(a,b,c,d)}
function td (line 131) | function td(a,b,c,d){for(var f=0;f<a;f++){var k=S[c](),l=k&&da(d);k?(k.n...
function ud (line 131) | function ud(a,b){td(a,b,"createVertexArray",ad)}
function vd (line 132) | function vd(a,b,c){if(b){var d=void 0;switch(a){case 36346:d=1;break;cas...
function yd (line 135) | function yd(a){return"]"==a.slice(-1)&&a.lastIndexOf("[")}
function zd (line 135) | function zd(a){a-=5120;return 0==a?Ha:1==a?C:2==a?Ia:4==a?K:6==a?N:5==a|...
function Ad (line 135) | function Ad(a,b,c,d,f){a=zd(a);var k=31-Math.clz32(a.BYTES_PER_ELEMENT),...
function W (line 136) | function W(a){var b=S.We;if(b){var c=b.je[a];"number"==typeof c&&(b.je[a...
function Gd (line 136) | function Gd(a){var b=Array(ja(a)+1);ka(a,b,0,b.length);return b}
function f (line 137) | function f(u,F,H){for(u="number"==typeof u?u.toString():u||"";u.length<F...
function k (line 137) | function k(u,F){return f(u,F,"0")}
function l (line 137) | function l(u,F){function H(ca){return 0>ca?-1:0<ca?1:0}var T;0===(T=H(u....
function m (line 137) | function m(u){switch(u.getDay()){case 0:return new Date(u.getFullYear()-...
function p (line 138) | function p(u){var F=u.$d;for(u=new Date((new Date(u.ae+1900,0,1)).getTim...
method constructor (line 143) | constructor(a){super(a);this.name="InternalError"}
method constructor (line 143) | constructor(a){super(a);this.name="BindingError"}
function b (line 148) | function b(){return this.pe?Pb(this.Ld.fe,{Nd:this.jf,Kd:c,Td:this,Pd:a}...
method get (line 150) | get(a){return this.Wd[a]}
method has (line 150) | has(a){return void 0!==this.Wd[a]}
method ve (line 150) | ve(a){var b=this.Ie.pop()||this.Wd.length;this.Wd[b]=a;return b}
method we (line 150) | we(a){this.Wd[a]=void 0;this.Ie.push(a)}
function w (line 156) | function w(){rc(`Cannot call ${y} due to unbound types`,m)}
function y (line 158) | function y(){rc(`Cannot call ${B} due to unbound types`,p)}
function f (line 159) | function f(){}
function d (line 161) | function d(k){k>>=2;var l=
function a (line 197) | function a(c){G=c=c.exports;Fa=G.ad;La();Na=G.dd;Pa.unshift(G.bd);Ua--;r...
function Wd (line 202) | function Wd(a,b,c,d,f){var k=be();try{Na.get(a)(b,c,d,f)}catch(l){ce(k);...
function Od (line 202) | function Od(a,b,c){var d=be();try{return Na.get(a)(b,c)}catch(f){ce(d);i...
function Ud (line 202) | function Ud(a,b,c){var d=be();try{Na.get(a)(b,c)}catch(f){ce(d);if(f!==f...
function Nd (line 202) | function Nd(a,b){var c=be();try{return Na.get(a)(b)}catch(d){ce(c);if(d!...
function Td (line 202) | function Td(a,b){var c=be();try{Na.get(a)(b)}catch(d){ce(c);if(d!==d+0)t...
function Pd (line 203) | function Pd(a,b,c,d){var f=be();try{return Na.get(a)(b,c,d)}catch(k){ce(...
function Zd (line 203) | function Zd(a,b,c,d,f,k,l,m,p,w){var y=be();try{Na.get(a)(b,c,d,f,k,l,m,...
function Vd (line 203) | function Vd(a,b,c,d){var f=be();try{Na.get(a)(b,c,d)}catch(k){ce(f);if(k...
function Yd (line 203) | function Yd(a,b,c,d,f,k,l){var m=be();try{Na.get(a)(b,c,d,f,k,l)}catch(p...
function Qd (line 204) | function Qd(a,b,c,d,f){var k=be();try{return Na.get(a)(b,c,d,f)}catch(l)...
function Rd (line 204) | function Rd(a,b,c,d,f,k,l){var m=be();try{return Na.get(a)(b,c,d,f,k,l)}...
function Xd (line 204) | function Xd(a,b,c,d,f,k){var l=be();try{Na.get(a)(b,c,d,f,k)}catch(m){ce...
function Sd (line 204) | function Sd(a,b,c,d,f,k,l,m,p,w){var y=be();try{return Na.get(a)(b,c,d,f...
function fe (line 205) | function fe(){function a(){if(!de&&(de=!0,r.calledRun=!0,!Ga)){eb(Pa);aa...
FILE: legacy/dist/canvaskit/chromium/canvaskit.js
function b (line 12) | function b(m,q,w){return m&&m.hasOwnProperty(q)?m[q]:w}
function c (line 12) | function c(m){var q=da(ea);ea[q]=m;return q}
function e (line 12) | function e(m){return m.naturalHeight||m.videoHeight||m.displayHeight||m....
function f (line 12) | function f(m){return m.naturalWidth||m.videoWidth||m.displayWidth||m.width}
function k (line 12) | function k(m,q,w,y){m.bindTexture(m.TEXTURE_2D,q);y||w.alphaType!==a.Alp...
function l (line 12) | function l(m,q,w){w||q.alphaType!==a.AlphaType.Premul||
function b (line 23) | function b(g){return(f(255*g[3])<<24|f(255*g[0])<<16|f(255*g[1])<<8|f(25...
function c (line 23) | function c(g){if(g&&g._ck)return g;if(g instanceof Float32Array){for(var...
function e (line 23) | function e(g){if(void 0===g)return 1;var d=parseFloat(g);return g&&-1!==...
function f (line 23) | function f(g){return Math.round(Math.max(0,
function k (line 24) | function k(g,d){d&&d._ck||a._free(g)}
function l (line 24) | function l(g,d,h){if(!g||!g.length)return M;if(g&&g._ck)return g.byteOff...
function m (line 24) | function m(g){var d={Ld:M,count:g.length,colorType:a.ColorType.RGBA_F32}...
function q (line 25) | function q(g){if(!g)return M;var d=T.toTypedArray();if(g.length){if(6===...
function w (line 26) | function w(g){if(!g)return M;var d=Y.toTypedArray();if(g.length){if(16!=...
function y (line 27) | function y(g,d){return l(g,"HEAPF32",d||va)}
function B (line 27) | function B(g,d,h,n){var t=Ma.toTypedArray();t[0]=g;t[1]=d;t[2]=h;t[3]=n;...
function D (line 27) | function D(g){for(var d=new Float32Array(4),h=0;4>h;h++)d[h]=a.HEAPF32[g...
function u (line 27) | function u(g,d){return l(g,"HEAPF32",d||X)}
function F (line 27) | function F(g,d){return l(g,
function g (line 32) | function g(d,h,n,t,v,z,E){z||(z=4*t.width,t.colorType===a.ColorType.RGBA...
function d (line 65) | function d(p){p&&(p.dir=0===p.dir?g.TextDirection.RTL:g.TextDirection.LT...
function h (line 65) | function h(p){if(!p||!p.length)return[];
function n (line 66) | function n(p){p=p||{};void 0===p.weight&&(p.weight=g.FontWeight.Normal);...
function t (line 66) | function t(p){if(!p||!p.length)return M;for(var A=[],O=0;O<p.length;O++)...
function v (line 66) | function v(p){if(J[p])return J[p];var A=
function z (line 67) | function z(p){p._colorPtr=y(p.color);p._foregroundColorPtr=M;p._backgrou...
function E (line 70) | function E(p){g._free(p._fontFamiliesPtr);g._free(p._shadowColorsPtr);g....
function La (line 92) | function La(){var a=Fa.buffer;r.HEAP8=Ha=new Int8Array(a);r.HEAP16=Ia=ne...
function Ra (line 92) | function Ra(){var a=r.preRun.shift();Oa.unshift(a)}
function Ea (line 93) | function Ea(a){if(r.onAbort)r.onAbort(a);a="Aborted("+a+")";Ca(a);Ga=!0;...
function Xa (line 93) | function Xa(a){return a.startsWith("data:application/octet-stream;base64...
function $a (line 93) | function $a(a){if(a==Ya&&Da)return new Uint8Array(Da);if(xa)return xa(a)...
function ab (line 94) | function ab(a){if(!Da&&(pa||ra)){if("function"==typeof fetch&&!a.startsW...
function bb (line 94) | function bb(a,b,c){return ab(a).then(e=>WebAssembly.instantiate(e,b)).th...
function cb (line 95) | function cb(a,b){var c=Ya;return Da||"function"!=typeof WebAssembly.inst...
function db (line 95) | function db(a){this.name="ExitStatus";this.message=`Program terminated w...
function fb (line 96) | function fb(a){this.Ed=a-24;this.Ie=function(b){L[this.Ed+4>>2]=b};this....
function mb (line 98) | function mb(a){for(;a.length;){var b=a.pop();a.pop()(b)}}
function nb (line 98) | function nb(a){return this.fromWireType(K[a>>2])}
function sb (line 98) | function sb(a){throw new rb(a);}
function tb (line 99) | function tb(a,b,c){function e(m){m=c(m);m.length!==a.length&&sb("Mismatc...
function vb (line 100) | function vb(a){switch(a){case 1:return 0;case 2:return 1;case 4:return 2...
function P (line 100) | function P(a){for(var b="";C[a];)b+=wb[C[a++]];return b}
function Q (line 100) | function Q(a){throw new xb(a);}
function yb (line 101) | function yb(a,b,c={}){var e=b.name;a||Q(`type "${e}" must have a positiv...
function ub (line 101) | function ub(a,b,c={}){if(!("argPackAdvance"in b))throw new TypeError("re...
function zb (line 101) | function zb(a){Q(a.jd.Hd.Fd.name+" instance already deleted")}
function Bb (line 101) | function Bb(){}
function Cb (line 102) | function Cb(a){--a.count.value;0===a.count.value&&(a.Jd?a.Nd.Rd(a.Jd):a....
function Db (line 102) | function Db(a,b,c){if(b===c)return a;if(void 0===c.Kd)return null;a=Db(a...
function Lb (line 102) | function Lb(){for(;Kb.length;){var a=Kb.pop();a.jd.Zd=!1;a["delete"]()}}
function Ob (line 102) | function Ob(a,b){for(void 0===b&&Q("ptr should not be undefined");a.Kd;)...
function Pb (line 103) | function Pb(a,b){b.Hd&&b.Ed||sb("makeClassHandle requires ptr and ptrTyp...
function Qb (line 103) | function Qb(a){if("undefined"===typeof FinalizationRegistry)return Qb=b=...
function Rb (line 103) | function Rb(){}
function Sb (line 104) | function Sb(a){if(void 0===a)return"_unknown";a=a.replace(/[^a-zA-Z0-9_]...
function Tb (line 104) | function Tb(a,b){a=Sb(a);return{[a]:function(){return b.apply(this,argum...
function Ub (line 105) | function Ub(a,b,c){if(void 0===a[b].Id){var e=a[b];a[b]=function(){a[b]....
function Vb (line 106) | function Vb(a,b,c){r.hasOwnProperty(a)?((void 0===c||void 0!==r[a].Id&&v...
function Wb (line 106) | function Wb(a,b,c,e,f,k,l,m){this.name=a;this.constructor=b;this.$d=c;th...
function Xb (line 107) | function Xb(a,b,c){for(;b!==c;)b.ee||Q(`Expected null or instance of ${c...
function Yb (line 107) | function Yb(a,b){if(null===b)return this.ue&&Q(`null is not a valid ${th...
function $b (line 108) | function $b(a,b){if(null===b){this.ue&&Q(`null is not a valid ${this.nam...
function bc (line 110) | function bc(a,b){if(null===b)return this.ue&&Q(`null is not a valid ${th...
function cc (line 111) | function cc(a,b,c,e,f,k,l,m,q,w,y){this.name=a;this.Fd=b;this.ue=c;this....
function dc (line 111) | function dc(a,b,c){r.hasOwnProperty(a)||sb("Replacing nonexistant public...
function mc (line 112) | function mc(a,b){a=P(a);var c=a.includes("j")?ec(a,b):Na.get(b);"functio...
function oc (line 112) | function oc(a){a=pc(a);var b=P(a);qc(a);return b}
function rc (line 113) | function rc(a,b){function c(k){f[k]||pb[k]||(qb[k]?qb[k].forEach(c):(e.p...
function sc (line 114) | function sc(a,b,c,e,f){var k=b.length;2>k&&Q("argTypes array size mismat...
function tc (line 115) | function tc(a,b){for(var c=[],e=0;e<a;e++)c.push(L[b+4*e>>2]);return c}
function uc (line 115) | function uc(){this.Qd=[void 0];this.Ce=[]}
function wc (line 115) | function wc(a){a>=vc.Td&&0===--vc.get(a).Fe&&vc.qe(a)}
function yc (line 116) | function yc(a,b,c){switch(b){case 0:return function(e){return this.fromW...
function zc (line 117) | function zc(a,b){var c=pb[a];void 0===c&&Q(b+" has unknown type "+oc(a))...
function Zb (line 117) | function Zb(a){if(null===a)return"null";var b=typeof a;return"object"===...
function Ac (line 117) | function Ac(a,b){switch(b){case 2:return function(c){return this.fromWir...
function Bc (line 118) | function Bc(a,b,c){switch(b){case 0:return c?function(e){return Ha[e]}:f...
function Kc (line 122) | function Kc(a){var b=Jc[a];return void 0===b?P(a):b}
function Mc (line 123) | function Mc(){function a(b){b.$$$embind_global$$$=b;var c="object"==type...
function Nc (line 124) | function Nc(a){var b=Lc.length;Lc.push(a);return b}
function Oc (line 124) | function Oc(a,b){for(var c=Array(a),e=0;e<a;++e)c[e]=zc(L[b+4*e>>2],"par...
function Qc (line 124) | function Qc(a){var b=Array(a+1);return function(c,e,f){b[0]=c;for(var k=...
function Sc (line 125) | function Sc(a){var b=a.getExtension("ANGLE_instanced_arrays");b&&(a.vert...
function Tc (line 126) | function Tc(a){var b=a.getExtension("OES_vertex_array_object");b&&(a.cre...
function Uc (line 126) | function Uc(a){var b=a.getExtension("WEBGL_draw_buffers");b&&(a.drawBuff...
function R (line 127) | function R(a){hd||(hd=a)}
function da (line 127) | function da(a){for(var b=Vc++,c=a.length;c<b;c++)a[c]=null;return b}
function fa (line 127) | function fa(a,b){a.Td||(a.Td=a.getContext,a.getContext=function(e,f){f=a...
function jd (line 128) | function jd(a,b){var c=da(ia),e={handle:c,attributes:b,version:b.majorVe...
function ha (line 128) | function ha(a){x=ia[a];r.pf=S=x&&x.Od;return!(a&&!S)}
function kd (line 129) | function kd(a){a||(a=x);if(!a.af){a.af=!0;var b=a.Od;Sc(b);Tc(b);Uc(b);b...
function pd (line 130) | function pd(a){S.bindVertexArray(ad[a])}
function qd (line 131) | function qd(a,b){for(var c=0;c<a;c++){var e=K[b+4*c>>2];S.deleteVertexAr...
function sd (line 131) | function sd(a,b,c,e){S.drawElements(a,b,c,e)}
function td (line 131) | function td(a,b,c,e){for(var f=0;f<a;f++){var k=S[c](),l=k&&da(e);k?(k.n...
function ud (line 131) | function ud(a,b){td(a,b,"createVertexArray",ad)}
function vd (line 132) | function vd(a,b,c){if(b){var e=void 0;switch(a){case 36346:e=1;break;cas...
function yd (line 135) | function yd(a){return"]"==a.slice(-1)&&a.lastIndexOf("[")}
function zd (line 135) | function zd(a){a-=5120;return 0==a?Ha:1==a?C:2==a?Ia:4==a?K:6==a?N:5==a|...
function Ad (line 135) | function Ad(a,b,c,e,f){a=zd(a);var k=31-Math.clz32(a.BYTES_PER_ELEMENT),...
function W (line 136) | function W(a){var b=S.Qe;if(b){var c=b.de[a];"number"==typeof c&&(b.de[a...
function Gd (line 136) | function Gd(a){var b=Array(ja(a)+1);ka(a,b,0,b.length);return b}
function f (line 137) | function f(u,F,H){for(u="number"==typeof u?u.toString():u||"";u.length<F...
function k (line 137) | function k(u,F){return f(u,F,"0")}
function l (line 137) | function l(u,F){function H(ca){return 0>ca?-1:0<ca?1:0}var T;0===(T=H(u....
function m (line 137) | function m(u){switch(u.getDay()){case 0:return new Date(u.getFullYear()-...
function q (line 138) | function q(u){var F=u.Ud;for(u=new Date((new Date(u.Vd+1900,0,1)).getTim...
method constructor (line 143) | constructor(a){super(a);this.name="InternalError"}
method constructor (line 143) | constructor(a){super(a);this.name="BindingError"}
function b (line 148) | function b(){return this.je?Pb(this.Fd.$d,{Hd:this.cf,Ed:c,Nd:this,Jd:a}...
method get (line 150) | get(a){return this.Qd[a]}
method has (line 150) | has(a){return void 0!==this.Qd[a]}
method pe (line 150) | pe(a){var b=this.Ce.pop()||this.Qd.length;this.Qd[b]=a;return b}
method qe (line 150) | qe(a){this.Qd[a]=void 0;this.Ce.push(a)}
function w (line 156) | function w(){rc(`Cannot call ${y} due to unbound types`,m)}
function y (line 158) | function y(){rc(`Cannot call ${B} due to unbound types`,q)}
function f (line 159) | function f(){}
function e (line 161) | function e(k){k>>=2;var l=
function a (line 197) | function a(c){G=c=c.exports;Fa=G.$c;La();Na=G.cd;Pa.unshift(G.ad);Ua--;r...
function Wd (line 201) | function Wd(a,b,c,e,f){var k=be();try{Na.get(a)(b,c,e,f)}catch(l){ce(k);...
function Od (line 201) | function Od(a,b,c){var e=be();try{return Na.get(a)(b,c)}catch(f){ce(e);i...
function Ud (line 201) | function Ud(a,b,c){var e=be();try{Na.get(a)(b,c)}catch(f){ce(e);if(f!==f...
function Nd (line 201) | function Nd(a,b){var c=be();try{return Na.get(a)(b)}catch(e){ce(c);if(e!...
function Td (line 201) | function Td(a,b){var c=be();try{Na.get(a)(b)}catch(e){ce(c);if(e!==e+0)t...
function Pd (line 202) | function Pd(a,b,c,e){var f=be();try{return Na.get(a)(b,c,e)}catch(k){ce(...
function Zd (line 202) | function Zd(a,b,c,e,f,k,l,m,q,w){var y=be();try{Na.get(a)(b,c,e,f,k,l,m,...
function Vd (line 202) | function Vd(a,b,c,e){var f=be();try{Na.get(a)(b,c,e)}catch(k){ce(f);if(k...
function Yd (line 202) | function Yd(a,b,c,e,f,k,l){var m=be();try{Na.get(a)(b,c,e,f,k,l)}catch(q...
function Qd (line 203) | function Qd(a,b,c,e,f){var k=be();try{return Na.get(a)(b,c,e,f)}catch(l)...
function Rd (line 203) | function Rd(a,b,c,e,f,k,l){var m=be();try{return Na.get(a)(b,c,e,f,k,l)}...
function Xd (line 203) | function Xd(a,b,c,e,f,k){var l=be();try{Na.get(a)(b,c,e,f,k)}catch(m){ce...
function Sd (line 203) | function Sd(a,b,c,e,f,k,l,m,q,w){var y=be();try{return Na.get(a)(b,c,e,f...
function fe (line 204) | function fe(){function a(){if(!de&&(de=!0,r.calledRun=!0,!Ga)){eb(Pa);aa...
FILE: legacy/dist/canvaskit/skwasm.js
function aa (line 8) | function aa(){d.buffer!=h.buffer&&l();return h}
function p (line 8) | function p(){d.buffer!=h.buffer&&l();return ca}
function q (line 8) | function q(){d.buffer!=h.buffer&&l();return da}
function t (line 8) | function t(){d.buffer!=h.buffer&&l();return ea}
function v (line 8) | function v(){d.buffer!=h.buffer&&l();return fa}
function ha (line 8) | function ha(){d.buffer!=h.buffer&&l();return ia}
function qa (line 9) | function qa(a){return w.locateFile?w.locateFile(a,C):C+a}
function l (line 14) | function l(){var a=d.buffer;w.HEAP8=h=new Int8Array(a);w.HEAP16=Da=new I...
function Ka (line 16) | function Ka(){return noExitRuntime||0<Ja}
function Na (line 16) | function Na(){H++;w.monitorRunDependencies&&w.monitorRunDependencies(H)}
function Oa (line 16) | function Oa(){H--;w.monitorRunDependencies&&w.monitorRunDependencies(H);...
function za (line 17) | function za(a){if(w.onAbort)w.onAbort(a);a="Aborted("+a+")";D(a);Ba=!0;C...
function Pa (line 17) | function Pa(a){return a.startsWith("data:application/octet-stream;base64...
function Ra (line 17) | function Ra(a){if(a==Qa&&ya)return new Uint8Array(ya);if(ta)return ta(a)...
function Sa (line 18) | function Sa(a){if(!ya&&(oa||pa)){if("function"==typeof fetch&&!a.startsW...
function Ta (line 18) | function Ta(a,b,c){return Sa(a).then(e=>WebAssembly.instantiate(e,b)).th...
function Ua (line 19) | function Ua(a,b){var c=Qa;return ya||"function"!=typeof WebAssembly.inst...
function Va (line 19) | function Va(a){this.name="ExitStatus";this.message=`Program terminated w...
function Wa (line 20) | function Wa(a){a.terminate();a.onmessage=()=>{}}
function Xa (line 20) | function Xa(a){(a=I.g[a])||za();I.Aa(a)}
function Ya (line 20) | function Ya(a){var b=I.ma();if(!b)return 6;I.u.push(b);I.g[a.m]=b;b.m=a....
function ab (line 22) | function ab(a){if(A)return K(1,1,a);Ca=a;if(!Ka()){I.Ca();if(w.onExit)w....
function bb (line 27) | function bb(a){if(A)return K(2,0,a);cb(a)}
function kb (line 27) | function kb(a){this.C=a-24;this.ua=function(b){t()[this.C+4>>2]=b};this....
function nb (line 28) | function nb(a,b,c,e){return A?K(3,1,a,b,c,e):ob(a,b,c,e)}
function ob (line 29) | function ob(a,b,c,e){if("undefined"==typeof SharedArrayBuffer)return D("...
function qb (line 32) | function qb(a,b,c){return A?K(4,1,a,b,c):0}
function rb (line 32) | function rb(a,b){if(A)return K(5,1,a,b)}
function sb (line 32) | function sb(a,b,c){return A?K(6,1,a,b,c):0}
function tb (line 32) | function tb(a,b,c,e){if(A)return K(7,1,a,b,c,e)}
function vb (line 33) | function vb(a){"function"===typeof Atomics.Ia&&(Atomics.Ia(q(),a>>2,a).v...
function gb (line 33) | function gb(){var a=fb();a&&(vb(a),ub(()=>wb()))}
function Bb (line 35) | function Bb(a,b,c,e){b=b?J(b):"";xb(function(){var f=Cb(12),g=0;b&&(g=Ab...
function Eb (line 36) | function Eb(a){var b=a.getExtension("ANGLE_instanced_arrays");b&&(a.vert...
function Fb (line 37) | function Fb(a){var b=a.getExtension("OES_vertex_array_object");b&&(a.cre...
function Gb (line 37) | function Gb(a){var b=a.getExtension("WEBGL_draw_buffers");b&&(a.drawBuff...
function Hb (line 38) | function Hb(a){a.Z=a.getExtension("WEBGL_draw_instanced_base_vertex_base...
function Ib (line 38) | function Ib(a){a.ea=a.getExtension("WEBGL_multi_draw_instanced_base_vert...
function Jb (line 38) | function Jb(a){a.Ma=a.getExtension("WEBGL_multi_draw")}
function S (line 38) | function S(a){Ub||(Ub=a)}
function Vb (line 38) | function Vb(a){for(var b=Kb++,c=a.length;c<b;c++)a[c]=null;return b}
function Wb (line 39) | function Wb(a){var b={da:2,alpha:!0,depth:!0,stencil:!0,antialias:!1,pre...
function Xb (line 40) | function Xb(a,b){var c=pb(8);q()[c+4>>2]=fb();var e={handle:c,attributes...
function Yb (line 41) | function Yb(a){a||(a=T);if(!a.pa){a.pa=!0;var b=a.s;Eb(b);Fb(b);Gb(b);Hb...
function Zb (line 42) | function Zb(a){a=2<a?J(a):a;return L[a.substr(1)]||"canvas"==a&&Object.k...
function $b (line 42) | function $b(a,b,c){var e=Zb(a);if(!e)return-4;e.h&&(q()[e.h>>2]=b,q()[e....
function ac (line 43) | function ac(a,b,c){return A?K(8,1,a,b,c):$b(a,b,c)}
function bc (line 43) | function bc(a,b,c,e,f,g,k,n){return A?K(9,1,a,b,c,e,f,g,k,n):-52}
function cc (line 43) | function cc(a,b,c,e,f,g,k){if(A)return K(10,1,a,b,c,e,f,g,k)}
function dc (line 43) | function dc(a,b){U.bindFramebuffer(a,Mb[b])}
function ec (line 43) | function ec(a){U.clear(a)}
function fc (line 43) | function fc(a,b,c,e){U.clearColor(a,b,c,e)}
function gc (line 43) | function gc(a){U.clearStencil(a)}
function hc (line 44) | function hc(a,b,c){if(b){var e=void 0;switch(a){case 36346:e=1;break;cas...
function ic (line 46) | function ic(a,b){hc(a,b,0)}
function K (line 47) | function K(a,b){var c=arguments.length-2,e=arguments;return xb(()=>{for(...
function oc (line 49) | function oc(a,b){if(A)return K(11,1,a,b);var c=0;nc().forEach(function(e...
function pc (line 49) | function pc(a,b){if(A)return K(12,1,a,b);var c=nc();t()[a>>2]=c.length;v...
function qc (line 49) | function qc(a){return A?K(13,1,a):52}
function rc (line 49) | function rc(a,b,c,e,f,g){return A?K(14,1,a,b,c,e,f,g):52}
function sc (line 49) | function sc(a,b,c,e){return A?K(15,1,a,b,c,e):52}
function tc (line 50) | function tc(a,b,c,e,f){return A?K(16,1,a,b,c,e,f):70}
function vc (line 50) | function vc(a,b,c,e){if(A)return K(17,1,a,b,c,e);for(var f=0,g=0;g<c;g++...
function wc (line 50) | function wc(a){U.bindVertexArray(Ob[a])}
function xc (line 50) | function xc(a,b){for(var c=0;c<a;c++){var e=q()[b+4*c>>2];U.deleteVertex...
function zc (line 51) | function zc(a,b,c,e){U.drawElements(a,b,c,e)}
function Ac (line 51) | function Ac(a,b,c,e){for(var f=0;f<a;f++){var g=U[c](),k=g&&Vb(e);g?(g.n...
function Bc (line 51) | function Bc(a,b){Ac(a,b,"createVertexArray",Ob)}
function Cc (line 51) | function Cc(a){return"]"==a.slice(-1)&&a.lastIndexOf("[")}
function Dc (line 51) | function Dc(a){a-=5120;0==a?a=aa():1==a?a=p():2==a?(d.buffer!=h.buffer&&...
function Ec (line 52) | function Ec(a,b,c,e,f){a=Dc(a);var g=31-Math.clz32(a.BYTES_PER_ELEMENT),...
function W (line 52) | function W(a){var b=U.la;if(b){var c=b.H[a];"number"==typeof c&&(b.H[a]=...
function Gc (line 52) | function Gc(){}
function Hc (line 52) | function Hc(){}
function Ic (line 52) | function Ic(){}
function Jc (line 52) | function Jc(){}
function Lc (line 52) | function Lc(){}
function Mc (line 52) | function Mc(){}
function Nc (line 52) | function Nc(){}
function Oc (line 52) | function Oc(){}
function Pc (line 52) | function Pc(){}
function Qc (line 53) | function Qc(){}
function Rc (line 53) | function Rc(){}
function Vc (line 53) | function Vc(a){var b=Array(yb(a)+1);zb(a,b,0,b.length);return b}
function f (line 54) | function f(m,z,B){for(m="number"==typeof m?m.toString():m||"";m.length<z...
function g (line 54) | function g(m,z){return f(m,z,"0")}
function k (line 54) | function k(m,z){function B(Kc){return 0>Kc?-1:0<Kc?1:0}var ba;0===(ba=B(...
function n (line 54) | function n(m){switch(m.getDay()){case 0:return new Date(m.getFullYear()-...
function r (line 55) | function r(m){var z=m.v;for(m=new Date((new Date(m.A+1900,0,1)).getTime(...
function f (line 61) | function f({data:g}){var k=g.G;if(k)switch(k){case "syncTimeOrigin":c=pe...
function a (line 101) | function a(c,e){F=c=c.exports;w.wasmExports=F;I.ha.push(F._emscripten_tl...
function gd (line 153) | function gd(a,b,c){var e=N();try{return G.get(a)(b,c)}catch(f){M(e);if(f...
function md (line 153) | function md(a,b,c){var e=N();try{G.get(a)(b,c)}catch(f){M(e);if(f!==f+0)...
function fd (line 153) | function fd(a,b){var c=N();try{return G.get(a)(b)}catch(e){M(c);if(e!==e...
function nd (line 153) | function nd(a,b,c,e){var f=N();try{G.get(a)(b,c,e)}catch(g){M(f);if(g!==...
function hd (line 153) | function hd(a,b,c,e){var f=N();try{return G.get(a)(b,c,e)}catch(g){M(f);...
function od (line 154) | function od(a,b,c,e,f){var g=N();try{G.get(a)(b,c,e,f)}catch(k){M(g);if(...
function pd (line 154) | function pd(a,b,c,e,f,g,k,n){var r=N();try{G.get(a)(b,c,e,f,g,k,n)}catch...
function ld (line 154) | function ld(a,b){var c=N();try{G.get(a)(b)}catch(e){M(c);if(e!==e+0)thro...
function kd (line 154) | function kd(a,b,c,e,f,g,k){var n=N();try{return G.get(a)(b,c,e,f,g,k)}ca...
function jd (line 155) | function jd(a,b,c,e,f){var g=N();try{return G.get(a)(b,c,e,f)}catch(k){M...
function td (line 159) | function td(){function a(){if(!rd&&(rd=!0,w.calledRun=!0,!Ba)){A||hb(Ha)...
FILE: legacy/dist/canvaskit/skwasm.worker.js
function threadPrintErr (line 1) | function threadPrintErr(){var text=Array.prototype.slice.call(arguments)...
function threadAlert (line 1) | function threadAlert(){var text=Array.prototype.slice.call(arguments).jo...
function handleMessage (line 1) | function handleMessage(e){try{if(e.data.cmd==="load"){let messageQueue=[...
FILE: legacy/dist/flutter.js
function L (line 1) | function L(){let o=document.querySelector("base");return o&&o.getAttribu...
function U (line 1) | function U(o){return o===""||o.endsWith("/")?o:`${o}/`}
method constructor (line 1) | constructor(){this._scriptLoaded=!1}
method setTrustedTypesPolicy (line 1) | setTrustedTypesPolicy(e){this._ttPolicy=e}
method loadEntrypoint (line 1) | async loadEntrypoint(e){let{entrypointUrl:n=`${w}main.dart.js`,onEntrypo...
method load (line 1) | async load(e,n,r,t,i){if(i??=s=>{s.initializeEngine(r).then(a=>a.runApp(...
method didCreateEngineInitializer (line 1) | didCreateEngineInitializer(e){typeof this._didCreateEngineInitializerRes...
method _loadJSEntrypoint (line 1) | _loadJSEntrypoint(e,n,r){let t=typeof n=="function";if(!this._scriptLoad...
method _loadWasmEntrypoint (line 1) | async _loadWasmEntrypoint(e,n,r){if(!this._scriptLoaded){this._scriptLoa...
method _createScriptTag (line 1) | _createScriptTag(e,n){let r=document.createElement("script");r.type="app...
function b (line 1) | async function b(o,e,n){if(e<0)return o;let r,t=new Promise((i,s)=>{r=se...
method setTrustedTypesPolicy (line 1) | setTrustedTypesPolicy(e){this._ttPolicy=e}
method loadServiceWorker (line 1) | loadServiceWorker(e){if(!e)return console.debug("Null serviceWorker conf...
method _getNewServiceWorker (line 3) | async _getNewServiceWorker(e,n){if(!e.active&&(e.installing||e.waiting))...
method _waitForServiceWorkerActivation (line 3) | async _waitForServiceWorkerActivation(e){if(!e||e.state==="activated")if...
method constructor (line 3) | constructor(e,n="flutter-js"){let r=e||[/\.js$/,/\.mjs$/];window.trusted...
method loadEntrypoint (line 3) | async loadEntrypoint(e){let{serviceWorker:n,...r}=e||{},t=new v,i=new h;...
method load (line 3) | async load({serviceWorkerSettings:e,onEntrypointLoaded:n,nonce:r,config:...
FILE: legacy/dist/flutter_bootstrap.js
function L (line 1) | function L(){let o=document.querySelector("base");return o&&o.getAttribu...
function U (line 1) | function U(o){return o===""||o.endsWith("/")?o:`${o}/`}
method constructor (line 1) | constructor(){this._scriptLoaded=!1}
method setTrustedTypesPolicy (line 1) | setTrustedTypesPolicy(e){this._ttPolicy=e}
method loadEntrypoint (line 1) | async loadEntrypoint(e){let{entrypointUrl:n=`${w}main.dart.js`,onEntrypo...
method load (line 1) | async load(e,n,r,t,i){if(i??=s=>{s.initializeEngine(r).then(a=>a.runApp(...
method didCreateEngineInitializer (line 1) | didCreateEngineInitializer(e){typeof this._didCreateEngineInitializerRes...
method _loadJSEntrypoint (line 1) | _loadJSEntrypoint(e,n,r){let t=typeof n=="function";if(!this._scriptLoad...
method _loadWasmEntrypoint (line 1) | async _loadWasmEntrypoint(e,n,r){if(!this._scriptLoaded){this._scriptLoa...
method _createScriptTag (line 1) | _createScriptTag(e,n){let r=document.createElement("script");r.type="app...
function b (line 1) | async function b(o,e,n){if(e<0)return o;let r,t=new Promise((i,s)=>{r=se...
method setTrustedTypesPolicy (line 1) | setTrustedTypesPolicy(e){this._ttPolicy=e}
method loadServiceWorker (line 1) | loadServiceWorker(e){if(!e)return console.debug("Null serviceWorker conf...
method _getNewServiceWorker (line 3) | async _getNewServiceWorker(e,n){if(!e.active&&(e.installing||e.waiting))...
method _waitForServiceWorkerActivation (line 3) | async _waitForServiceWorkerActivation(e){if(!e||e.state==="activated")if...
method constructor (line 3) | constructor(e,n="flutter-js"){let r=e||[/\.js$/,/\.mjs$/];window.trusted...
method loadEntrypoint (line 3) | async loadEntrypoint(e){let{serviceWorker:n,...r}=e||{},t=new v,i=new h;...
method load (line 3) | async load({serviceWorkerSettings:e,onEntrypointLoaded:n,nonce:r,config:...
FILE: legacy/dist/flutter_service_worker.js
constant MANIFEST (line 2) | const MANIFEST = 'flutter-app-manifest';
constant TEMP (line 3) | const TEMP = 'flutter-temp-cache';
constant CACHE_NAME (line 4) | const CACHE_NAME = 'flutter-app-cache';
constant RESOURCES (line 6) | const RESOURCES = {"flutter_bootstrap.js": "1dad2b46325bcd99fea57b142990...
constant CORE (line 49) | const CORE = ["main.dart.js",
function downloadOffline (line 178) | async function downloadOffline() {
function onlineFirst (line 198) | function onlineFirst(event) {
FILE: legacy/dist/tv.js
function rv (line 21) | function rv(lang) {
FILE: project/web/src/app/layout.tsx
function RootLayout (line 12) | function RootLayout({
FILE: project/web/src/app/page.tsx
function Home (line 4) | function Home() {
FILE: project/web/src/components/ui/button.tsx
type ButtonProps (line 36) | interface ButtonProps
FILE: project/web/src/components/ui/form.tsx
type FormFieldContextValue (line 18) | type FormFieldContextValue<
type FormItemContextValue (line 65) | type FormItemContextValue = {
FILE: project/web/src/components/ui/input.tsx
type InputProps (line 5) | interface InputProps
FILE: project/web/src/components/ui/pagination.tsx
type PaginationLinkProps (line 37) | type PaginationLinkProps = {
FILE: project/web/src/components/ui/toast.tsx
type ToastProps (line 115) | type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
type ToastActionElement (line 117) | type ToastActionElement = React.ReactElement<typeof ToastAction>
FILE: project/web/src/components/ui/toaster.tsx
function Toaster (line 13) | function Toaster() {
FILE: project/web/src/components/ui/use-toast.ts
constant TOAST_LIMIT (line 11) | const TOAST_LIMIT = 1
constant TOAST_REMOVE_DELAY (line 12) | const TOAST_REMOVE_DELAY = 1000000
type ToasterToast (line 14) | type ToasterToast = ToastProps & {
function genId (line 30) | function genId() {
type ActionType (line 35) | type ActionType = typeof actionTypes
type Action (line 37) | type Action =
type State (line 55) | interface State {
function dispatch (line 136) | function dispatch(action: Action) {
type Toast (line 143) | type Toast = Omit<ToasterToast, "id">
function toast (line 145) | function toast({ ...props }: Toast) {
function useToast (line 174) | function useToast() {
FILE: project/web/src/lib/utils.ts
function cn (line 4) | function cn(...inputs: ClassValue[]) {
FILE: project/web/src/models/types.ts
type Question (line 2) | interface Question {
type CustomContent (line 17) | interface CustomContent {
FILE: web/src/App.tsx
type CredentialMode (line 34) | type CredentialMode = 'auth' | 'api-key' | 'opencode-config';
constant OPENCODE_RECOMMENDED_MODEL_VALUE (line 35) | const OPENCODE_RECOMMENDED_MODEL_VALUE = '__opencode_recommended__';
constant MAX_INSTRUCTION_ATTACHMENT_BYTES (line 36) | const MAX_INSTRUCTION_ATTACHMENT_BYTES = 40 * 1024 * 1024;
constant PROJECT_HISTORY_STORAGE_KEY (line 37) | const PROJECT_HISTORY_STORAGE_KEY = 'glowby_oss_project_history';
constant DEFAULT_BUILD_INSTRUCTIONS (line 38) | const DEFAULT_BUILD_INSTRUCTIONS = 'Make this project production ready. ...
constant OPENCODE_MODEL_PREFERENCE_ORDER (line 39) | const OPENCODE_MODEL_PREFERENCE_ORDER = [
constant RUN_STATUS_LABEL (line 46) | const RUN_STATUS_LABEL: Record<string, string> = {
constant DEFAULT_PROVIDER_KEYS (line 54) | const DEFAULT_PROVIDER_KEYS: ProviderKeyState = {
constant PROVIDER_KEYS_STORAGE_KEY (line 65) | const PROVIDER_KEYS_STORAGE_KEY = 'glowby_oss_provider_keys';
function loadProviderKeys (line 67) | function loadProviderKeys(): ProviderKeyState {
function saveProviderKeys (line 80) | function saveProviderKeys(keys: ProviderKeyState): void {
constant IMAGE_SOURCE_STORAGE_KEY (line 88) | const IMAGE_SOURCE_STORAGE_KEY = 'glowby_oss_image_source';
function loadImageSource (line 90) | function loadImageSource(): string {
function saveImageSource (line 98) | function saveImageSource(value: string): void {
constant CREDENTIAL_MODE_STORAGE_KEY (line 106) | const CREDENTIAL_MODE_STORAGE_KEY = 'glowby_oss_credential_mode';
constant VALID_CREDENTIAL_MODES (line 107) | const VALID_CREDENTIAL_MODES: CredentialMode[] = ['auth', 'api-key', 'op...
function loadCredentialMode (line 109) | function loadCredentialMode(): CredentialMode {
function saveCredentialMode (line 121) | function saveCredentialMode(mode: CredentialMode): void {
constant TARGET_SELECTION_STORAGE_KEY (line 129) | const TARGET_SELECTION_STORAGE_KEY = 'glowby_oss_selected_targets';
constant BUILD_TARGETS (line 131) | const BUILD_TARGETS = [
constant ALL_TARGET_IDS (line 138) | const ALL_TARGET_IDS: string[] = BUILD_TARGETS.map((t) => t.id);
function loadSelectedTargets (line 140) | function loadSelectedTargets(): string[] {
function saveSelectedTargets (line 156) | function saveSelectedTargets(ids: string[]): void {
function validateImageSource (line 164) | function validateImageSource(source: string, keys: ProviderKeyState): st...
function suggestBundleID (line 172) | function suggestBundleID(name: string): string {
type ProjectHistoryEntry (line 185) | interface ProjectHistoryEntry {
type PreferredOpenCodeModel (line 192) | interface PreferredOpenCodeModel {
type HistoryViewEntry (line 199) | interface HistoryViewEntry extends Omit<OpenCodeProjectHistoryEntry, 'at...
function IDEQuickActionIcon (line 204) | function IDEQuickActionIcon({ ide }: { ide: OpenCodeIDE }) {
function isMultiSelect (line 266) | function isMultiSelect(prompt: string, inputType?: string): boolean {
function toggleSelection (line 283) | function toggleSelection(current: string[], value: string, multiSelect: ...
function targetSummary (line 295) | function targetSummary(project: OpenCodeProject | null): Array<{ id: str...
function formatCredentialType (line 307) | function formatCredentialType(value?: string): string {
function formatAuthMode (line 320) | function formatAuthMode(value?: string): string {
function formatAttachmentSize (line 333) | function formatAttachmentSize(sizeBytes: number): string {
function compactPathLabel (line 354) | function compactPathLabel(path: string): string {
function compactHistoryText (line 364) | function compactHistoryText(value: string, maxLength = 180, fallback = '...
function formatHistoryTimestamp (line 375) | function formatHistoryTimestamp(value: string): string {
function inferHistoryAttachmentMediaType (line 389) | function inferHistoryAttachmentMediaType(mimeType?: string, fileName?: s...
function normalizeHistoryEntries (line 412) | function normalizeHistoryEntries(entries: OpenCodeProjectHistoryEntry[])...
function historyAttachmentCountLabel (line 424) | function historyAttachmentCountLabel(count: number): string {
function normalizeProjectHistory (line 428) | function normalizeProjectHistory(raw: unknown): ProjectHistoryEntry[] {
function upsertProjectHistory (line 463) | function upsertProjectHistory(previous: ProjectHistoryEntry[], entry: Pr...
function normalizeOpenCodeLookupValue (line 467) | function normalizeOpenCodeLookupValue(value: string): string {
function formatOpenCodeModelSelection (line 471) | function formatOpenCodeModelSelection(
function preferredOpenCodeModel (line 486) | function preferredOpenCodeModel(providers: OpenCodeAvailableProvider[]):...
function inferProviderFromCustomModel (line 515) | function inferProviderFromCustomModel(customModel: string): ProviderID |...
function defaultModelOptionValue (line 530) | function defaultModelOptionValue(dynamicOpenAIModels: OpenAIModelsRespon...
function ideActionFor (line 543) | function ideActionFor(actions: OpenCodeProjectIDEAction[], ide: OpenCode...
function isUnsupportedNativePickerError (line 547) | function isUnsupportedNativePickerError(error: unknown): boolean {
function App (line 555) | function App() {
FILE: web/src/hooks/useRefineRun.ts
type RefineRunStatus (line 17) | type RefineRunStatus = 'idle' | 'running' | 'completed' | 'failed' | 'ca...
type StartRefineInput (line 19) | interface StartRefineInput {
type SubmitQuestionInput (line 32) | interface SubmitQuestionInput {
function toStringValue (line 38) | function toStringValue(value: unknown): string {
function extractSessionIDFromText (line 42) | function extractSessionIDFromText(text: string): string {
function normalizeIncomingOutputText (line 47) | function normalizeIncomingOutputText(text: string): string {
function firstRegexInt (line 64) | function firstRegexInt(text: string, regex: RegExp): number | undefined {
function usageLimitWindowMinutes (line 73) | function usageLimitWindowMinutes(text: string): number | undefined {
function usageLimitUsedPercent (line 77) | function usageLimitUsedPercent(text: string): number | undefined {
function usageLimitResetDate (line 81) | function usageLimitResetDate(text: string): Date | undefined {
function formatQuotaWindow (line 98) | function formatQuotaWindow(minutes: number): string {
function relativeDurationString (line 111) | function relativeDurationString(untilDate: Date): string | undefined {
function usageLimitFriendlyMessage (line 141) | function usageLimitFriendlyMessage(text: string): string | undefined {
function normalizedAgentOutputLine (line 189) | function normalizedAgentOutputLine(line: string): string {
function userFacingAgentErrorMessage (line 197) | function userFacingAgentErrorMessage(raw: string): string {
function isAlphaNumericCharacter (line 215) | function isAlphaNumericCharacter(character: string): boolean {
function isWhitespaceCharacter (line 219) | function isWhitespaceCharacter(character: string): boolean {
function inferredChunkSeparator (line 223) | function inferredChunkSeparator(currentLine: string, nextPart: string): ...
function parseQuestionOptions (line 242) | function parseQuestionOptions(raw: unknown): OpenCodeQuestionOption[] {
function normalizeQuestion (line 268) | function normalizeQuestion(raw: unknown, fallbackSessionID: string): Ope...
function normalizePermission (line 313) | function normalizePermission(raw: unknown, fallbackSessionID: string): O...
function isAbortError (line 335) | function isAbortError(error: unknown): boolean {
function sanitizeChangedFiles (line 339) | function sanitizeChangedFiles(files: unknown): string[] {
function trimmedValue (line 347) | function trimmedValue(value: string | undefined): string {
function useRefineRun (line 351) | function useRefineRun() {
FILE: web/src/lib/api.ts
constant API_PREFIX (line 30) | const API_PREFIX = '/api';
class ApiError (line 32) | class ApiError extends Error {
method constructor (line 35) | constructor(message: string, status: number) {
function parseResponseError (line 42) | async function parseResponseError(response: Response): Promise<string> {
function requestJson (line 56) | async function requestJson<T>(input: RequestInfo | URL, init?: RequestIn...
method getHealth (line 68) | async getHealth(): Promise<OpenCodeHealthResponse> {
method getAuthStatus (line 72) | async getAuthStatus(): Promise<OpenCodeAuthStatus> {
method connectOpenAIAuth (line 76) | async connectOpenAIAuth(payload: OpenCodeAuthConnectRequest): Promise<Op...
method disconnectOpenAIAuth (line 84) | async disconnectOpenAIAuth(payload: OpenCodeAuthDisconnectRequest): Prom...
method startOpenAIOAuth (line 92) | async startOpenAIOAuth(payload: OpenCodeAuthOAuthStartRequest): Promise<...
method getOpenAIOAuthStatus (line 100) | async getOpenAIOAuthStatus(state: string): Promise<OpenCodeAuthOAuthStat...
method getProject (line 105) | async getProject(projectPath: string): Promise<OpenCodeProjectEnvelope> {
method getProjectHistory (line 110) | async getProjectHistory(projectPath: string): Promise<OpenCodeProjectHis...
method getProjectIDEStatus (line 115) | async getProjectIDEStatus(projectPath: string): Promise<OpenCodeProjectI...
method pickProjectFolder (line 120) | async pickProjectFolder(): Promise<OpenCodeProjectPickResponse> {
method pickInstructionFiles (line 126) | async pickInstructionFiles(): Promise<OpenCodeInstructionFilesPickRespon...
method renameProject (line 132) | async renameProject(path: string, name: string): Promise<{ success: bool...
method updateProjectSettings (line 140) | async updateProjectSettings(payload: OpenCodeProjectSettingsRequest): Pr...
method generateIcon (line 148) | async generateIcon(payload: OpenCodeGenerateIconRequest): Promise<OpenCo...
method getProjectIcon (line 156) | async getProjectIcon(path: string): Promise<{ success: boolean; exists: ...
method openProject (line 160) | async openProject(payload: OpenCodeProjectOpenRequest): Promise<OpenCode...
method fetchOpenAIModels (line 168) | async fetchOpenAIModels(payload: OpenAIModelsRequest): Promise<OpenAIMod...
method fetchOpenCodeAvailableModels (line 176) | async fetchOpenCodeAvailableModels(payload: OpenCodeAvailableModelsReque...
method respondToQuestion (line 184) | async respondToQuestion(payload: OpenCodeQuestionRespondRequest): Promis...
method respondToPermission (line 192) | async respondToPermission(payload: OpenCodePermissionRespondRequest): Pr...
function toErrorMessage (line 201) | function toErrorMessage(error: unknown, fallback: string): string {
FILE: web/src/lib/console-render.ts
function lineLooksLikeMarkdown (line 28) | function lineLooksLikeMarkdown(line: string): boolean {
function parseConsoleHeading (line 39) | function parseConsoleHeading(line: string): { level: number; text: strin...
function parseConsoleMarkdown (line 66) | function parseConsoleMarkdown(line: string): string | null {
FILE: web/src/lib/model-catalog.ts
constant OPTION_SEPARATOR (line 10) | const OPTION_SEPARATOR = '::';
constant MODEL_PROVIDERS (line 12) | const MODEL_PROVIDERS: ModelCatalogProvider[] = [
constant STATIC_MODEL_CATALOG (line 69) | const STATIC_MODEL_CATALOG: Record<ProviderID, ModelCatalogEntry[]> = {
function dedupeModels (line 226) | function dedupeModels(models: ModelCatalogEntry[]): ModelCatalogEntry[] {
function staticModelCatalogFor (line 242) | function staticModelCatalogFor(providerId: ProviderID): ModelCatalogEntr...
function modelCatalogGroups (line 246) | function modelCatalogGroups(dynamicOpenAIModels: OpenAIModelsResponseMod...
function providerDefinition (line 269) | function providerDefinition(providerId: ProviderID): ModelCatalogProvide...
function encodeModelOptionValue (line 273) | function encodeModelOptionValue(providerId: ProviderID, modelId: string)...
function decodeModelOptionValue (line 277) | function decodeModelOptionValue(value: string): { providerId: ProviderID...
function resolveRefineModel (line 302) | function resolveRefineModel(providerId: ProviderID, modelId: string): Re...
FILE: web/src/lib/server-auth.ts
constant SESSION_STORAGE_KEY (line 1) | const SESSION_STORAGE_KEY = 'glowby.serverToken';
function browserTokenFromURL (line 3) | function browserTokenFromURL(): string {
function resolveServerToken (line 25) | function resolveServerToken(): string {
function withServerAuthHeaders (line 49) | function withServerAuthHeaders(headers?: HeadersInit): Headers {
FILE: web/src/lib/sse.ts
type StreamJsonSseOptions (line 3) | interface StreamJsonSseOptions<TRequest, TEvent extends Record<string, u...
function parseErrorMessage (line 10) | function parseErrorMessage(response: Response, bodyText: string): string {
function parseDataPayload (line 23) | function parseDataPayload(frame: string): string {
function streamJsonSse (line 36) | async function streamJsonSse<TRequest extends object, TEvent extends Rec...
FILE: web/src/types/opencode.ts
type OpenAIAuthMode (line 1) | type OpenAIAuthMode = 'api-key' | 'codex-jwt' | 'opencode-config';
type AuthProviderID (line 2) | type AuthProviderID = 'chatgpt';
type ProviderID (line 4) | type ProviderID =
type ProviderKeyField (line 14) | type ProviderKeyField =
type ProviderKeyState (line 24) | interface ProviderKeyState {
type OpenCodeHealthResponse (line 35) | interface OpenCodeHealthResponse {
type OpenCodeAuthStatus (line 42) | interface OpenCodeAuthStatus {
type OpenCodeAuthConnectRequest (line 51) | interface OpenCodeAuthConnectRequest {
type OpenCodeAuthDisconnectRequest (line 58) | interface OpenCodeAuthDisconnectRequest {
type OpenCodeAuthConnectionResponse (line 62) | interface OpenCodeAuthConnectionResponse {
type OpenCodeAuthOAuthStartRequest (line 70) | interface OpenCodeAuthOAuthStartRequest {
type OpenCodeAuthOAuthStartResponse (line 74) | interface OpenCodeAuthOAuthStartResponse {
type OpenCodeAuthOAuthStatusResponse (line 83) | interface OpenCodeAuthOAuthStatusResponse {
type GlowbomTarget (line 93) | interface GlowbomTarget {
type OpenCodeProject (line 102) | interface OpenCodeProject {
type OpenCodeProjectSettingsRequest (line 114) | interface OpenCodeProjectSettingsRequest {
type OpenCodeProjectSettingsResponse (line 122) | interface OpenCodeProjectSettingsResponse {
type OpenCodeGenerateIconRequest (line 128) | interface OpenCodeGenerateIconRequest {
type OpenCodeGenerateIconResponse (line 138) | interface OpenCodeGenerateIconResponse {
type OpenCodeProjectEnvelope (line 146) | interface OpenCodeProjectEnvelope {
type OpenCodeProjectPickResponse (line 154) | interface OpenCodeProjectPickResponse {
type OpenCodeInstructionPickedFile (line 162) | interface OpenCodeInstructionPickedFile {
type OpenCodeHistoryStatus (line 169) | type OpenCodeHistoryStatus = 'running' | 'completed' | 'failed' | 'cance...
type OpenCodeProjectHistoryAttachment (line 171) | interface OpenCodeProjectHistoryAttachment extends OpenCodeInstructionPi...
type OpenCodeProjectHistoryEntry (line 177) | interface OpenCodeProjectHistoryEntry {
type OpenCodeProjectHistoryResponse (line 189) | interface OpenCodeProjectHistoryResponse {
type OpenCodeInstructionFilesPickResponse (line 196) | interface OpenCodeInstructionFilesPickResponse {
type OpenCodeIDE (line 204) | type OpenCodeIDE = 'finder' | 'xcode' | 'android-studio' | 'vscode';
type OpenCodeProjectIDEAction (line 206) | interface OpenCodeProjectIDEAction {
type OpenCodeProjectIDEStatusResponse (line 214) | interface OpenCodeProjectIDEStatusResponse {
type OpenCodeProjectOpenRequest (line 221) | interface OpenCodeProjectOpenRequest {
type OpenCodeProjectOpenResponse (line 226) | interface OpenCodeProjectOpenResponse {
type OpenCodeAgentRequest (line 233) | interface OpenCodeAgentRequest {
type OpenCodeQuestionOption (line 254) | interface OpenCodeQuestionOption {
type OpenCodeQuestionItem (line 260) | interface OpenCodeQuestionItem {
type OpenCodeQuestion (line 267) | interface OpenCodeQuestion {
type OpenCodePermission (line 275) | interface OpenCodePermission {
type OpenCodeQuestionRespondRequest (line 284) | interface OpenCodeQuestionRespondRequest {
type OpenCodePermissionRespondRequest (line 293) | interface OpenCodePermissionRespondRequest {
type OpenCodeSseEvent (line 300) | interface OpenCodeSseEvent {
type OpenAIModelsRequest (line 313) | interface OpenAIModelsRequest {
type OpenAIModelsResponseModel (line 321) | interface OpenAIModelsResponseModel {
type OpenAIModelsResponseDebug (line 326) | interface OpenAIModelsResponseDebug {
type OpenAIModelsResponse (line 336) | interface OpenAIModelsResponse {
type OpenCodeAvailableModelsRequest (line 344) | interface OpenCodeAvailableModelsRequest {
type OpenCodeAvailableProviderModel (line 358) | interface OpenCodeAvailableProviderModel {
type OpenCodeAvailableProvider (line 363) | interface OpenCodeAvailableProvider {
type OpenCodeAvailableModelsResponse (line 369) | interface OpenCodeAvailableModelsResponse {
type ModelCatalogProvider (line 375) | interface ModelCatalogProvider {
type ModelCatalogEntry (line 383) | interface ModelCatalogEntry {
type ModelCatalogGroup (line 391) | interface ModelCatalogGroup {
type ResolvedRefineModel (line 396) | interface ResolvedRefineModel {
Condensed preview — 339 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (8,230K chars).
[
{
"path": ".claude/settings.json",
"chars": 73,
"preview": "{\n \"permissions\": {\n \"allow\": [\n \"Bash(go build:*)\"\n ]\n }\n}\n"
},
{
"path": ".github/workflows/glowby-release.yml",
"chars": 2205,
"preview": "name: Release glowby\n\non:\n push:\n tags:\n - 'v*'\n\npermissions:\n contents: write\n\njobs:\n build:\n runs-on: ub"
},
{
"path": ".gitignore",
"chars": 1398,
"preview": "# See https://www.dartlang.org/guides/libraries/private-files\n\n# Files and directories created by pub\n.dart_tool/\n.packa"
},
{
"path": "AGENTS.md",
"chars": 165,
"preview": "# Workspace Instructions\n\n## Commit Messages\n- After making code changes, always suggest a one-line conventional commit "
},
{
"path": "CLI.md",
"chars": 2786,
"preview": "# glowby CLI\n\nTerminal-first CLI for Glowby OSS. Starts the Go backend and web UI, opens the browser, and manages the fu"
},
{
"path": "LICENSE",
"chars": 1070,
"preview": "MIT License\n\nCopyright (c) 2023 Glowbom, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a c"
},
{
"path": "README.md",
"chars": 4916,
"preview": "# Glowby OSS\n\n> We just launched Glowby OSS! [See the announcement](https://x.com/jacobilin/status/2035059308463833292)\n"
},
{
"path": "backend/backend_info_page.go",
"chars": 13377,
"preview": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"html\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\nconst (\n\tglowbyR"
},
{
"path": "backend/claude_anthropic.go",
"chars": 23664,
"preview": "package main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n)\n\nconst c"
},
{
"path": "backend/codex_chatgpt.go",
"chars": 13605,
"preview": "package main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"net/http\"\n\t\"runtime\"\n\t\"strings\"\n\t\"time\""
},
{
"path": "backend/elevenlabs_audio.go",
"chars": 14137,
"preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\tneturl \"net/url\"\n\t\""
},
{
"path": "backend/fireworks_fireworks.go",
"chars": 39672,
"preview": "package main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"regexp\"\n\t\"strings\"\n\t\"time\"\n)\n\nconst"
},
{
"path": "backend/gemini_google.go",
"chars": 27114,
"preview": "package main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc is"
},
{
"path": "backend/gemini_image.go",
"chars": 7597,
"preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nconst nanoBan"
},
{
"path": "backend/gemini_text_with_attachment.go",
"chars": 2768,
"preview": "package main\n\n// callGeminiApiGoWithAttachment builds a Gemini chat request that includes inline_data attachment.\nfunc c"
},
{
"path": "backend/gemini_veo.go",
"chars": 10967,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"strings\"\n\n\t\"google.golang.org/gena"
},
{
"path": "backend/gemini_video.go",
"chars": 4627,
"preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n)\n\n// callGeminiVideoAnalys"
},
{
"path": "backend/go.mod",
"chars": 1152,
"preview": "module glowbom-backend\n\ngo 1.24\n\ntoolchain go1.24.11\n\nrequire (\n\tcloud.google.com/go v0.116.0 // indirect\n\tcloud.google."
},
{
"path": "backend/go.sum",
"chars": 13094,
"preview": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.116.0 h1:B3fRr"
},
{
"path": "backend/gpt5_openai.go",
"chars": 15905,
"preview": "package main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n)\n\n// call"
},
{
"path": "backend/gpt5_responses_api.go",
"chars": 10801,
"preview": "package main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n)\n\n// call"
},
{
"path": "backend/grok_image.go",
"chars": 6378,
"preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"mime\"\n\t\"net/http\"\n\tneturl \"net/url\"\n\t"
},
{
"path": "backend/grok_video.go",
"chars": 17374,
"preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\tneturl \"net/url\"\n\t\"strings\"\n)\n\nconst (\n\txAIVi"
},
{
"path": "backend/grok_xai.go",
"chars": 17652,
"preview": "package main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode/"
},
{
"path": "backend/handlers.go",
"chars": 50215,
"preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"re"
},
{
"path": "backend/main.go",
"chars": 3335,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n)\n\nfunc main() {\n\tmux := http.NewServeMux()\n\n\tmux.HandleFunc(\"/he"
},
{
"path": "backend/model_helpers.go",
"chars": 5248,
"preview": "package main\n\nimport \"strings\"\n\nconst defaultOpenAIModelID = \"gpt-5.4\"\n\ntype openAITextPricing struct {\n\tInputPerMillion"
},
{
"path": "backend/o3_openai.go",
"chars": 6946,
"preview": "package main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nfunc callo3miniOpenAiDr"
},
{
"path": "backend/openai_image.go",
"chars": 7932,
"preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"mime/multipart\"\n\t\"net/http\"\n\t\"net/tex"
},
{
"path": "backend/opencode_driver.go",
"chars": 253924,
"preview": "package main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/rand\"\n\t\"crypto/sha256\"\n\t\"encoding/base64\"\n\t\"encoding/hex\"\n\t"
},
{
"path": "backend/opencode_folder_picker.go",
"chars": 13273,
"preview": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"mime\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"str"
},
{
"path": "backend/opencode_media_postpass.go",
"chars": 56224,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"crypto/sha1\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\tne"
},
{
"path": "backend/opencode_project_history.go",
"chars": 7267,
"preview": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n)\n\ntype ope"
},
{
"path": "backend/opencode_project_open.go",
"chars": 19398,
"preview": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"stri"
},
{
"path": "backend/opencode_zen.go",
"chars": 39290,
"preview": "package main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n)\n\nconst"
},
{
"path": "backend/openrouter_openrouter.go",
"chars": 32915,
"preview": "package main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n)\n\nconst openRoute"
},
{
"path": "backend/prompts.go",
"chars": 17167,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc isGlowbyImagesSource(imageSource string) bool {\n\ttrimmed := strings.Tri"
},
{
"path": "backend/r1_groq.go",
"chars": 15046,
"preview": "package main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc ca"
},
{
"path": "backend/r1_ollama.go",
"chars": 23533,
"preview": "package main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc no"
},
{
"path": "backend/search.go",
"chars": 12218,
"preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n)\n\ntype openAIRe"
},
{
"path": "backend/security.go",
"chars": 4172,
"preview": "package main\n\nimport (\n\t\"crypto/subtle\"\n\t\"encoding/json\"\n\t\"net\"\n\t\"net/http\"\n\tneturl \"net/url\"\n\t\"os\"\n\t\"strings\"\n)\n\ntype g"
},
{
"path": "backend/stack_specs.go",
"chars": 4285,
"preview": "package main\n\n// Stack-specific instruction templates shared across translation prompts.\n\nvar baseStackInstructions = ma"
},
{
"path": "backend/types.go",
"chars": 3734,
"preview": "package main\n\n// ====================================================================\n// Data structures for the backend"
},
{
"path": "cli/code.go",
"chars": 17735,
"preview": "package main\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"crypto/rand\"\n\t\"crypto/sha1\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/ht"
},
{
"path": "cli/doctor.go",
"chars": 1609,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"os/exec\"\n\t\"strings\"\n)\n\ntype depCheck struct {\n\tname string\n\trequired bool\n\tfixHint "
},
{
"path": "cli/go.mod",
"chars": 44,
"preview": "module glowby\n\ngo 1.24\n\ntoolchain go1.24.11\n"
},
{
"path": "cli/main.go",
"chars": 1169,
"preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n)\n\nvar (\n\tversion = \"dev\"\n\tcommit = \"unknown\"\n\tdate = \"unknown\"\n)\n\nconst usage ="
},
{
"path": "cli/version.go",
"chars": 138,
"preview": "package main\n\nimport \"fmt\"\n\nfunc runVersion() int {\n\tfmt.Printf(\"glowby %s (commit: %s, built: %s)\\n\", version, commit, "
},
{
"path": "docs/.dockerignore",
"chars": 42,
"preview": ".react-router\nbuild\nnode_modules\nREADME.md"
},
{
"path": "docs/.gitignore",
"chars": 70,
"preview": ".DS_Store\n.env\n/node_modules/\n\n# React Router\n/.react-router/\n/build/\n"
},
{
"path": "docs/Dockerfile",
"chars": 599,
"preview": "FROM node:20-alpine AS development-dependencies-env\nCOPY . /app\nWORKDIR /app\nRUN npm ci\n\nFROM node:20-alpine AS producti"
},
{
"path": "docs/README.md",
"chars": 2060,
"preview": "# Glowby OSS\n\nGlowby helps you build production-ready software with coding agents. It is an open source coding agent wor"
},
{
"path": "docs/app/app.css",
"chars": 2309,
"preview": "@import \"tailwindcss\";\n@import \"fumadocs-ui/style.css\";\n\n@theme {\n --font-sans: \"Manrope\", ui-sans-serif, system-ui, sa"
},
{
"path": "docs/app/components/brand-title.tsx",
"chars": 468,
"preview": "const logoSrc = `${import.meta.env.BASE_URL}glowbom-logo.svg`;\n\nexport function BrandTitle() {\n return (\n <span\n "
},
{
"path": "docs/app/components/docs-route-view.tsx",
"chars": 1796,
"preview": "import { Suspense } from \"react\";\nimport { deserializePageTree } from \"fumadocs-core/source/client\";\nimport { DocsLayout"
},
{
"path": "docs/app/components/docs-search.tsx",
"chars": 12020,
"preview": "import { buttonVariants } from \"fumadocs-ui/components/ui/button\";\nimport { Search, X } from \"lucide-react\";\nimport {\n "
},
{
"path": "docs/app/components/nav-actions.tsx",
"chars": 1058,
"preview": "import { Github } from \"lucide-react\";\nimport { DISCORD_URL, GITHUB_URL } from \"../lib/site\";\n\nconst discordIconSrc = `$"
},
{
"path": "docs/app/lib/docs.ts",
"chars": 427,
"preview": "import type { SerializedPageTree } from \"fumadocs-core/source/client\";\n\nexport interface SearchRecord {\n id: string;\n "
},
{
"path": "docs/app/lib/meta.ts",
"chars": 512,
"preview": "import type { LoadedDocPage } from \"./docs\";\n\nexport function buildDocMeta(data?: LoadedDocPage) {\n if (!data) {\n re"
},
{
"path": "docs/app/lib/site.ts",
"chars": 131,
"preview": "export const GITHUB_URL = \"https://github.com/glowbom/glowby\";\nexport const DISCORD_URL = \"https://discord.com/invite/jp"
},
{
"path": "docs/app/lib/source.browser.tsx",
"chars": 591,
"preview": "import type { ComponentType, ReactElement } from \"react\";\nimport browserCollections from \"fumadocs-mdx:collections/brows"
},
{
"path": "docs/app/lib/source.server.ts",
"chars": 3588,
"preview": "import { loader } from \"fumadocs-core/source\";\nimport { docs } from \"fumadocs-mdx:collections/server\";\nimport type { Loa"
},
{
"path": "docs/app/root.tsx",
"chars": 2460,
"preview": "import {\n isRouteErrorResponse,\n Links,\n Meta,\n Outlet,\n Scripts,\n ScrollRestoration,\n} from \"react-router\";\nimpor"
},
{
"path": "docs/app/routes/docs-page.tsx",
"chars": 730,
"preview": "import type { Route } from \"./+types/docs-page\";\nimport { DocsRouteView } from \"../components/docs-route-view\";\nimport {"
},
{
"path": "docs/app/routes/home.tsx",
"chars": 468,
"preview": "import type { Route } from \"./+types/home\";\nimport { DocsRouteView } from \"../components/docs-route-view\";\nimport { buil"
},
{
"path": "docs/app/routes.ts",
"chars": 184,
"preview": "import { type RouteConfig, index, route } from \"@react-router/dev/routes\";\n\nexport default [\n index(\"routes/home.tsx\"),"
},
{
"path": "docs/content/docs/desktop.mdx",
"chars": 1007,
"preview": "---\ntitle: Glowbom Desktop\ndescription: The native macOS app for building software with AI, offline and on your terms.\n-"
},
{
"path": "docs/content/docs/glowbom.mdx",
"chars": 1943,
"preview": "---\ntitle: Glowbom\ndescription: A sketch-to-software system for building native apps from drawings.\n---\n\nGlowbom turns d"
},
{
"path": "docs/content/docs/glowby-oss.mdx",
"chars": 3599,
"preview": "---\ntitle: Glowby OSS\ndescription: Make your Glowbom project production-ready with AI coding agents, locally.\n---\n\nGlowb"
},
{
"path": "docs/content/docs/index.mdx",
"chars": 1434,
"preview": "---\ntitle: Glowbom Docs\ndescription: One sketch. Real native apps on every platform.\n---\n\n## What is Glowbom?\n\nGlowbom i"
},
{
"path": "docs/content/docs/meta.json",
"chars": 118,
"preview": "{\n \"title\": \"Glowbom Docs\",\n \"root\": true,\n \"pages\": [\"index\", \"glowbom\", \"quickstart\", \"glowby-oss\", \"desktop\"]\n}\n"
},
{
"path": "docs/content/docs/quickstart.mdx",
"chars": 1637,
"preview": "---\ntitle: Quickstart\ndescription: Get started with Glowbom in minutes.\n---\n\n## Glowbom Web\n\n### Step 1. Open the canvas"
},
{
"path": "docs/package.json",
"chars": 1121,
"preview": "{\n \"name\": \"docs\",\n \"private\": true,\n \"type\": \"module\",\n \"scripts\": {\n \"build\": \"fumadocs-mdx && react-router bui"
},
{
"path": "docs/react-router.config.ts",
"chars": 344,
"preview": "import type { Config } from \"@react-router/dev/config\";\n\nconst basename = process.env.DOCS_BASE_PATH ?? (process.env.NOD"
},
{
"path": "docs/source.config.ts",
"chars": 157,
"preview": "import { defineConfig, defineDocs } from \"fumadocs-mdx/config\";\n\nexport const docs = defineDocs({\n dir: \"content/docs\","
},
{
"path": "docs/test-browser.js",
"chars": 755,
"preview": "const { chromium } = require('playwright');\n(async () => {\n const browser = await chromium.launch();\n const context = "
},
{
"path": "docs/test-search.js",
"chars": 102,
"preview": "import { source } from \"./app/lib/source.server.ts\";\nconsole.log(\"Pages:\", source.getPages().length);\n"
},
{
"path": "docs/test.ts",
"chars": 102,
"preview": "import { source } from \"./app/lib/source.server.ts\";\nconsole.log(\"Pages:\", source.getPages().length);\n"
},
{
"path": "docs/tsconfig.json",
"chars": 658,
"preview": "{\n \"include\": [\n \"**/*\",\n \"**/.server/**/*\",\n \"**/.client/**/*\",\n \".react-router/types/**/*\"\n ],\n \"compil"
},
{
"path": "docs/vite.config.ts",
"chars": 550,
"preview": "import { reactRouter } from \"@react-router/dev/vite\";\nimport tailwindcss from \"@tailwindcss/vite\";\nimport mdx from \"fuma"
},
{
"path": "legacy/GlowbyGenius.md",
"chars": 1722,
"preview": "# Glowby Genius\n\nAI software engineer for Vision Pro that works without internet. It's available on the [**Apple App Sto"
},
{
"path": "legacy/README.md",
"chars": 14379,
"preview": "## Why This Moved to Legacy\n\nThis repo reflects an earlier version of Glowby. We are moving it into legacy/ as we focus "
},
{
"path": "legacy/app/.gitignore",
"chars": 655,
"preview": "# Miscellaneous\n*.class\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.buildlog/\n.history\n.svn/\n\n# IntelliJ related\n*.iml\n*.ipr\n*.i"
},
{
"path": "legacy/app/.metadata",
"chars": 1264,
"preview": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrade"
},
{
"path": "legacy/app/.vscode/launch.json",
"chars": 369,
"preview": "{\n // Use IntelliSense to learn about possible attributes.\n // Hover to view descriptions of existing attributes.\n"
},
{
"path": "legacy/app/README.md",
"chars": 533,
"preview": "# pwa\n\nA new Flutter project.\n\n## Getting Started\n\nThis project is a starting point for a Flutter application.\n\nA few re"
},
{
"path": "legacy/app/analysis_options.yaml",
"chars": 1420,
"preview": "# This file configures the analyzer, which statically analyzes Dart code to\n# check for errors, warnings, and lints.\n#\n#"
},
{
"path": "legacy/app/android/.gitignore",
"chars": 110,
"preview": "gradle-wrapper.jar\n/.gradle\n/captures/\n/gradlew\n/gradlew.bat\n/local.properties\nGeneratedPluginRegistrant.java\n"
},
{
"path": "legacy/app/android/app/build.gradle",
"chars": 2120,
"preview": "def localProperties = new Properties()\ndef localPropertiesFile = rootProject.file('local.properties')\nif (localPropertie"
},
{
"path": "legacy/app/android/app/src/debug/AndroidManifest.xml",
"chars": 323,
"preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"com.example.pwa\">\n <!-- Flutter nee"
},
{
"path": "legacy/app/android/app/src/main/AndroidManifest.xml",
"chars": 2592,
"preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"com.example.pwa\">\n <!-- io.flutter."
},
{
"path": "legacy/app/android/app/src/main/kotlin/com/example/pwa/MainActivity.kt",
"chars": 120,
"preview": "package com.example.pwa\n\nimport io.flutter.embedding.android.FlutterActivity\n\nclass MainActivity: FlutterActivity() {\n}\n"
},
{
"path": "legacy/app/android/app/src/main/res/drawable/launch_background.xml",
"chars": 434,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Modify this file to customize your launch splash screen -->\n<layer-list xmln"
},
{
"path": "legacy/app/android/app/src/main/res/values/styles.xml",
"chars": 951,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <!-- Theme applied to the Android Window while the process is sta"
},
{
"path": "legacy/app/android/app/src/profile/AndroidManifest.xml",
"chars": 323,
"preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"com.example.pwa\">\n <!-- Flutter nee"
},
{
"path": "legacy/app/android/build.gradle",
"chars": 582,
"preview": "buildscript {\n ext.kotlin_version = '1.3.50'\n repositories {\n google()\n jcenter()\n }\n\n depende"
},
{
"path": "legacy/app/android/gradle/wrapper/gradle-wrapper.properties",
"chars": 233,
"preview": "#Fri Jun 23 08:50:38 CEST 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER"
},
{
"path": "legacy/app/android/gradle.properties",
"chars": 104,
"preview": "org.gradle.jvmargs=-Xmx1536M\nandroid.enableR8=true\nandroid.useAndroidX=true\nandroid.enableJetifier=true\n"
},
{
"path": "legacy/app/android/settings.gradle",
"chars": 484,
"preview": "include ':app'\n\ndef flutterProjectRoot = rootProject.projectDir.parentFile.toPath()\n\ndef plugins = new Properties()\ndef "
},
{
"path": "legacy/app/assets/talk.glowbom",
"chars": 1257,
"preview": "{\"title\":\"Glowby\",\"main_color\":\"Black\",\"conclusion\":\"Please send us your information and we'll come back to you shortly!"
},
{
"path": "legacy/app/assets/test.html",
"chars": 21409,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <title>My Simple HTML File</title>\n </head>\n <body>\n <h1>Hello, world!</h1>\n "
},
{
"path": "legacy/app/assets/website.html",
"chars": 3190,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "legacy/app/ios/.gitignore",
"chars": 542,
"preview": "*.mode1v3\n*.mode2v3\n*.moved-aside\n*.pbxuser\n*.perspectivev3\n**/*sync/\n.sconsign.dblite\n.tags*\n**/.vagrant/\n**/DerivedDat"
},
{
"path": "legacy/app/ios/Flutter/.last_build_id",
"chars": 32,
"preview": "9d7ae77f231555adfd08a323be129284"
},
{
"path": "legacy/app/ios/Flutter/AppFrameworkInfo.plist",
"chars": 775,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "legacy/app/ios/Flutter/Debug.xcconfig",
"chars": 106,
"preview": "#include \"Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"\n#include \"Generated.xcconfig\"\n"
},
{
"path": "legacy/app/ios/Flutter/Release.xcconfig",
"chars": 108,
"preview": "#include \"Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"\n#include \"Generated.xcconfig\"\n"
},
{
"path": "legacy/app/ios/Podfile",
"chars": 1355,
"preview": "# Uncomment this line to define a global platform for your project\n# platform :ios, '12.0'\n\n# CocoaPods analytics sends "
},
{
"path": "legacy/app/ios/Runner/AppDelegate.swift",
"chars": 404,
"preview": "import UIKit\nimport Flutter\n\n@UIApplicationMain\n@objc class AppDelegate: FlutterAppDelegate {\n override func applicatio"
},
{
"path": "legacy/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
"chars": 6834,
"preview": "{\n \"images\" : [\n {\n \"filename\" : \"40.png\",\n \"idiom\" : \"iphone\",\n \"scale\" : \"2x\",\n \"size\" : \"20x2"
},
{
"path": "legacy/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json",
"chars": 391,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"LaunchImage.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "legacy/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md",
"chars": 336,
"preview": "# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in"
},
{
"path": "legacy/app/ios/Runner/Base.lproj/LaunchScreen.storyboard",
"chars": 2377,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard"
},
{
"path": "legacy/app/ios/Runner/Base.lproj/Main.storyboard",
"chars": 1808,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
},
{
"path": "legacy/app/ios/Runner/Glowby.swift",
"chars": 2015,
"preview": "import UIKit\nimport RealityKit\nimport ARKit\n\nclass GlowbyARView: UIView, ARSessionDelegate {\n var arView: ARView!\n\n "
},
{
"path": "legacy/app/ios/Runner/Info.plist",
"chars": 2040,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "legacy/app/ios/Runner/Runner-Bridging-Header.h",
"chars": 38,
"preview": "#import \"GeneratedPluginRegistrant.h\"\n"
},
{
"path": "legacy/app/ios/Runner.xcodeproj/project.pbxproj",
"chars": 24226,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "legacy/app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
"chars": 135,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"self:\">\n </FileRef"
},
{
"path": "legacy/app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
"chars": 238,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "legacy/app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
"chars": 226,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "legacy/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
"chars": 3291,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"1430\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "legacy/app/ios/Runner.xcworkspace/contents.xcworkspacedata",
"chars": 224,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"group:Runner.xcodepr"
},
{
"path": "legacy/app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
"chars": 238,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "legacy/app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
"chars": 226,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "legacy/app/lib/main.dart",
"chars": 281,
"preview": "// Importing required packages\nimport 'package:flutter/material.dart';\nimport 'views/screens/talk_screen.dart';\n\n// Entr"
},
{
"path": "legacy/app/lib/models/ai.dart",
"chars": 7705,
"preview": "import 'dart:math';\n\nimport 'package:glowby/services/hugging_face_api.dart';\nimport 'package:glowby/services/pulze_ai_ap"
},
{
"path": "legacy/app/lib/services/hugging_face_api.dart",
"chars": 6191,
"preview": "import 'dart:convert';\nimport 'package:flutter/foundation.dart';\nimport 'package:http/http.dart' as http;\nimport 'packag"
},
{
"path": "legacy/app/lib/services/openai_api.dart",
"chars": 16917,
"preview": "import 'dart:convert';\nimport 'package:flutter/foundation.dart';\nimport 'package:flutter_secure_storage/flutter_secure_s"
},
{
"path": "legacy/app/lib/services/pulze_ai_api.dart",
"chars": 5461,
"preview": "import 'dart:convert';\nimport 'package:flutter/foundation.dart';\nimport 'package:http/http.dart' as http;\nimport 'packag"
},
{
"path": "legacy/app/lib/utils/color_utils.dart",
"chars": 3707,
"preview": "import 'dart:math';\nimport 'package:flutter/material.dart';\n\n/// Adjusts a color value by increasing its brightness towa"
},
{
"path": "legacy/app/lib/utils/text_to_speech.dart",
"chars": 4030,
"preview": "import 'dart:async';\n\nimport 'package:flutter_tts/flutter_tts.dart';\n\nclass TextToSpeech {\n static const String typingI"
},
{
"path": "legacy/app/lib/utils/timestamp.dart",
"chars": 3887,
"preview": "// Copyright 2018, the Chromium project authors. Please see the AUTHORS file\n// for details. All rights reserved. Use o"
},
{
"path": "legacy/app/lib/utils/utils.dart",
"chars": 8044,
"preview": "import 'dart:convert';\nimport 'dart:ui' as ui;\nimport 'dart:math';\n\nimport 'package:flutter/material.dart';\nimport 'pack"
},
{
"path": "legacy/app/lib/utils/utils_desktop.dart",
"chars": 4285,
"preview": "import 'dart:convert';\nimport 'dart:io';\nimport 'dart:math';\nimport 'package:flutter/foundation.dart';\nimport 'package:f"
},
{
"path": "legacy/app/lib/utils/utils_stub.dart",
"chars": 1053,
"preview": "import 'package:flutter/material.dart';\nimport 'dart:ui' as ui;\n\nclass UtilsPlatform {\n static Future<void> downloadIma"
},
{
"path": "legacy/app/lib/utils/utils_web.dart",
"chars": 6020,
"preview": "import 'dart:async';\nimport 'dart:convert';\nimport 'dart:typed_data';\nimport 'dart:ui' as ui;\nimport 'dart:html' as html"
},
{
"path": "legacy/app/lib/views/dialogs/ai_error_dialog.dart",
"chars": 1497,
"preview": "import 'package:flutter/material.dart';\nimport 'package:glowby/utils/utils.dart';\n\nclass AiErrorDialog extends StatefulW"
},
{
"path": "legacy/app/lib/views/dialogs/ai_settings_dialog.dart",
"chars": 13512,
"preview": "import 'package:flutter/material.dart';\nimport 'package:glowby/views/screens/global_settings.dart';\nimport 'package:glow"
},
{
"path": "legacy/app/lib/views/dialogs/api_key_dialog.dart",
"chars": 10998,
"preview": "import 'package:flutter/material.dart';\nimport 'package:glowby/services/openai_api.dart';\nimport 'package:glowby/utils/u"
},
{
"path": "legacy/app/lib/views/html/html_view_screen.dart",
"chars": 258,
"preview": "// This file conditionally exports the appropriate implementation.\nexport 'html_view_screen_interface.dart';\nexport 'htm"
},
{
"path": "legacy/app/lib/views/html/html_view_screen_desktop.dart",
"chars": 3056,
"preview": "// html_view_screen_stub.dart\n// import 'dart:async';\nimport 'dart:io';\n\nimport 'package:flutter/material.dart';\nimport "
},
{
"path": "legacy/app/lib/views/html/html_view_screen_interface.dart",
"chars": 122,
"preview": "import 'package:flutter/material.dart';\n\nabstract class HtmlViewScreenInterface {\n Widget build(BuildContext context);\n"
},
{
"path": "legacy/app/lib/views/html/html_view_screen_mobile.dart",
"chars": 912,
"preview": "import 'package:flutter/material.dart';\nimport 'package:webview_flutter/webview_flutter.dart';\n\nclass HtmlViewScreenMobi"
},
{
"path": "legacy/app/lib/views/html/html_view_screen_stub.dart",
"chars": 606,
"preview": "// html_view_screen_stub.dart\nimport 'package:flutter/material.dart';\n\nclass HtmlViewScreen extends StatelessWidget {\n "
},
{
"path": "legacy/app/lib/views/html/html_view_screen_web.dart",
"chars": 1821,
"preview": "import 'dart:html';\nimport 'dart:html' as html;\nimport 'dart:ui_web' as ui;\nimport 'package:flutter/material.dart';\nimpo"
},
{
"path": "legacy/app/lib/views/screens/chat_screen.dart",
"chars": 21129,
"preview": "import 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:glowby/views/screens/glo"
},
{
"path": "legacy/app/lib/views/screens/global_settings.dart",
"chars": 9270,
"preview": "import 'package:glowby/services/hugging_face_api.dart';\nimport 'package:glowby/services/openai_api.dart';\n\nclass GlobalS"
},
{
"path": "legacy/app/lib/views/screens/magical_loading_view.dart",
"chars": 3962,
"preview": "import 'dart:math';\nimport 'package:flutter/material.dart';\n\nclass MagicalLoadingView extends StatefulWidget {\n const M"
},
{
"path": "legacy/app/lib/views/screens/talk_screen.dart",
"chars": 11402,
"preview": "import 'dart:convert';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:g"
},
{
"path": "legacy/app/lib/views/widgets/message.dart",
"chars": 938,
"preview": "import '../../utils/timestamp.dart';\n\n/// A class representing a chat message with a text, timestamp, user ID, and optio"
},
{
"path": "legacy/app/lib/views/widgets/message_bubble.dart",
"chars": 9450,
"preview": "import 'package:flutter/gestures.dart';\nimport 'package:flutter/material.dart';\nimport 'package:url_launcher/url_launche"
},
{
"path": "legacy/app/lib/views/widgets/messages.dart",
"chars": 2072,
"preview": "import 'package:glowby/views/screens/global_settings.dart';\n\nimport 'message.dart';\nimport 'message_bubble.dart';\nimport"
},
{
"path": "legacy/app/lib/views/widgets/new_message.dart",
"chars": 11143,
"preview": "import 'dart:async';\nimport 'dart:math';\n\nimport 'package:flutter/foundation.dart';\nimport 'package:glowby/views/screens"
},
{
"path": "legacy/app/lib/views/widgets/paint_window.dart",
"chars": 11876,
"preview": "import 'dart:async';\nimport 'dart:convert';\nimport 'dart:math';\n\nimport 'package:flutter/material.dart';\nimport 'package"
},
{
"path": "legacy/app/lib/views/widgets/tasks_view.dart",
"chars": 5399,
"preview": "import 'package:flutter/material.dart';\n\nclass TasksView extends StatefulWidget {\n final List<String> tasks;\n final St"
},
{
"path": "legacy/app/linux/.gitignore",
"chars": 18,
"preview": "flutter/ephemeral\n"
},
{
"path": "legacy/app/linux/CMakeLists.txt",
"chars": 5419,
"preview": "# Project-level configuration.\ncmake_minimum_required(VERSION 3.10)\nproject(runner LANGUAGES CXX)\n\n# The name of the exe"
},
{
"path": "legacy/app/linux/flutter/CMakeLists.txt",
"chars": 2815,
"preview": "# This file controls Flutter-level build steps. It should not be edited.\ncmake_minimum_required(VERSION 3.10)\n\nset(EPHEM"
},
{
"path": "legacy/app/linux/flutter/generated_plugin_registrant.cc",
"chars": 1062,
"preview": "//\n// Generated file. Do not edit.\n//\n\n// clang-format off\n\n#include \"generated_plugin_registrant.h\"\n\n#include <file_se"
},
{
"path": "legacy/app/linux/flutter/generated_plugin_registrant.h",
"chars": 303,
"preview": "//\n// Generated file. Do not edit.\n//\n\n// clang-format off\n\n#ifndef GENERATED_PLUGIN_REGISTRANT_\n#define GENERATED_PLUG"
},
{
"path": "legacy/app/linux/flutter/generated_plugins.cmake",
"chars": 813,
"preview": "#\n# Generated file, do not edit.\n#\n\nlist(APPEND FLUTTER_PLUGIN_LIST\n file_selector_linux\n flutter_secure_storage_linux"
},
{
"path": "legacy/app/linux/main.cc",
"chars": 180,
"preview": "#include \"my_application.h\"\n\nint main(int argc, char** argv) {\n g_autoptr(MyApplication) app = my_application_new();\n "
},
{
"path": "legacy/app/linux/my_application.cc",
"chars": 3704,
"preview": "#include \"my_application.h\"\n\n#include <flutter_linux/flutter_linux.h>\n#ifdef GDK_WINDOWING_X11\n#include <gdk/gdkx.h>\n#en"
},
{
"path": "legacy/app/linux/my_application.h",
"chars": 388,
"preview": "#ifndef FLUTTER_MY_APPLICATION_H_\n#define FLUTTER_MY_APPLICATION_H_\n\n#include <gtk/gtk.h>\n\nG_DECLARE_FINAL_TYPE(MyApplic"
},
{
"path": "legacy/app/macos/.gitignore",
"chars": 89,
"preview": "# Flutter-related\n**/Flutter/ephemeral/\n**/Pods/\n\n# Xcode-related\n**/dgph\n**/xcuserdata/\n"
},
{
"path": "legacy/app/macos/Flutter/Flutter-Debug.xcconfig",
"chars": 125,
"preview": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"\n#include \"ephemeral/Flutter-Generated.xccon"
},
{
"path": "legacy/app/macos/Flutter/Flutter-Release.xcconfig",
"chars": 127,
"preview": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"\n#include \"ephemeral/Flutter-Generated.xcc"
},
{
"path": "legacy/app/macos/Flutter/GeneratedPluginRegistrant.swift",
"chars": 1029,
"preview": "//\n// Generated file. Do not edit.\n//\n\nimport FlutterMacOS\nimport Foundation\n\nimport file_selector_macos\nimport flutter"
},
{
"path": "legacy/app/macos/Podfile",
"chars": 1389,
"preview": "platform :osx, '10.15'\n\n# CocoaPods analytics sends network stats synchronously affecting flutter build latency.\nENV['CO"
},
{
"path": "legacy/app/macos/Runner/AppDelegate.swift",
"chars": 214,
"preview": "import Cocoa\nimport FlutterMacOS\n\n@NSApplicationMain\nclass AppDelegate: FlutterAppDelegate {\n override func application"
},
{
"path": "legacy/app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
"chars": 1207,
"preview": "{\n \"images\" : [\n {\n \"filename\" : \"16.png\",\n \"idiom\" : \"mac\",\n \"scale\" : \"1x\",\n \"size\" : \"16x16\"\n"
},
{
"path": "legacy/app/macos/Runner/Base.lproj/MainMenu.xib",
"chars": 23723,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion"
},
{
"path": "legacy/app/macos/Runner/Configs/AppInfo.xcconfig",
"chars": 592,
"preview": "// Application-level settings for the Runner target.\n//\n// This may be replaced with something auto-generated from metad"
},
{
"path": "legacy/app/macos/Runner/Configs/Debug.xcconfig",
"chars": 77,
"preview": "#include \"../../Flutter/Flutter-Debug.xcconfig\"\n#include \"Warnings.xcconfig\"\n"
},
{
"path": "legacy/app/macos/Runner/Configs/Release.xcconfig",
"chars": 79,
"preview": "#include \"../../Flutter/Flutter-Release.xcconfig\"\n#include \"Warnings.xcconfig\"\n"
},
{
"path": "legacy/app/macos/Runner/Configs/Warnings.xcconfig",
"chars": 580,
"preview": "WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverl"
},
{
"path": "legacy/app/macos/Runner/DebugProfile.entitlements",
"chars": 403,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "legacy/app/macos/Runner/Info.plist",
"chars": 1060,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "legacy/app/macos/Runner/MainFlutterWindow.swift",
"chars": 388,
"preview": "import Cocoa\nimport FlutterMacOS\n\nclass MainFlutterWindow: NSWindow {\n override func awakeFromNib() {\n let flutterVi"
},
{
"path": "legacy/app/macos/Runner/Release.entitlements",
"chars": 240,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "legacy/app/macos/Runner.xcodeproj/project.pbxproj",
"chars": 32444,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXAggregateTarget sec"
},
{
"path": "legacy/app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
"chars": 238,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "legacy/app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
"chars": 3635,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"1430\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "legacy/app/macos/Runner.xcworkspace/contents.xcworkspacedata",
"chars": 224,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"group:Runner.xcodepr"
},
{
"path": "legacy/app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
"chars": 238,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "legacy/app/macos/RunnerTests/RunnerTests.swift",
"chars": 290,
"preview": "import FlutterMacOS\nimport Cocoa\nimport XCTest\n\nclass RunnerTests: XCTestCase {\n\n func testExample() {\n // If you ad"
},
{
"path": "legacy/app/pubspec.yaml",
"chars": 3188,
"preview": "name: glowby\ndescription: Glowby app.\n\n# The following line prevents the package from being accidentally published to\n# "
},
{
"path": "legacy/app/web/index.html",
"chars": 1134,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"UTF-8\">\n <meta content=\"IE=Edge\" http-equiv=\"X-UA-Compatible\">\n <meta n"
},
{
"path": "legacy/app/web/manifest.json",
"chars": 1363,
"preview": "{\n \"name\": \"Quiz App\",\n \"short_name\": \"Quiz\",\n \"start_url\": \".\",\n \"display\": \"standalone\",\n \"background_c"
},
{
"path": "legacy/app/web/tv.js",
"chars": 785,
"preview": "const SpeechRecognition =\n window.SpeechRecognition || window.webkitSpeechRecognition;\n\nif (SpeechRecognition != null) "
},
{
"path": "legacy/app/windows/.gitignore",
"chars": 291,
"preview": "flutter/ephemeral/\n\n# Visual Studio user-specific files.\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# Visual Studio bu"
},
{
"path": "legacy/app/windows/CMakeLists.txt",
"chars": 4142,
"preview": "# Project-level configuration.\ncmake_minimum_required(VERSION 3.14)\nproject(app LANGUAGES CXX)\n\n# The name of the execut"
},
{
"path": "legacy/app/windows/flutter/CMakeLists.txt",
"chars": 3742,
"preview": "# This file controls Flutter-level build steps. It should not be edited.\ncmake_minimum_required(VERSION 3.14)\n\nset(EPHEM"
},
{
"path": "legacy/app/windows/flutter/generated_plugin_registrant.cc",
"chars": 850,
"preview": "//\n// Generated file. Do not edit.\n//\n\n// clang-format off\n\n#include \"generated_plugin_registrant.h\"\n\n#include <file_se"
},
{
"path": "legacy/app/windows/flutter/generated_plugin_registrant.h",
"chars": 302,
"preview": "//\n// Generated file. Do not edit.\n//\n\n// clang-format off\n\n#ifndef GENERATED_PLUGIN_REGISTRANT_\n#define GENERATED_PLUG"
},
{
"path": "legacy/app/windows/flutter/generated_plugins.cmake",
"chars": 837,
"preview": "#\n# Generated file, do not edit.\n#\n\nlist(APPEND FLUTTER_PLUGIN_LIST\n file_selector_windows\n flutter_secure_storage_win"
},
{
"path": "legacy/app/windows/runner/CMakeLists.txt",
"chars": 1796,
"preview": "cmake_minimum_required(VERSION 3.14)\nproject(runner LANGUAGES CXX)\n\n# Define the application target. To change its name,"
},
{
"path": "legacy/app/windows/runner/Runner.rc",
"chars": 3009,
"preview": "// Microsoft Visual C++ generated resource script.\n//\n#pragma code_page(65001)\n#include \"resource.h\"\n\n#define APSTUDIO_R"
},
{
"path": "legacy/app/windows/runner/flutter_window.cpp",
"chars": 2122,
"preview": "#include \"flutter_window.h\"\n\n#include <optional>\n\n#include \"flutter/generated_plugin_registrant.h\"\n\nFlutterWindow::Flutt"
}
]
// ... and 139 more files (download for full content)
About this extraction
This page contains the full source code of the glowbom/glowby GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 339 files (7.4 MB), approximately 1.9M tokens, and a symbol index with 1709 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.