decant-core 1.0.0 → 1.0.1

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/qwen.js CHANGED
@@ -1,24 +1,24 @@
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 QwenParser extends ChatParser {
5
- name = 'Qwen';
5
+ name = "Qwen";
6
6
  isAvailable(url) {
7
- return url.includes('qwen.ai');
7
+ return url.includes("qwen.ai");
8
8
  }
9
9
 
10
10
  async parse() {
11
11
  // Try to get the actual chat title from multiple possible selectors
12
12
  const titleSelectors = [
13
- '.chat-item-drag-link-content-tip-text',
14
- '.ant-tooltip-inner',
13
+ ".chat-item-drag-link-content-tip-text",
14
+ ".ant-tooltip-inner",
15
15
  'input[placeholder*="title"]',
16
- '.chat-title',
17
- 'h1',
18
- 'title',
16
+ ".chat-title",
17
+ "h1",
18
+ "title",
19
19
  ];
20
20
 
21
- let title = 'Qwen Chat';
21
+ let title = "Qwen Chat";
22
22
  for (const selector of titleSelectors) {
23
23
  const element = document.querySelector(selector);
24
24
  if (element) {
@@ -33,27 +33,29 @@ export class QwenParser extends ChatParser {
33
33
  const messages = [];
34
34
 
35
35
  // chat.qwen.ai uses specific class names
36
- const chatMessages = document.querySelectorAll('.qwen-chat-message');
36
+ const chatMessages = document.querySelectorAll(".qwen-chat-message");
37
37
 
38
38
  chatMessages.forEach((message) => {
39
- const isUser = message.classList.contains('qwen-chat-message-user');
40
- const role = isUser ? 'User' : 'Qwen';
39
+ const isUser = message.classList.contains("qwen-chat-message-user");
40
+ const role = isUser ? "User" : "Qwen";
41
41
 
42
- let content = '';
42
+ let content = "";
43
43
  let attachments = [];
44
44
 
45
45
  if (isUser) {
46
46
  // Extract attachments first
47
- const fileItems = message.querySelectorAll('.index-module__file-message-document___OjWnc');
47
+ const fileItems = message.querySelectorAll(
48
+ ".index-module__file-message-document___OjWnc",
49
+ );
48
50
  fileItems.forEach((item) => {
49
- const fileNameEl = item.querySelector('.fileitem-file-name-text');
50
- const fileExtEl = item.querySelector('.fileitem-file-name-ext');
51
- const fileSizeEl = item.querySelector('.fileitem-file-size span');
51
+ const fileNameEl = item.querySelector(".fileitem-file-name-text");
52
+ const fileExtEl = item.querySelector(".fileitem-file-name-ext");
53
+ const fileSizeEl = item.querySelector(".fileitem-file-size span");
52
54
 
53
55
  if (fileNameEl && fileExtEl) {
54
56
  const fileName = fileNameEl.textContent.trim();
55
57
  const fileExt = fileExtEl.textContent.trim();
56
- const fileSize = fileSizeEl ? fileSizeEl.textContent.trim() : '';
58
+ const fileSize = fileSizeEl ? fileSizeEl.textContent.trim() : "";
57
59
 
58
60
  attachments.push({
59
61
  name: fileName + fileExt,
@@ -63,13 +65,13 @@ export class QwenParser extends ChatParser {
63
65
  });
64
66
 
65
67
  // User messages are in .user-message-content
66
- const userContent = message.querySelector('.user-message-content');
68
+ const userContent = message.querySelector(".user-message-content");
67
69
  if (userContent) {
68
70
  content = convertToMarkdown(userContent);
69
71
  }
70
72
  } else {
71
73
  // Assistant messages are in .qwen-markdown elements
72
- const markdownContent = message.querySelector('.qwen-markdown');
74
+ const markdownContent = message.querySelector(".qwen-markdown");
73
75
  if (markdownContent) {
74
76
  content = convertToMarkdown(markdownContent);
75
77
  }
@@ -79,8 +81,8 @@ export class QwenParser extends ChatParser {
79
81
  if (attachments.length > 0) {
80
82
  const attachmentList = attachments
81
83
  .map((att) => `- **${att.name}** (${att.size})`)
82
- .join('\n');
83
- content = content + '\n\n**Attachments:**\n' + attachmentList;
84
+ .join("\n");
85
+ content = content + "\n\n**Attachments:**\n" + attachmentList;
84
86
  }
85
87
 
86
88
  if (content && content.trim()) {
@@ -89,9 +91,11 @@ export class QwenParser extends ChatParser {
89
91
  });
90
92
 
91
93
  const currentUrl =
92
- typeof window !== 'undefined' && window.location ? window.location.href || '' : '';
94
+ typeof window !== "undefined" && window.location
95
+ ? window.location.href || ""
96
+ : "";
93
97
  const metadata = {
94
- Source: 'Qwen',
98
+ Source: "Qwen",
95
99
  Date: new Date().toLocaleString(),
96
100
  Link: currentUrl,
97
101
  };
package/ai/z_ai.js CHANGED
@@ -1,69 +1,73 @@
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 ZAiParser extends ChatParser {
5
- name = 'Z.ai';
5
+ name = "Z.ai";
6
6
  isAvailable(url) {
7
- return url.includes('chat.z.ai');
7
+ return url.includes("chat.z.ai");
8
8
  }
9
9
 
10
10
  async parse() {
11
- let title = '';
12
- const titleEl = document.querySelector('title');
11
+ let title = "";
12
+ const titleEl = document.querySelector("title");
13
13
  if (titleEl) {
14
- title = titleEl.textContent.trim().replace(/\s+/g, ' ');
14
+ title = titleEl.textContent.trim().replace(/\s+/g, " ");
15
15
  }
16
16
  if (!title && document.title) {
17
- title = document.title.trim().replace(/\s+/g, ' ');
17
+ title = document.title.trim().replace(/\s+/g, " ");
18
18
  }
19
- title = title || 'Z.ai Chat';
19
+ title = title || "Z.ai Chat";
20
20
  const messages = [];
21
21
 
22
22
  // Selectors for z.ai messages
23
- const userSelector = '.chat-user';
24
- const assistantSelector = '.chat-assistant';
23
+ const userSelector = ".chat-user";
24
+ const assistantSelector = ".chat-assistant";
25
25
 
26
26
  // We'll traverse the DOM to find these in order
27
- const allElements = document.querySelectorAll(`${userSelector}, ${assistantSelector}`);
27
+ const allElements = document.querySelectorAll(
28
+ `${userSelector}, ${assistantSelector}`,
29
+ );
28
30
 
29
31
  allElements.forEach((el) => {
30
- let role = 'Unknown';
32
+ let role = "Unknown";
31
33
  let contentEl = null;
32
34
 
33
35
  if (el.matches(userSelector)) {
34
- role = 'User';
36
+ role = "User";
35
37
  // Select user message text block (excluding edit/copy buttons)
36
- contentEl = el.querySelector('div.relative.overflow-hidden') || el;
38
+ contentEl = el.querySelector("div.relative.overflow-hidden") || el;
37
39
  } else if (el.matches(assistantSelector)) {
38
- role = 'Z.ai';
40
+ role = "Z.ai";
39
41
  // Select assistant message content wrapper (excluding copy/regenerate buttons)
40
42
  const rawContentEl =
41
- el.querySelector('#response-content-container') ||
42
- el.querySelector('.markdown-prose') ||
43
+ el.querySelector("#response-content-container") ||
44
+ el.querySelector(".markdown-prose") ||
43
45
  el;
44
46
  const contentElClone = rawContentEl.cloneNode(true);
45
47
 
46
48
  // Preprocess CodeMirror 6 code blocks into standard HTML <pre><code> structures
47
- contentElClone.querySelectorAll('.cm-editor').forEach((cmEditor) => {
49
+ contentElClone.querySelectorAll(".cm-editor").forEach((cmEditor) => {
48
50
  // Detect language from class names of the parent language container
49
51
  const languageWrapper = cmEditor.closest('[class*="language-"]');
50
- let language = '';
52
+ let language = "";
51
53
  if (languageWrapper) {
52
54
  const classList = Array.from(languageWrapper.classList);
53
- const langClass = classList.find((cls) => cls.startsWith('language-'));
55
+ const langClass = classList.find((cls) =>
56
+ cls.startsWith("language-"),
57
+ );
54
58
  if (langClass) {
55
- language = langClass.replace('language-', '');
59
+ language = langClass.replace("language-", "");
56
60
  }
57
61
  }
58
62
 
59
63
  // Extract the lines from CodeMirror editor view
60
- const lines = Array.from(cmEditor.querySelectorAll('.cm-line'));
61
- const codeText = lines.map((line) => line.textContent).join('\n');
64
+ const lines = Array.from(cmEditor.querySelectorAll(".cm-line"));
65
+ const codeText = lines.map((line) => line.textContent).join("\n");
62
66
 
63
67
  // Create new pre and code tags using the clone's owner document context
64
68
  const ownerDoc = cmEditor.ownerDocument || document;
65
- const pre = ownerDoc.createElement('pre');
66
- const code = ownerDoc.createElement('code');
69
+ const pre = ownerDoc.createElement("pre");
70
+ const code = ownerDoc.createElement("code");
67
71
  if (language) {
68
72
  code.className = `language-${language}`;
69
73
  }
@@ -89,9 +93,11 @@ export class ZAiParser extends ChatParser {
89
93
  });
90
94
 
91
95
  const currentUrl =
92
- typeof window !== 'undefined' && window.location ? window.location.href || '' : '';
96
+ typeof window !== "undefined" && window.location
97
+ ? window.location.href || ""
98
+ : "";
93
99
  const metadata = {
94
- Source: 'Z.ai',
100
+ Source: "Z.ai",
95
101
  Date: new Date().toLocaleString(),
96
102
  Link: currentUrl,
97
103
  };
@@ -1,19 +1,21 @@
1
- import { AI_CHAT_DOMAINS, URL_PATTERNS } from './domains.js';
2
- import { ChatGPTParser } from '../ai/chatgpt.js';
3
- import { ClaudeParser } from '../ai/claude.js';
4
- import { GeminiParser } from '../ai/gemini.js';
5
- import { CopilotParser } from '../ai/copilot.js';
6
- import { DeepSeekParser } from '../ai/deepseek.js';
7
- import { MetaParser } from '../ai/meta.js';
8
- import { MistralParser } from '../ai/mistral.js';
9
- import { PerplexityParser } from '../ai/perplexity.js';
10
- import { QwenParser } from '../ai/qwen.js';
11
- import { LumoParser } from '../ai/lumo.js';
12
- import { ZAiParser } from '../ai/z_ai.js';
13
- import { GoogleAIStudioParser } from '../ai/google_ai_studio.js';
14
- import { NotebookLMParser } from '../ai/notebooklm.js';
15
- import { GoogleSearchAIParser } from '../ai/google_search_ai.js';
16
- import { GeminiCloudAssistParser } from '../ai/gemini_cloud_assist.js';
1
+ import { AI_CHAT_DOMAINS, URL_PATTERNS } from "./domains.js";
2
+ import { ChatGPTParser } from "../ai/chatgpt.js";
3
+ import { ClaudeParser } from "../ai/claude.js";
4
+ import { GeminiParser } from "../ai/gemini.js";
5
+ import { CopilotParser } from "../ai/copilot.js";
6
+ import { DeepSeekParser } from "../ai/deepseek.js";
7
+ import { MetaParser } from "../ai/meta.js";
8
+ import { MistralParser } from "../ai/mistral.js";
9
+ import { PerplexityParser } from "../ai/perplexity.js";
10
+ import { QwenParser } from "../ai/qwen.js";
11
+ import { LumoParser } from "../ai/lumo.js";
12
+ import { ZAiParser } from "../ai/z_ai.js";
13
+ import { GoogleAIStudioParser } from "../ai/google_ai_studio.js";
14
+ import { NotebookLMParser } from "../ai/notebooklm.js";
15
+ import { GoogleSearchAIParser } from "../ai/google_search_ai.js";
16
+ import { GeminiCloudAssistParser } from "../ai/gemini_cloud_assist.js";
17
+ import { JoylandParser } from "../ai/joyland.js";
18
+ import { ChubParser } from "../ai/chub.js";
17
19
 
18
20
  /**
19
21
  * Ordered list of parsers. First match wins.
@@ -35,6 +37,8 @@ export const parsers = [
35
37
  new NotebookLMParser(),
36
38
  new GoogleSearchAIParser(),
37
39
  new GeminiCloudAssistParser(),
40
+ new JoylandParser(),
41
+ new ChubParser(),
38
42
  ];
39
43
 
40
44
  /**
@@ -56,7 +60,7 @@ export function detectPlatform(url) {
56
60
  if (domainMatch) {
57
61
  const parser = parsers.find((p) => p.isAvailable(url));
58
62
  if (parser) {
59
- return { type: 'ai-chat', platform: parser.getPlatformName(), parser };
63
+ return { type: "ai-chat", platform: parser.getPlatformName(), parser };
60
64
  }
61
65
  }
62
66
  } catch {
@@ -68,7 +72,7 @@ export function detectPlatform(url) {
68
72
  if (pattern.test(url)) {
69
73
  const parser = parsers.find((p) => p.isAvailable(url));
70
74
  if (parser) {
71
- return { type: 'ai-chat', platform: parser.getPlatformName(), parser };
75
+ return { type: "ai-chat", platform: parser.getPlatformName(), parser };
72
76
  }
73
77
  }
74
78
  }
@@ -89,7 +93,11 @@ export function isAiChatUrl(url) {
89
93
  try {
90
94
  const parsed = new URL(url);
91
95
  const hostname = parsed.hostname.toLowerCase();
92
- if (AI_CHAT_DOMAINS.some((domain) => hostname === domain || hostname.endsWith(`.${domain}`))) {
96
+ if (
97
+ AI_CHAT_DOMAINS.some(
98
+ (domain) => hostname === domain || hostname.endsWith(`.${domain}`),
99
+ )
100
+ ) {
93
101
  return true;
94
102
  }
95
103
  } catch {
@@ -4,21 +4,24 @@
4
4
  */
5
5
 
6
6
  export const AI_CHAT_DOMAINS = [
7
- 'chatgpt.com',
8
- 'claude.ai',
9
- 'gemini.google.com',
10
- 'chat.deepseek.com',
11
- 'perplexity.ai',
12
- 'chat.qwen.ai',
13
- 'qwen.ai',
14
- 'chat.mistral.ai',
15
- 'copilot.microsoft.com',
16
- 'lumo.proton.me',
17
- 'meta.ai',
18
- 'aistudio.google.com',
19
- 'notebooklm.google.com',
20
- 'notebook.google.com',
21
- 'chat.z.ai',
7
+ "chatgpt.com",
8
+ "claude.ai",
9
+ "gemini.google.com",
10
+ "chat.deepseek.com",
11
+ "perplexity.ai",
12
+ "chat.qwen.ai",
13
+ "qwen.ai",
14
+ "chat.mistral.ai",
15
+ "copilot.microsoft.com",
16
+ "lumo.proton.me",
17
+ "meta.ai",
18
+ "aistudio.google.com",
19
+ "notebooklm.google.com",
20
+ "notebook.google.com",
21
+ "chat.z.ai",
22
+ "joyland.ai",
23
+ "chub.ai",
24
+ "characterhub.org",
22
25
  ];
23
26
 
24
27
  /**
@@ -27,19 +30,24 @@ export const AI_CHAT_DOMAINS = [
27
30
  */
28
31
  export const URL_PATTERNS = [
29
32
  {
30
- test: (url) => url.includes('copilot.microsoft.com') || url.includes('copilot.com') ||
31
- url.includes('copilot.cloud.microsoft') || url.includes('m365.cloud.microsoft') ||
32
- url.includes('m365.microsoft.com') || url.includes('bing.com/chat') ||
33
- url.includes('bing.com/copilot') || url.includes('bing.com/copilotsearch') ||
34
- url.includes('edgeservices.bing.com'),
35
- platform: 'copilot',
33
+ test: (url) =>
34
+ url.includes("copilot.microsoft.com") ||
35
+ url.includes("copilot.com") ||
36
+ url.includes("copilot.cloud.microsoft") ||
37
+ url.includes("m365.cloud.microsoft") ||
38
+ url.includes("m365.microsoft.com") ||
39
+ url.includes("bing.com/chat") ||
40
+ url.includes("bing.com/copilot") ||
41
+ url.includes("bing.com/copilotsearch") ||
42
+ url.includes("edgeservices.bing.com"),
43
+ platform: "copilot",
36
44
  },
37
45
  {
38
46
  test: (url) => /google\.[a-z.]+\/search/.test(url),
39
- platform: 'google-search-ai',
47
+ platform: "google-search-ai",
40
48
  },
41
49
  {
42
- test: (url) => url.includes('console.cloud.google.com/gemini'),
43
- platform: 'gemini-cloud-assist',
50
+ test: (url) => url.includes("console.cloud.google.com/gemini"),
51
+ platform: "gemini-cloud-assist",
44
52
  },
45
53
  ];