104 Commits

Author SHA1 Message Date
github-actions[bot]
6b68edbe28 chore: ci build 2026-07-05 19:23:05 +00:00
github-actions[bot]
e579f4f676 chore: ci build 2026-05-28 14:54:32 +00:00
github-actions[bot]
cf1ad3b408 chore: ci build 2026-05-12 17:59:27 +00:00
github-actions[bot]
726251c71a chore: ci build 2026-05-10 04:24:30 +00:00
github-actions[bot]
ef72fe335d chore: ci build 2026-05-03 13:28:27 +00:00
Chris James
462836d1b2 feat: batched export, smart rate-limit backoff, filter chips, and dialog UX improvements (#348)
* feat(export): batch export in 100-conv waves and date-range filter

Closes #347 (feature request on upstream pionxzh/chatgpt-exporter).

Changes:
- src/constants.ts: add EXPORT_OPERATION_BATCH = 100 (fixed, non-tunable)
- src/utils/download.ts: add PartInfo type, part-suffix support to
  buildZipFileName, and new buildJsonBatchFileName for official JSON
- src/exporter/{html,json,markdown}.ts: accept optional partIndex /
  totalParts params; each exportAllTo* uses PartInfo to suffix its
  output filename when multiple batches are produced
- src/ui/ExportDialog.tsx:
  - Remove hardcoded EXPORT_LIMIT = 100 selection cap; users may now
    select all loaded conversations (up to exportAllLimit from settings)
  - Add DateFilter component with from/to date inputs that filter the
    conversation list by create_time (inclusive, end-of-day for dateTo)
  - Wave-based export pipeline: splits selected conversations into
    chunks of EXPORT_OPERATION_BATCH (100), fetches each chunk via
    RequestQueue, downloads the completed batch, pauses 400 ms, then
    continues with the next chunk — keeping memory bounded and API
    pressure low
  - Progress bar now shows global count across all batches plus
    "Batch X/N" indicator when multiple batches are active
  - Local-file source also batched in 100-conversation waves with the
    same 400 ms inter-batch delay
  - Shift-click range selection and Select All no longer impose a
    hard cap (handled by the batch pipeline instead)
- src/locales/en.json: add Date Filter Label/Hint, Selected count,
  Export batch info, Exporting batch strings; update Export All Limit
  Description to mention 100-conversation waves

Made-with: Cursor

* fix(export): correct date filter for ISO string timestamps + add update_time field

The list endpoint returns create_time/update_time as ISO 8601 strings
(e.g. "2025-07-10T14:53:58.103234Z"), not Unix numbers. The previous
filter compared a string to a number, always producing NaN comparisons
and filtering out every conversation.

- Add toMs() helper that handles both ISO strings and legacy Unix numbers
- Fix filter comparisons to use toMs() and compare in milliseconds
- Update ApiConversationItem type: create_time is number|string,
  update_time is optional number|string
- Add filterField state ('create_time' | 'update_time') with a
  Created/Updated selector in the DateFilter UI so users can filter
  by either timestamp
- Add i18n strings for the new selector labels

Made-with: Cursor

* feat: redesign date filter layout with presets, auto-load conversations, batch label on export

- Date filter redesigned to two-row layout: field selector + quick preset
  buttons (7d / 30d / 90d / This year / Clear) on row 1; From–To date
  inputs on row 2, eliminating the wrapping issue
- Auto-load all conversations when dialog opens (default project null
  instead of undefined) so search box, Last 100 button, and counter are
  immediately visible without needing to manually pick a project
- Export button shows batch count when selection > 100, e.g. "Export · 3 files"
  with a tooltip explaining "3 separate downloads, 100 conversations each"
- Progress display shows "Batch X/Y · completed/total" for multi-batch exports
- Remove redundant "Export from API" static text (project selector implies it)
- Add locale strings: Date Preset 7d/30d/90d/Year, Clear filter, Batch progress

Made-with: Cursor

* fix: show batch download count inline below export button

Made-with: Cursor

* feat: filter chips, date column, All conversations, starred indicator

- Filter chips via # trigger: type # in search box to open a popover
  with available filters; click to add as a removable chip:
    ★ Starred only | 💾 Saved only (skip temp) | ⏱ Long conversation
    (active ≥ N days, editable inline) | 🤖 GPT/custom AI | 💬 Regular chats
- Date column: each conversation row now shows a compact relative date
  (Today / Yesterday / Xd ago / Mon DD / MMM YYYY) on the right edge
- Starred indicator: ★ shown inline for starred conversations
- Project dropdown: new "📂 All conversations" option fetches the main
  list plus every project's conversations, deduplicated; uses new
  fetchAllConversationsAll() API function
- ApiConversationItem: add is_starred, is_temporary_chat, gizmo_id fields
  from the list endpoint payload
- CSS: SelectItem flex layout, new SelectChip / SelectChips /
  SelectFilterPopover / SelectItemMeta classes

Made-with: Cursor

* feat: add Load More button to fetch conversations beyond the initial limit

- Export fetchConversationsPage from api.ts for single-page incremental loading
- Add onHasMore callback to fetchAllConversations so callers know when the
  fetch was stopped by the user-configured limit (vs the API having no more data)
- DialogContent tracks hasMore / totalAvailable / loadingMore state
- A "Load N more" button appears below the conversation list when more pages
  exist; shows "N remaining" count once the API total is known
- Resets cleanly on project change

Made-with: Cursor

* feat: comprehensive filter chip system with chat class, origin, status, and mode logic

- Add conversation_origin, pinned_time, is_archived, is_do_not_remember to ApiConversationItem
- Replace simple chip flags with typed FilterChipDef union:
    chat_class (Regular/GPT/Project), origin (dynamic from data),
    status (starred/temporary/pinned), duration_gte, recency_lte
- Each chip has an include/exclude mode toggle (click badge to flip)
- AND/OR logic toggle appears in chip bar when 2+ chips are active
- Text search supports * and ? wildcards alongside chips
- Picker grouped by category with dynamic Origin values from loaded conversations
- Project classification cross-references fetched project IDs against gizmo_id
- Remove ProjectSelect dropdown: always auto-load all conversations on open
- Chat class chip: Regular (no gizmo), GPT (store gizmo), Project (matched gizmo)

Made-with: Cursor

* refactor(filters): redesign chips as multi-select with two-stage picker

- Chat class, Origin, Project, Status are now single chips that open a
  sub-view where users tick multiple values (Regular + GPT, etc.)
- New Project chip lets users pick specific project(s) by name
- Date filter moved from top-of-dialog into the chip bar as a full-width
  date-range chip (Created/Updated selector + date inputs + presets)
- Picker uses two-stage UX: root list → sub-view with checkboxes + Apply
- Clicking a chip's value label reopens the picker for in-place editing
- AND/OR logic toggle only appears for 2+ non-date chips
- Remove standalone DateFilter component and related state from DialogContent

Made-with: Cursor

* fix(filters): guard gizmo_id null check for project chip

Made-with: Cursor

* fix: prevent export dialog close mid-run and add resume-from-offset control

- Block ESC, outside-click, and X button while an export is in progress;
  module-level exportingRef bridges DialogContent to ExportDialog without
  lifting state.
- Stale-fetch prevention: fetchGenRef generation counter discards callbacks
  from a previous dialog mount after remount, eliminating the console
  errors seen when reopening the dialog mid-run.
- Cleanup on unmount: clear all three RequestQueues when DialogContent
  unmounts so orphaned background work stops immediately.
- Add "→ 100 from #N" resume control in the toolbar: a number input sets
  the starting offset and the arrow button selects the next 100 convs
  from that position, letting users continue a partial export (e.g. set
  offset to 200 to resume after 2 finished batches).

Made-with: Cursor

* perf: use Web Worker for sleep() to bypass background-tab timer throttle

Chrome throttles setTimeout in non-focused tabs to >=1 s minimum, which
causes the export queue (200 ms between requests * 100 convs = 20 s/batch)
to stall entirely when the user switches away from the tab.

Fix: run all timers inside a single persistent Web Worker. Workers are
exempt from background-tab throttling, so exports run at full speed
regardless of tab focus. A graceful fallback to regular setTimeout is
retained for environments where blob-URL Worker creation fails (CSP etc.)
so the change is entirely non-breaking.

Made-with: Cursor

* fix: proper 429 backoff — wait Retry-After secs, cap retries, skip when exhausted

Previously the queue retried forever with a max backoff of 1600 ms, causing
the 429 storm seen in the console (the same request hammering the API
continuously with no meaningful pause).

Changes:
- api.ts: export RateLimitError with retryAfterMs read from Retry-After
  header (defaults to 30 s when header is absent or unparseable)
- queue.ts: on RateLimitError, sleep max(retryAfterMs, 30 s) before
  retrying; give up after 8 consecutive 429s and skip the request; on
  regular errors give up after 5 retries and skip; add rate_limited status
  so the UI can show what is happening
- ExportDialog.tsx: show "Rate limited — waiting Xs…" with an amber
  progress bar when status is rate_limited; clear progress bar to blue
  once the pause ends

Made-with: Cursor

* fix: global queue pause on 429 instead of per-item backoff

The previous design retried each item individually with a 30s wait, so if
all 100 conversations in a batch were rate-limited the theoretical worst
case was 100 x 8 x 30s = 24,000s.

New design:
- On the first 429 the entire queue is frozen (pauseUntil timestamp).
  Every subsequent process() call checks pauseUntil and waits out the
  remaining time before making any new request — one shared wait for all
  queued items, not N separate waits.
- The pause length is max(Retry-After header, 60s * pauseCount) so it
  backs off exponentially if the first pause was not long enough:
  pause 1 = 60s, pause 2 = 120s, pause 3 = 180s, etc.
- After MAX_GLOBAL_PAUSES (5) the queue aborts rather than looping forever
  (total max wait ~15 min before giving up).
- The failed item is always returned to the front of the queue to be
  retried after the pause clears rather than being counted as a skip.
- Removed per-item rateRetries counter (no longer needed).

Made-with: Cursor

* feat: add Test API button and rate-limit header logging

- probeApi(): make one minimal request (1 conversation) to check whether
  the API is currently accepting traffic. Returns ok/rate_limited status
  plus any X-RateLimit-* or Retry-After headers in the response.
- fetchApi(): logs all known rate-limit response headers to console on
  every call (success or failure) so we can discover which headers
  ChatGPT actually sends.
- ExportDialog: "Test API" button next to the upload icon. Shows
  "Testing... / API ready / Rate limited · wait Xs / Error" inline.
  Hovering the button after a successful test shows any rate-limit headers
  as a tooltip (useful for debugging).

Made-with: Cursor

* fix: add Cancel button during export; replace useless dimmed X

Previously the X button was grayed out and non-functional while an export
was running, with no way to stop it short of refreshing the page.

Changes:
- Cancel button (red, top-right) appears while processing. Clicking it
  sets cancelledRef, calls queue.stop() on all three queues, and lets the
  in-flight request finish before cleanly stopping the loop.
- The on('done') handler checks cancelledRef: if set, it skips the
  download and the next-batch kickoff and just resets processing state.
- cancelledRef is cleared at the start of each new export so back-to-back
  exports work correctly.
- The dimmed X is kept (slightly more transparent) as a visual reminder
  that the dialog cannot be closed mid-export; its tooltip now says
  "Click Cancel to stop the export".

Made-with: Cursor

* feat: sortable column headers, both date columns, unambiguous date format

Problems fixed:
- Date column only showed create_time with no label — confusing when the
  user had filtered or sorted by update_time.
- Dates < 1 year omitted the year ("Jun 17" vs "Jun 17, 2025"), making it
  impossible to tell which year a conversation belonged to.
- No way to sort the list — order was whatever the API returned.

Changes:
- formatConvDate: always includes the year (removes the < 365-day branch).
  Still shows "Today" / "Yesterday" for very recent items.
- ConversationSelect: two date columns (Created, Updated). The active-sort
  column is visually highlighted (bold, blue) so the user always knows
  what order the list is in.
- Sortable header row (SelectListHeader): click Title / Created / Updated
  to sort; click again to toggle asc/desc. Arrow indicator (↑/↓/↕) shows
  current state. Defaults to Created ↓ (newest first) matching the
  original API order.
- Sorting applied in the filtered memo after chip/text filtering.

Made-with: Cursor

---------

Co-authored-by: Pionxzh <spigbbbbb@gmail.com>
2026-05-03 19:18:36 +08:00
github-actions[bot]
3111334b3a chore: ci build 2026-03-01 15:24:37 +00:00
github-actions[bot]
425a115fab chore: ci build 2026-02-24 17:35:27 +00:00
github-actions[bot]
fda9ea0ea0 chore: ci build 2026-02-19 17:37:17 +00:00
github-actions[bot]
2f8b34c4c9 chore: ci build 2026-02-05 22:14:43 +00:00
github-actions[bot]
7eeccf6dbf chore: ci build 2026-01-18 06:19:25 +00:00
github-actions[bot]
0baa7b12a5 chore: ci build 2025-07-25 02:31:35 +00:00
github-actions[bot]
d34a575689 chore: ci build 2025-07-03 16:17:35 +00:00
github-actions[bot]
7e57605a21 chore: ci build 2025-06-08 19:20:46 +00:00
github-actions[bot]
94824983fa chore: ci build 2025-05-11 16:15:11 +00:00
github-actions[bot]
f5e810ccfb chore: ci build 2025-05-04 19:52:37 +00:00
github-actions[bot]
9dd55891de chore: ci build 2025-02-02 03:06:43 +00:00
github-actions[bot]
eccd564ce2 chore: ci build 2024-12-22 12:55:47 +00:00
github-actions[bot]
330f8d38ad chore: ci build 2024-11-29 06:12:00 +00:00
github-actions[bot]
96c28f4b09 chore: ci build 2024-09-12 17:58:29 +00:00
github-actions[bot]
dd52ffc811 chore: ci build 2024-09-08 19:30:31 +00:00
github-actions[bot]
67af2bf530 chore: ci build 2024-09-07 09:33:44 +00:00
github-actions[bot]
e4b02bb5f6 chore: ci build 2024-06-29 18:36:39 +00:00
github-actions[bot]
73529ab2d2 chore: ci build 2024-06-17 06:31:57 +00:00
github-actions[bot]
1e019db457 chore: ci build 2024-05-21 04:13:47 +00:00
Ilya Rice
e48c902021 fix: add @match for new ChatGPT domain with query parameter
Added @match for https://chatgpt.com/?oai-dm=1 to the Tampermonkey script manifest. This is to
handle the new domain for ChatGPT, as the old domain https://chat.openai.com/ now redirects to
the new address with the '?oai-dm=1' query parameter. The manifest already includes all variations
of the new address except this one.
2024-05-21 11:14:39 +08:00
github-actions[bot]
f809da67b5 chore: ci build 2024-05-18 06:55:18 +00:00
github-actions[bot]
5e237c6e9c chore: ci build 2024-05-04 18:38:52 +00:00
github-actions[bot]
8825bee6b1 chore: ci build 2024-04-23 19:34:47 +00:00
github-actions[bot]
6e8fb5c79e chore: ci build 2024-04-04 19:53:43 +00:00
github-actions[bot]
45ff5bd852 chore: ci build 2024-03-30 18:08:24 +00:00
github-actions[bot]
b697fb364f chore: ci build 2024-03-13 19:35:08 +00:00
Pionxzh
b5b124a606 chore: ci build 2024-02-24 23:39:18 +08:00
github-actions[bot]
41739ec17a CI build 2024-02-13 09:29:17 +00:00
github-actions[bot]
c4662d4a2f CI build 2024-01-07 10:50:54 +00:00
github-actions[bot]
743c38e634 CI build 2023-12-24 19:00:19 +00:00
github-actions[bot]
c9c76890ed CI build 2023-11-22 19:26:23 +00:00
github-actions[bot]
c8a68c715e CI build 2023-11-14 16:00:38 +00:00
github-actions[bot]
d3ebf364e2 CI build 2023-11-12 19:31:15 +00:00
github-actions[bot]
a6ab77ab6e CI build 2023-11-08 18:33:35 +00:00
github-actions[bot]
0d83b1ab5d CI build 2023-11-07 14:41:09 +00:00
github-actions[bot]
aea00d2109 CI build 2023-11-05 10:59:29 +00:00
github-actions[bot]
54ece73b86 CI build 2023-10-16 15:55:41 +00:00
github-actions[bot]
0ef4baa150 CI build 2023-09-25 17:00:09 +00:00
github-actions[bot]
fd678d14c5 CI build 2023-09-19 16:20:31 +00:00
github-actions[bot]
2e176e0ce7 CI build 2023-08-29 16:47:15 +00:00
github-actions[bot]
ed004fea2f CI build 2023-08-21 18:02:20 +00:00
github-actions[bot]
23b7e65436 CI build 2023-08-20 10:24:54 +00:00
github-actions[bot]
ec9a266d69 CI build 2023-08-20 07:45:50 +00:00
github-actions[bot]
f6be988a4a CI build 2023-07-09 16:46:09 +00:00