Scrollbar:
- Override scrollbar-width to 'auto' (was 'thin' ~6px, nearly invisible)
- Increase scrollbar-color opacity to 0.7 (was 0.5)
- Add explicit ::-webkit-scrollbar width 10px for older Chrome
Text selection:
- Block selectstart in Phase 1 (onMouseDown) instead of Phase 2
- Clear existing text selection with removeAllRanges() on drag start
Stability:
- Add window blur handler to cleanup drag on tab switch / window leave
- Guard onWheel RAF callback against stale drag state
- Defensive removeMarquee() before createMarquee()
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The global CSS makes all scrollbars transparent by default (only visible
on hover). For table containers, scrollbars should always be visible so
users know the content is scrollable.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Binary search in findRowIndexAtY (O(n) → O(log n))
- Short-circuit applyRange when row index unchanged
- Cache scrollParent in beginDrag instead of per-frame lookup
- Skip autoScroll recomputation when scrollTop unchanged
- Detect scrollbar clicks on inner containers, not just document
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace document.body.style.userSelect manipulation with selectstart
event listener to prevent text selection (no body style mutation)
- Skip mousedown on scrollbar area (clientX >= documentElement.clientWidth)
- Extract cleanupDrag() for robust state cleanup in all code paths
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Allow starting drag from anywhere on the page (not just inside tbody)
- Use 5px drag threshold to prevent accidental selection on click
- Support mouse wheel scrolling during drag to select new rows
- Auto-scroll when dragging near viewport edges with row re-detection
- Find closest row by viewport Y coordinate instead of requiring exact hit
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove selectedRowIds/isRowSelected/row-selected CSS from DataTable.
Add semi-transparent marquee rectangle overlay during drag selection,
with dark mode theme support using primary colors.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add selectedRowIds prop to DataTable. Selected rows get a blue tint
background (primary-50), including sticky columns. Integrated into
AccountsView and ProxiesView for visual feedback during swipe select.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add useSwipeSelect composable that enables drag-to-select rows in
DataTable. Mousedown on a row and drag up/down to select/deselect
a contiguous range. Integrated into AccountsView and ProxiesView.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
P0 fixes:
- StorageType: UploadFromURL now returns actual storageType, fixing
GDrive tasks being incorrectly recorded as "s3"
- Concurrency: selectAccount returns ReleaseFunc to caller, fixing
premature slot release before task creation
- Race condition: applyRecoveredToken uses copy-on-write for
account.Credentials map to prevent concurrent write panic
- GetCameoStatus: use direct HTTP call to populate missing fields
(StatusMessage, InstructionSetHint, InstructionSet)
- Frontend: progress bar uses reactive now.value instead of
non-reactive Date.now()
P1 fixes:
- EditImage: pass req.Image through to CreateImageRequest
- ListPending: add LIMIT 200 to prevent unbounded queries
- Worker encapsulation: add ListPendingTasks/UpdateTask proxy methods
to SoraTaskService, worker no longer accesses repo directly
- Frontend polling: add retry counter (3 attempts) before giving up
P2-P3 fixes:
- QuotaBar: handle both quota_source and source field names
- i18n: replace hardcoded Chinese labels in SoraPromptBar and
example prompts in SoraGeneratePage with i18n keys
- Fix regenrate → regenerate i18n typo
- SoraMediaPreview: skip Escape handler when not visible
- SoraGeneratePage: void handleGenerate promise in handleRetry
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- QuotaLimitCard: label and input on same row for reduced height
- CreateAccountModal: move AffinityConfigCard into OAuth/SetupToken
quota control section, eliminating duplicate "配额控制" cards
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Merge the separate "配额限制" and "配额控制" sections into one unified
"配额控制" section per account type. For Anthropic OAuth/SetupToken
accounts, AffinityConfigCard is now inside the quota control section
instead of a separate card.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- selectAccountByID: use original task's account for remix instead of
random selection, preventing permission issues in OAuth path
- Upstream error passthrough: forward HTTP status code and body from
upstream instead of always returning 500
- Add accountRepo to SoraTaskService for direct account lookup
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add ClearAccountAffinity to mockGatewayCacheForPlatform,
openAIWSStateStoreTimeoutProbeCache, mockGatewayCacheForGemini
- Add allow_messages_dispatch field to groups/available contract test
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Windows terminals may send request bodies in GBK encoding, causing
garbled prompts and upstream failures. Validate UTF-8 encoding in
readBody and return 400 for invalid encodings.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
pq driver treats []byte as bytea, not jsonb. Convert charJSON and
reqBody to *string before passing to ExecContext for character_info
and request_body JSONB columns.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add async task management for Sora video/image generation with two
forwarding modes: OAuth (SDK) and API Key (HTTP upstream). Includes
background worker for status polling and 3-layer storage degradation
(S3 > local disk > upstream URL passthrough).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move both cards under a unified "配额限制" section wrapper for Anthropic
platform. Remove independent section wrappers from both components so
they render as plain rounded cards. Non-Anthropic apikey accounts get
their own quota section with just QuotaLimitCard.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
classifyByAffinityZone was running before filterByMinPriority, causing
high-priority accounts in the red zone to be removed before priority
filtering, allowing low-priority accounts in the green zone to be
selected instead. Move zone classification into the priority loop so
it operates within same-priority groups, with fallback to next priority
group when current group is entirely in the red zone.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- AffinityConfigCard redesigned to match QuotaLimitCard style (section title + toggle card)
- Moved from standalone section to quota area, visible for all Anthropic account types
- Green zone (base) allows empty = no limit, min=1 when set; yellow zone hidden when base unset
- Deduplicated affinity save logic in EditAccountModal (single block for all Anthropic types)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend: Add AffinityZone type with GetAffinityBase/GetAffinityBuffer/GetAffinityZone
methods. Integrate classifyByAffinityZone into Layer 2 scheduling and red zone
check into Layer 1.6 affinity hit path. 17 unit tests covering all zone boundaries.
Frontend: Add AffinityConfigCard for base/buffer configuration in EditAccountModal.
Move affinity badge from status column to capacity column with zone-based coloring
(count/limit format). Extract CapacityBadge and QuotaBadge components to reduce
AccountCapacityCell from 444 to 215 lines.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>