Commit Graph

508 Commits

Author SHA1 Message Date
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
Jonathan Goren
9ddd11f715 feat: add project support
Add support for getting project list. OpenAI
calls these 'gizmos' and they include custom GPTs
and other apps.
Only projects are supported for now.
Add project selection box in Export All dialog.
2025-05-12 00:13:47 +08:00