Commit Graph

509 Commits

Author SHA1 Message Date
github-actions[bot]
ef72fe335d chore: ci build 2026-05-03 13:28:27 +00:00
github-actions[bot]
68f0b03035 chore: release userscript 2.31.0 userscript-v2.31.0 2026-05-03 21:27:55 +08:00
pionxzh
fa68986831 fix: adapt to chatgpt new sidebar
closes #352
2026-05-03 21:24:53 +08:00
pionxzh
e3a54a3bd2 fix: disable the chip system 2026-05-03 19:57:04 +08: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
shallfun
986e053871 fix: skip file search tool exports 2026-04-30 00:03:13 +08:00
shallfun
7998a4fcc5 fix: preserve multimodal tool image exports 2026-04-30 00:03:13 +08:00
shallfun
3d491744f4 fix: skip hidden tool messages in exports 2026-04-30 00:03:13 +08:00
github-actions[bot]
3111334b3a chore: ci build 2026-03-01 15:24:37 +00:00
github-actions[bot]
097799ff66 chore: release userscript 2.30.0 userscript-v2.30.0 2026-03-01 23:24:07 +08:00
pionxzh
9c1ad6f6c0 feat: include project name in export all zip filenames
When exporting conversations from a project, the zip filename becomes
chatgpt-export-{format}-project-{normalized-name}.zip instead of the
generic chatgpt-export-{format}.zip.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 23:19:28 +08:00
pionxzh
df1af4bdf8 fix: improve shift select user experience 2026-03-01 23:07:37 +08:00
pionxzh
2fd3516993 fix: set a hard cap 100 on export conversations to avoid OOM 2026-03-01 23:07:13 +08:00
pionxzh
9ce72c0f05 fix: user need to select project first to load the conversation list 2026-03-01 22:36:56 +08:00
pionxzh
e927de3658 fix: stream conversation pages progressively to unblock Export All UI
With 400+ conversations the old code made 4+ sequential requests and
only rendered anything after all of them completed, freezing the dialog
for 10+ seconds with no interactable elements.

- Add optional `onBatch` callback to `fetchAllConversations` that fires
  after each page arrives, so callers can react incrementally
- Wire `onBatch` in ExportDialog to append each batch to state as it
  lands — first 100 conversations appear almost instantly
- Remove `loading` from the `disabled` guard so the user can select and
  export already-loaded conversations while remaining pages still fetch
- Show "Loading…" only when the list is empty (initial blank state);
  replace with a subtle italic sentinel at the bottom of the list while
  subsequent pages are still in flight
- Allow the project selector to be changed at any time during loading

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 22:05:27 +08:00
pionxzh
b31d5cd565 fix: add explicit ApiConversations type annotation to resolve TS7022
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 21:44:34 +08:00
pionxzh
0e1770356e fix: handle alphanumeric cursor pagination for project conversations
Project conversations API switched from numeric offsets to opaque
alphanumeric cursors. The old code discarded the cursor and fell back
to offset += limit, so only the first page was ever exported.

- Add `cursor?: string | null` to `ApiConversations` interface
- Update `projectConversationsApi` to accept `string | number` cursor
- Expose the API-returned cursor in `fetchProjectConversations`
- Use cursor-based advancement in `fetchAllConversations` for project
  requests, with numeric offset fallback for regular conversations
- Add early-exit guard when API returns neither total nor next cursor

Closes #341

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 21:42:10 +08:00
github-actions[bot]
425a115fab chore: ci build 2026-02-24 17:35:27 +00:00
github-actions[bot]
98032cfa2c chore: release userscript 2.29.5 userscript-v2.29.5 2026-02-25 01:34:58 +08:00
pionxzh
4300f4e81c fix: update script match for better coverage
closes #335
2026-02-25 01:30:03 +08:00
github-actions[bot]
fda9ea0ea0 chore: ci build 2026-02-19 17:37:17 +00:00
github-actions[bot]
83d0058aea chore: release userscript 2.29.4 userscript-v2.29.4 2026-02-20 01:36:47 +08:00
pionxzh
87e56790ee fix: add log on script startup for better debugging
#335
2026-02-20 01:30:22 +08:00
pionxzh
d659b9db83 fix: handle skipping thought/reasoning message
#309
2026-02-20 01:18:08 +08:00
pionxzh
ab01af10af fix: chatgpt changed from file service to sediment
fixes #333
2026-02-20 01:04:16 +08:00
pionxzh
c7ee6776fe fix: improve model name mapping 2026-02-20 00:49:37 +08:00
pionxzh
9452700c2b fix: load all projects with cursor
fixes #336
2026-02-20 00:31:39 +08:00
github-actions[bot]
2f8b34c4c9 chore: ci build 2026-02-05 22:14:43 +00:00
github-actions[bot]
680dd6cc15 chore: release userscript 2.29.3 userscript-v2.29.3 2026-02-06 06:14:18 +08:00
Eddy G
5f08862f38 fix: improve content reference handling for citations and entities
- Add support for alt_text content reference type (entity mentions)
- Fix Unicode normalization to include non-breaking hyphens and preserve newlines
- Render citations as inline links instead of footnotes
- Include all sources for multi-citations
- Fix HTML exporter to retain links instead of stripping them
2026-02-06 06:12:19 +08:00
github-actions[bot]
7eeccf6dbf chore: ci build 2026-01-18 06:19:25 +00:00
github-actions[bot]
a47f0da038 chore: release userscript 2.29.2 userscript-v2.29.2 2026-01-18 14:19:00 +08:00
pionxzh
54d4b6b15b fix: support navlist and handle content reference in text and html 2026-01-18 14:09:33 +08:00
Eddy G
748e1b9703 fix: parse citations and suppress 'thoughts' 2026-01-18 14:09:33 +08:00
github-actions[bot]
0baa7b12a5 chore: ci build 2025-07-25 02:31:35 +00:00
github-actions[bot]
c182b6a345 chore: release userscript 2.29.1 userscript-v2.29.1 2025-07-25 10:31:08 +08:00
finickyspider
f4c4a7f132 fix(api): fix project-level chat export stuck at "Loading"
Fixes #305
2025-07-25 10:30:29 +08:00
github-actions[bot]
d34a575689 chore: ci build 2025-07-03 16:17:35 +00:00
github-actions[bot]
c8f3b4e81f chore: release userscript 2.29.0 userscript-v2.29.0 2025-07-04 00:17:11 +08:00
Pionxzh
0b1109b0d1 fix: correct timestamp default 2025-07-04 00:07:10 +08:00
Pionxzh
e1d1a2f645 feat: support audio chat
closes #298
2025-07-03 23:47:38 +08:00
github-actions[bot]
832732b90c feat(export): make max-message-export configurable 2025-07-03 23:08:28 +08:00
github-actions[bot]
7e57605a21 chore: ci build 2025-06-08 19:20:46 +00:00
github-actions[bot]
32f1971767 chore: release userscript 2.28.1 userscript-v2.28.1 2025-06-09 03:20:23 +08:00
Pionxzh
87c2cb74ba fix: fix exporting markdown with timestamp
closes #296
2025-06-09 03:19:03 +08:00
Pionxzh
103b6170ff fix: fix toggle style 2025-06-05 01:14:07 +08:00
SukkaW
1cc4972760 refactor: replace eventemitter3 w/ mitt 2025-06-01 16:39:31 +08:00
github-actions[bot]
94824983fa chore: ci build 2025-05-11 16:15:11 +00:00
github-actions[bot]
b38cf3a503 chore: release userscript 2.28.0 userscript-v2.28.0 2025-05-12 00:14:44 +08:00
Jonathan Goren
cc89487c74 chore(i18n): add translations for project selection 2025-05-12 00:13:47 +08:00