decant-core 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ai/copilot.js CHANGED
@@ -1,36 +1,36 @@
1
- import { ChatParser } from './base.js';
2
- import { convertToMarkdown } from '../utils/html-to-markdown.js';
1
+ import { ChatParser } from "./base.js";
2
+ import { convertToMarkdown } from "../utils/html-to-markdown.js";
3
3
 
4
4
  export class CopilotParser extends ChatParser {
5
- name = 'Copilot';
5
+ name = "Copilot";
6
6
  isAvailable(url) {
7
7
  return (
8
- url.includes('copilot.microsoft.com') ||
9
- url.includes('copilot.com') ||
10
- url.includes('copilot.cloud.microsoft') ||
11
- url.includes('m365.cloud.microsoft') ||
12
- url.includes('m365.microsoft.com') ||
13
- url.includes('bing.com/chat') ||
14
- url.includes('bing.com/copilot') ||
15
- url.includes('bing.com/copilotsearch') ||
16
- url.includes('edgeservices.bing.com')
8
+ url.includes("copilot.microsoft.com") ||
9
+ url.includes("copilot.com") ||
10
+ url.includes("copilot.cloud.microsoft") ||
11
+ url.includes("m365.cloud.microsoft") ||
12
+ url.includes("m365.microsoft.com") ||
13
+ url.includes("bing.com/chat") ||
14
+ url.includes("bing.com/copilot") ||
15
+ url.includes("bing.com/copilotsearch") ||
16
+ url.includes("edgeservices.bing.com")
17
17
  );
18
18
  }
19
19
 
20
20
  async parse() {
21
- let title = 'Copilot Conversation';
21
+ let title = "Copilot Conversation";
22
22
  if (document.title) {
23
23
  const cleanTitle = document.title
24
- .replace(/^Microsoft Copilot:\s*/i, '')
25
- .replace(/\s*-\s*Microsoft Copilot$/i, '')
26
- .replace(/^Copilot:\s*/i, '')
27
- .replace(/\s*-\s*Copilot$/i, '')
28
- .replace(/Your AI companion/i, '')
24
+ .replace(/^Microsoft Copilot:\s*/i, "")
25
+ .replace(/\s*-\s*Microsoft Copilot$/i, "")
26
+ .replace(/^Copilot:\s*/i, "")
27
+ .replace(/\s*-\s*Copilot$/i, "")
28
+ .replace(/Your AI companion/i, "")
29
29
  .trim();
30
30
  if (
31
31
  cleanTitle &&
32
- cleanTitle.toLowerCase() !== 'microsoft copilot' &&
33
- cleanTitle.toLowerCase() !== 'copilot'
32
+ cleanTitle.toLowerCase() !== "microsoft copilot" &&
33
+ cleanTitle.toLowerCase() !== "copilot"
34
34
  ) {
35
35
  title = cleanTitle;
36
36
  }
@@ -60,28 +60,28 @@ export class CopilotParser extends ChatParser {
60
60
  '[data-testid="regenerate-message-button-popover"]',
61
61
  '[data-testid="chat-suggestion"]',
62
62
  '[data-testid="loading-message"]',
63
- '.fai-CopilotMessage__actions',
64
- '.fai-SuggestionList',
65
- '.fai-UserMessage__accessibleHeading',
66
- '.fai-CopilotMessage__accessibleHeading',
63
+ ".fai-CopilotMessage__actions",
64
+ ".fai-SuggestionList",
65
+ ".fai-UserMessage__accessibleHeading",
66
+ ".fai-CopilotMessage__accessibleHeading",
67
67
  '[class*="suggestedReplies"]',
68
68
  '[class*="workingCard"]',
69
69
  '[class*="WorkingCard"]',
70
70
  '[class*="disclaimerText"]',
71
- 'cib-action-bar',
72
- 'cib-feedback-buttons',
73
- 'cib-message-actions',
74
- '.sr-only',
71
+ "cib-action-bar",
72
+ "cib-feedback-buttons",
73
+ "cib-message-actions",
74
+ ".sr-only",
75
75
  ];
76
76
  noiseSelectors.forEach((sel) => {
77
77
  clone.querySelectorAll(sel).forEach((el) => el.remove());
78
78
  });
79
79
 
80
80
  // Transform data-url span buttons into standard anchor tags
81
- clone.querySelectorAll('span[data-url]').forEach((span) => {
82
- const url = span.getAttribute('data-url');
83
- if (url && !url.startsWith('ca://')) {
84
- const a = document.createElement('a');
81
+ clone.querySelectorAll("span[data-url]").forEach((span) => {
82
+ const url = span.getAttribute("data-url");
83
+ if (url && !url.startsWith("ca://")) {
84
+ const a = document.createElement("a");
85
85
  a.href = url;
86
86
  a.textContent = span.textContent;
87
87
  span.replaceWith(a);
@@ -89,22 +89,26 @@ export class CopilotParser extends ChatParser {
89
89
  });
90
90
 
91
91
  // Standardize code blocks with language labels
92
- clone.querySelectorAll('div.rounded-xl, div[class*="code-block"]').forEach((block) => {
93
- const langEl = block.querySelector('span.capitalize, [class*="language-"]');
94
- const codeEl = block.querySelector('code, pre');
95
- if (codeEl) {
96
- const lang = langEl ? langEl.innerText.trim().toLowerCase() : '';
97
- const codeText = codeEl.innerText || codeEl.textContent;
98
- const newPre = document.createElement('pre');
99
- const newCode = document.createElement('code');
100
- if (lang) {
101
- newCode.className = `language-${lang}`;
92
+ clone
93
+ .querySelectorAll('div.rounded-xl, div[class*="code-block"]')
94
+ .forEach((block) => {
95
+ const langEl = block.querySelector(
96
+ 'span.capitalize, [class*="language-"]',
97
+ );
98
+ const codeEl = block.querySelector("code, pre");
99
+ if (codeEl) {
100
+ const lang = langEl ? langEl.innerText.trim().toLowerCase() : "";
101
+ const codeText = codeEl.innerText || codeEl.textContent;
102
+ const newPre = document.createElement("pre");
103
+ const newCode = document.createElement("code");
104
+ if (lang) {
105
+ newCode.className = `language-${lang}`;
106
+ }
107
+ newCode.textContent = codeText;
108
+ newPre.appendChild(newCode);
109
+ block.replaceWith(newPre);
102
110
  }
103
- newCode.textContent = codeText;
104
- newPre.appendChild(newCode);
105
- block.replaceWith(newPre);
106
- }
107
- });
111
+ });
108
112
 
109
113
  return clone.innerHTML;
110
114
  };
@@ -121,7 +125,8 @@ export class CopilotParser extends ChatParser {
121
125
  // Filter out nested matches
122
126
  if (turnElements.length) {
123
127
  turnElements = turnElements.filter(
124
- (el) => !turnElements.some((other) => other !== el && other.contains(el)),
128
+ (el) =>
129
+ !turnElements.some((other) => other !== el && other.contains(el)),
125
130
  );
126
131
  }
127
132
 
@@ -145,20 +150,23 @@ export class CopilotParser extends ChatParser {
145
150
 
146
151
  if (turnElements.length) {
147
152
  turnElements.forEach((node) => {
148
- const dataContent = node.getAttribute('data-content') || '';
149
- const dataAuthor = (node.getAttribute('data-message-author-role') || '').toLowerCase();
150
- const className = typeof node.className === 'string' ? node.className : '';
151
- const testId = node.getAttribute('data-testid') || '';
153
+ const dataContent = node.getAttribute("data-content") || "";
154
+ const dataAuthor = (
155
+ node.getAttribute("data-message-author-role") || ""
156
+ ).toLowerCase();
157
+ const className =
158
+ typeof node.className === "string" ? node.className : "";
159
+ const testId = node.getAttribute("data-testid") || "";
152
160
 
153
161
  const isUser =
154
- dataAuthor === 'user' ||
155
- testId === 'chatQuestion' ||
156
- testId === 'chat-turn-user' ||
157
- testId === 'user-turn' ||
158
- className.includes('UserMessage') ||
159
- dataContent === 'user-message' ||
160
- className.includes('user-message') ||
161
- testId === 'user-message';
162
+ dataAuthor === "user" ||
163
+ testId === "chatQuestion" ||
164
+ testId === "chat-turn-user" ||
165
+ testId === "user-turn" ||
166
+ className.includes("UserMessage") ||
167
+ dataContent === "user-message" ||
168
+ className.includes("user-message") ||
169
+ testId === "user-message";
162
170
 
163
171
  if (isUser) {
164
172
  const targetNode =
@@ -167,24 +175,26 @@ export class CopilotParser extends ChatParser {
167
175
  ) || node;
168
176
  const clone = targetNode.cloneNode(true);
169
177
  clone
170
- .querySelectorAll('.fai-UserMessage__accessibleHeading, .sr-only, button')
178
+ .querySelectorAll(
179
+ ".fai-UserMessage__accessibleHeading, .sr-only, button",
180
+ )
171
181
  .forEach((el) => el.remove());
172
182
  const text = clone.innerText || clone.textContent;
173
183
  if (text && text.trim()) {
174
- messages.push({ role: 'User', content: text.trim() });
184
+ messages.push({ role: "User", content: text.trim() });
175
185
  }
176
186
  } else {
177
187
  const targetNode =
178
188
  node.querySelector('[data-testid="markdown-reply"]') ||
179
189
  node.querySelector('[data-testid="ai-message-body"]') ||
180
190
  node.querySelector('[data-testid="copilot-message-content"]') ||
181
- node.querySelector('.fai-CopilotMessage__content') ||
191
+ node.querySelector(".fai-CopilotMessage__content") ||
182
192
  node.querySelector('[class*="group/ai-message-item"]') ||
183
193
  node;
184
194
  const html = processAiElement(targetNode);
185
195
  const markdown = convertToMarkdown(html);
186
196
  if (markdown && markdown.trim()) {
187
- messages.push({ role: 'Copilot', content: markdown });
197
+ messages.push({ role: "Copilot", content: markdown });
188
198
  }
189
199
  }
190
200
  });
@@ -193,39 +203,45 @@ export class CopilotParser extends ChatParser {
193
203
  // Helper to extract messages from Shadow DOM cib-serp components (Classic Bing Chat / Copilot)
194
204
  const extractFromCibSerp = (rootDoc = document) => {
195
205
  try {
196
- const cibSerp = rootDoc.querySelector('cib-serp');
206
+ const cibSerp = rootDoc.querySelector("cib-serp");
197
207
  if (!cibSerp || !cibSerp.shadowRoot) return [];
198
- const cibConversation = cibSerp.shadowRoot.querySelector('cib-conversation');
208
+ const cibConversation =
209
+ cibSerp.shadowRoot.querySelector("cib-conversation");
199
210
  if (!cibConversation || !cibConversation.shadowRoot) return [];
200
- const cibTurns = cibConversation.shadowRoot.querySelectorAll('cib-chat-turn');
211
+ const cibTurns =
212
+ cibConversation.shadowRoot.querySelectorAll("cib-chat-turn");
201
213
  const results = [];
202
214
 
203
215
  cibTurns.forEach((turn) => {
204
216
  const turnRoot = turn.shadowRoot || turn;
205
- const msgGroups = turnRoot.querySelectorAll('cib-message-group');
217
+ const msgGroups = turnRoot.querySelectorAll("cib-message-group");
206
218
  msgGroups.forEach((group) => {
207
- const source = (group.getAttribute('source') || '').toLowerCase();
208
- const role = source === 'user' ? 'User' : 'Copilot';
219
+ const source = (group.getAttribute("source") || "").toLowerCase();
220
+ const role = source === "user" ? "User" : "Copilot";
209
221
  const groupRoot = group.shadowRoot || group;
210
- const cibMessages = groupRoot.querySelectorAll('cib-message');
222
+ const cibMessages = groupRoot.querySelectorAll("cib-message");
211
223
 
212
224
  cibMessages.forEach((msg) => {
213
225
  const msgRoot = msg.shadowRoot || msg;
214
- if (role === 'User') {
226
+ if (role === "User") {
215
227
  const text = msgRoot.textContent?.trim();
216
- if (text) results.push({ role: 'User', content: text });
228
+ if (text) results.push({ role: "User", content: text });
217
229
  } else {
218
- const shared = msgRoot.querySelector('cib-shared') || msgRoot;
230
+ const shared = msgRoot.querySelector("cib-shared") || msgRoot;
219
231
  const html = processAiElement(shared);
220
232
  const md = convertToMarkdown(html);
221
- if (md && md.trim()) results.push({ role: 'Copilot', content: md.trim() });
233
+ if (md && md.trim())
234
+ results.push({ role: "Copilot", content: md.trim() });
222
235
  }
223
236
  });
224
237
  });
225
238
  });
226
239
  return results;
227
240
  } catch (err) {
228
- console.warn('[AI Exporter] Shadow DOM cib-serp extraction error:', err);
241
+ console.warn(
242
+ "[AI Exporter] Shadow DOM cib-serp extraction error:",
243
+ err,
244
+ );
229
245
  return [];
230
246
  }
231
247
  };
@@ -240,21 +256,21 @@ export class CopilotParser extends ChatParser {
240
256
 
241
257
  // Tier 5: Web components in light DOM (cib-chat-turn / cib-message-group)
242
258
  if (!messages.length) {
243
- const cibTurns = document.querySelectorAll('cib-chat-turn');
259
+ const cibTurns = document.querySelectorAll("cib-chat-turn");
244
260
  if (cibTurns.length) {
245
261
  cibTurns.forEach((turn) => {
246
- const source = (turn.getAttribute('source') || '').toLowerCase();
247
- const role = source === 'user' ? 'User' : 'Copilot';
248
- if (role === 'User') {
262
+ const source = (turn.getAttribute("source") || "").toLowerCase();
263
+ const role = source === "user" ? "User" : "Copilot";
264
+ if (role === "User") {
249
265
  const text = turn.innerText || turn.textContent;
250
266
  if (text && text.trim()) {
251
- messages.push({ role: 'User', content: text.trim() });
267
+ messages.push({ role: "User", content: text.trim() });
252
268
  }
253
269
  } else {
254
270
  const html = processAiElement(turn);
255
271
  const markdown = convertToMarkdown(html);
256
272
  if (markdown && markdown.trim()) {
257
- messages.push({ role: 'Copilot', content: markdown });
273
+ messages.push({ role: "Copilot", content: markdown });
258
274
  }
259
275
  }
260
276
  });
@@ -263,25 +279,25 @@ export class CopilotParser extends ChatParser {
263
279
 
264
280
  // Tier 6: React [data-turn-id] layout
265
281
  if (!messages.length) {
266
- const turnNodes = document.querySelectorAll('[data-turn-id]');
282
+ const turnNodes = document.querySelectorAll("[data-turn-id]");
267
283
  turnNodes.forEach((node) => {
268
284
  const roleAttr = (
269
- node.getAttribute('data-turn-role') ||
270
- node.getAttribute('data-author') ||
271
- ''
285
+ node.getAttribute("data-turn-role") ||
286
+ node.getAttribute("data-author") ||
287
+ ""
272
288
  ).toLowerCase();
273
- const role = roleAttr.includes('user') ? 'User' : 'Copilot';
289
+ const role = roleAttr.includes("user") ? "User" : "Copilot";
274
290
 
275
- if (role === 'User') {
291
+ if (role === "User") {
276
292
  const text = node.innerText || node.textContent;
277
293
  if (text && text.trim()) {
278
- messages.push({ role: 'User', content: text.trim() });
294
+ messages.push({ role: "User", content: text.trim() });
279
295
  }
280
296
  } else {
281
297
  const html = processAiElement(node);
282
298
  const markdown = convertToMarkdown(html);
283
299
  if (markdown && markdown.trim()) {
284
- messages.push({ role: 'Copilot', content: markdown });
300
+ messages.push({ role: "Copilot", content: markdown });
285
301
  }
286
302
  }
287
303
  });
@@ -289,51 +305,58 @@ export class CopilotParser extends ChatParser {
289
305
 
290
306
  // Tier 7: Universal role="article" scanner for Copilot
291
307
  if (!messages.length) {
292
- const articles = Array.from(document.querySelectorAll('[role="article"]'));
308
+ const articles = Array.from(
309
+ document.querySelectorAll('[role="article"]'),
310
+ );
293
311
  articles.forEach((art) => {
294
312
  const headingText = (
295
- art.querySelector('h1, h2, h3, h4, h5, h6')?.textContent || ''
313
+ art.querySelector("h1, h2, h3, h4, h5, h6")?.textContent || ""
296
314
  ).toLowerCase();
297
315
  const ariaLabel = (
298
- art.getAttribute('aria-label') ||
299
- art.getAttribute('aria-labelledby') ||
300
- ''
316
+ art.getAttribute("aria-label") ||
317
+ art.getAttribute("aria-labelledby") ||
318
+ ""
301
319
  ).toLowerCase();
302
- const className = typeof art.className === 'string' ? art.className : '';
320
+ const className =
321
+ typeof art.className === "string" ? art.className : "";
303
322
 
304
323
  const isUser =
305
- headingText.includes('you said') ||
306
- ariaLabel.includes('user-message') ||
307
- ariaLabel.includes('you said') ||
308
- className.includes('UserMessage') ||
309
- art.matches('[data-testid="chatQuestion"], [data-testid="user-message"]');
324
+ headingText.includes("you said") ||
325
+ ariaLabel.includes("user-message") ||
326
+ ariaLabel.includes("you said") ||
327
+ className.includes("UserMessage") ||
328
+ art.matches(
329
+ '[data-testid="chatQuestion"], [data-testid="user-message"]',
330
+ );
310
331
 
311
332
  const isCopilot =
312
- headingText.includes('copilot said') ||
313
- ariaLabel.includes('copilot-message') ||
314
- ariaLabel.includes('copilot said') ||
315
- className.includes('CopilotMessage') ||
316
- art.matches('[data-testid="copilot-message-div"], [data-testid="ai-message"]');
333
+ headingText.includes("copilot said") ||
334
+ ariaLabel.includes("copilot-message") ||
335
+ ariaLabel.includes("copilot said") ||
336
+ className.includes("CopilotMessage") ||
337
+ art.matches(
338
+ '[data-testid="copilot-message-div"], [data-testid="ai-message"]',
339
+ );
317
340
 
318
341
  if (isUser) {
319
342
  const clone = art.cloneNode(true);
320
343
  clone
321
- .querySelectorAll('h1, h2, h3, h4, h5, h6, .sr-only, button')
344
+ .querySelectorAll("h1, h2, h3, h4, h5, h6, .sr-only, button")
322
345
  .forEach((el) => el.remove());
323
346
  const text = clone.innerText || clone.textContent;
324
347
  if (text && text.trim()) {
325
- messages.push({ role: 'User', content: text.trim() });
348
+ messages.push({ role: "User", content: text.trim() });
326
349
  }
327
350
  } else if (isCopilot) {
328
351
  const targetNode =
329
352
  art.querySelector('[data-testid="markdown-reply"]') ||
330
- art.querySelector('.fai-CopilotMessage__content') ||
353
+ art.querySelector(".fai-CopilotMessage__content") ||
331
354
  art.querySelector('[data-testid="ai-message-body"]') ||
332
355
  art;
333
356
  const html = processAiElement(targetNode);
334
357
  const markdown = convertToMarkdown(html);
335
358
  if (markdown && markdown.trim()) {
336
- messages.push({ role: 'Copilot', content: markdown });
359
+ messages.push({ role: "Copilot", content: markdown });
337
360
  }
338
361
  }
339
362
  });
@@ -341,10 +364,11 @@ export class CopilotParser extends ChatParser {
341
364
 
342
365
  // Tier 8: Check embedded iframe documents (e.g. Edge Sidebar panel iframe)
343
366
  if (!messages.length) {
344
- const iframes = Array.from(document.querySelectorAll('iframe'));
367
+ const iframes = Array.from(document.querySelectorAll("iframe"));
345
368
  for (const iframe of iframes) {
346
369
  try {
347
- const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document;
370
+ const iframeDoc =
371
+ iframe.contentDocument || iframe.contentWindow?.document;
348
372
  if (iframeDoc) {
349
373
  const iframeCib = extractFromCibSerp(iframeDoc);
350
374
  if (iframeCib.length) {
@@ -358,19 +382,20 @@ export class CopilotParser extends ChatParser {
358
382
  );
359
383
  if (iframeTurns.length) {
360
384
  iframeTurns.forEach((node) => {
361
- const dataContent = node.getAttribute('data-content') || '';
362
- const testId = node.getAttribute('data-testid') || '';
385
+ const dataContent = node.getAttribute("data-content") || "";
386
+ const testId = node.getAttribute("data-testid") || "";
363
387
  const isUser =
364
- testId === 'chatQuestion' ||
365
- dataContent === 'user-message' ||
366
- testId === 'user-message';
388
+ testId === "chatQuestion" ||
389
+ dataContent === "user-message" ||
390
+ testId === "user-message";
367
391
  if (isUser) {
368
392
  const targetNode =
369
393
  node.querySelector(
370
394
  '[data-testid="chatOutput"], .fai-UserMessage__message, [data-content="user-message"]',
371
395
  ) || node;
372
396
  const text = targetNode.innerText || targetNode.textContent;
373
- if (text && text.trim()) messages.push({ role: 'User', content: text.trim() });
397
+ if (text && text.trim())
398
+ messages.push({ role: "User", content: text.trim() });
374
399
  } else {
375
400
  const targetNode =
376
401
  node.querySelector('[data-testid="markdown-reply"]') ||
@@ -379,7 +404,7 @@ export class CopilotParser extends ChatParser {
379
404
  const html = processAiElement(targetNode);
380
405
  const markdown = convertToMarkdown(html);
381
406
  if (markdown && markdown.trim()) {
382
- messages.push({ role: 'Copilot', content: markdown });
407
+ messages.push({ role: "Copilot", content: markdown });
383
408
  }
384
409
  }
385
410
  });
@@ -393,17 +418,26 @@ export class CopilotParser extends ChatParser {
393
418
  }
394
419
 
395
420
  // Fallback title generation if default title is generic
396
- if (title === 'Copilot Conversation' && messages.length > 0 && messages[0].role === 'User') {
397
- const firstPrompt = messages[0].content.split('\n')[0].trim();
421
+ if (
422
+ title === "Copilot Conversation" &&
423
+ messages.length > 0 &&
424
+ messages[0].role === "User"
425
+ ) {
426
+ const firstPrompt = messages[0].content.split("\n")[0].trim();
398
427
  if (firstPrompt) {
399
- title = firstPrompt.length > 40 ? `${firstPrompt.slice(0, 40)}...` : firstPrompt;
428
+ title =
429
+ firstPrompt.length > 40
430
+ ? `${firstPrompt.slice(0, 40)}...`
431
+ : firstPrompt;
400
432
  }
401
433
  }
402
434
 
403
435
  const currentUrl =
404
- typeof window !== 'undefined' && window.location ? window.location.href || '' : '';
436
+ typeof window !== "undefined" && window.location
437
+ ? window.location.href || ""
438
+ : "";
405
439
  const metadata = {
406
- Source: 'Copilot',
440
+ Source: "Copilot",
407
441
  Date: new Date().toLocaleString(),
408
442
  Link: currentUrl,
409
443
  };
package/ai/deepseek.js CHANGED
@@ -1,10 +1,10 @@
1
- import { ChatParser } from './base.js';
2
- import { convertToMarkdown } from '../utils/html-to-markdown.js';
1
+ import { ChatParser } from "./base.js";
2
+ import { convertToMarkdown } from "../utils/html-to-markdown.js";
3
3
 
4
4
  function getUserToken() {
5
5
  try {
6
- if (typeof localStorage === 'undefined') return null;
7
- const raw = localStorage.getItem('userToken');
6
+ if (typeof localStorage === "undefined") return null;
7
+ const raw = localStorage.getItem("userToken");
8
8
  if (!raw) return null;
9
9
  try {
10
10
  const parsed = JSON.parse(raw);
@@ -19,8 +19,8 @@ function getUserToken() {
19
19
 
20
20
  function getConversationId() {
21
21
  try {
22
- if (typeof window === 'undefined' || !window.location) return null;
23
- const path = window.location.pathname || window.location.href || '';
22
+ if (typeof window === "undefined" || !window.location) return null;
23
+ const path = window.location.pathname || window.location.href || "";
24
24
  return (
25
25
  path.match(/\/chat\/s\/([a-f0-9-]+)/)?.[1] ??
26
26
  path.match(/\/a\/chat\/s\/([a-f0-9-]+)/)?.[1] ??
@@ -34,10 +34,10 @@ function getConversationId() {
34
34
  async function fetchDeepSeekConversation(sessionId, token) {
35
35
  const url = `https://chat.deepseek.com/api/v0/chat/history_messages?chat_session_id=${sessionId}&cache_version=0`;
36
36
  const response = await fetch(url, {
37
- method: 'GET',
38
- credentials: 'include',
37
+ method: "GET",
38
+ credentials: "include",
39
39
  headers: {
40
- 'Content-Type': 'application/json',
40
+ "Content-Type": "application/json",
41
41
  Authorization: `Bearer ${token}`,
42
42
  },
43
43
  });
@@ -74,27 +74,29 @@ async function fetchDeepSeekConversation(sessionId, token) {
74
74
 
75
75
  return branch
76
76
  .map((msgNode) => {
77
- const isUser = msgNode.role === 'USER' || msgNode.role === 'user';
78
- const role = isUser ? 'User' : 'DeepSeek';
79
- const content = msgNode.content || msgNode.text || '';
77
+ const isUser = msgNode.role === "USER" || msgNode.role === "user";
78
+ const role = isUser ? "User" : "DeepSeek";
79
+ const content = msgNode.content || msgNode.text || "";
80
80
  return { role, content: content.trim() };
81
81
  })
82
82
  .filter((msg) => msg.content.length > 0);
83
83
  }
84
84
 
85
85
  export class DeepSeekParser extends ChatParser {
86
- name = 'DeepSeek';
86
+ name = "DeepSeek";
87
87
  isAvailable(url) {
88
- return url.includes('chat.deepseek.com');
88
+ return url.includes("chat.deepseek.com");
89
89
  }
90
90
 
91
91
  async parse() {
92
- const title = document.title || 'DeepSeek Chat';
92
+ const title = document.title || "DeepSeek Chat";
93
93
 
94
94
  const currentUrl =
95
- typeof window !== 'undefined' && window.location ? window.location.href || '' : '';
95
+ typeof window !== "undefined" && window.location
96
+ ? window.location.href || ""
97
+ : "";
96
98
  const metadata = {
97
- Source: 'DeepSeek',
99
+ Source: "DeepSeek",
98
100
  Date: new Date().toLocaleString(),
99
101
  Link: currentUrl,
100
102
  };
@@ -110,25 +112,30 @@ export class DeepSeekParser extends ChatParser {
110
112
  }
111
113
  }
112
114
  } catch (e) {
113
- console.warn('[AI Exporter] DeepSeek API fetch failed, falling back to DOM:', e);
115
+ console.warn(
116
+ "[AI Exporter] DeepSeek API fetch failed, falling back to DOM:",
117
+ e,
118
+ );
114
119
  }
115
120
 
116
121
  // Secondary: DOM Fallback
117
122
  const messages = [];
118
123
 
119
124
  // Selectors from research
120
- const userSelector = '.fbb737a4';
121
- const assistantSelector = '.ds-markdown';
125
+ const userSelector = ".fbb737a4";
126
+ const assistantSelector = ".ds-markdown";
122
127
 
123
128
  // We'll traverse the DOM to find these in order
124
- const allElements = document.querySelectorAll(`${userSelector}, ${assistantSelector}`);
129
+ const allElements = document.querySelectorAll(
130
+ `${userSelector}, ${assistantSelector}`,
131
+ );
125
132
 
126
133
  allElements.forEach((el) => {
127
- let role = 'Unknown';
134
+ let role = "Unknown";
128
135
  if (el.matches(userSelector)) {
129
- role = 'User';
136
+ role = "User";
130
137
  } else if (el.matches(assistantSelector)) {
131
- role = 'DeepSeek';
138
+ role = "DeepSeek";
132
139
  }
133
140
 
134
141
  const text = convertToMarkdown(el);
@@ -139,10 +146,12 @@ export class DeepSeekParser extends ChatParser {
139
146
 
140
147
  // Fallback if the specific classes fail (e.g. class name rotation)
141
148
  if (messages.length === 0) {
142
- const messageRows = document.querySelectorAll('.ds-message-row, .message-row');
149
+ const messageRows = document.querySelectorAll(
150
+ ".ds-message-row, .message-row",
151
+ );
143
152
  messageRows.forEach((row) => {
144
- const isUser = row.classList.contains('ds-user-message');
145
- const role = isUser ? 'User' : 'DeepSeek';
153
+ const isUser = row.classList.contains("ds-user-message");
154
+ const role = isUser ? "User" : "DeepSeek";
146
155
  const text = convertToMarkdown(row);
147
156
  if (text.trim()) {
148
157
  messages.push({ role, content: text.trim() });