zerochat 7.3.0__py3-none-any.whl

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.
Files changed (130) hide show
  1. zerochat-7.3.0.dist-info/METADATA +70 -0
  2. zerochat-7.3.0.dist-info/RECORD +130 -0
  3. zerochat-7.3.0.dist-info/WHEEL +5 -0
  4. zerochat-7.3.0.dist-info/entry_points.txt +2 -0
  5. zerochat-7.3.0.dist-info/licenses/LICENSE +34 -0
  6. zerochat-7.3.0.dist-info/top_level.txt +2 -0
  7. zerochat.py +2004 -0
  8. zerochat_runtime/__init__.py +1 -0
  9. zerochat_runtime/assets/css/base.css +199 -0
  10. zerochat_runtime/assets/css/components/composer.css +1258 -0
  11. zerochat_runtime/assets/css/components/debug.css +777 -0
  12. zerochat_runtime/assets/css/components/header.css +97 -0
  13. zerochat_runtime/assets/css/components/icons.css +39 -0
  14. zerochat_runtime/assets/css/components/markdown.css +685 -0
  15. zerochat_runtime/assets/css/components/messages.css +473 -0
  16. zerochat_runtime/assets/css/components/modals.css +1319 -0
  17. zerochat_runtime/assets/css/components/sidebar.css +622 -0
  18. zerochat_runtime/assets/css/components/tools.css +1223 -0
  19. zerochat_runtime/assets/css/layout.css +171 -0
  20. zerochat_runtime/assets/css/print.css +120 -0
  21. zerochat_runtime/assets/css/styles.css +18 -0
  22. zerochat_runtime/assets/css/theme-overrides.css +555 -0
  23. zerochat_runtime/assets/css/tokens.css +165 -0
  24. zerochat_runtime/assets/help/architecture.html +247 -0
  25. zerochat_runtime/assets/help/composer.html +134 -0
  26. zerochat_runtime/assets/help/conversations.html +105 -0
  27. zerochat_runtime/assets/help/debug.html +108 -0
  28. zerochat_runtime/assets/help/en/architecture.html +247 -0
  29. zerochat_runtime/assets/help/en/composer.html +134 -0
  30. zerochat_runtime/assets/help/en/conversations.html +104 -0
  31. zerochat_runtime/assets/help/en/debug.html +105 -0
  32. zerochat_runtime/assets/help/en/gemini-free.html +39 -0
  33. zerochat_runtime/assets/help/en/index.html +191 -0
  34. zerochat_runtime/assets/help/en/learning.html +71 -0
  35. zerochat_runtime/assets/help/en/mcp.html +143 -0
  36. zerochat_runtime/assets/help/en/openrouter-free.html +39 -0
  37. zerochat_runtime/assets/help/en/profiles.html +173 -0
  38. zerochat_runtime/assets/help/en/rag.html +130 -0
  39. zerochat_runtime/assets/help/en/reasoning-telemetry.html +108 -0
  40. zerochat_runtime/assets/help/en/tools-agent.html +133 -0
  41. zerochat_runtime/assets/help/en/webllm.html +355 -0
  42. zerochat_runtime/assets/help/gemini-free.html +39 -0
  43. zerochat_runtime/assets/help/help.css +604 -0
  44. zerochat_runtime/assets/help/help.js +51 -0
  45. zerochat_runtime/assets/help/index.html +194 -0
  46. zerochat_runtime/assets/help/learning.html +71 -0
  47. zerochat_runtime/assets/help/mcp.html +143 -0
  48. zerochat_runtime/assets/help/openrouter-free.html +39 -0
  49. zerochat_runtime/assets/help/profiles.html +174 -0
  50. zerochat_runtime/assets/help/rag.html +130 -0
  51. zerochat_runtime/assets/help/reasoning-telemetry.html +111 -0
  52. zerochat_runtime/assets/help/tools-agent.html +133 -0
  53. zerochat_runtime/assets/help/webllm.html +359 -0
  54. zerochat_runtime/assets/js/agent-core.js +1553 -0
  55. zerochat_runtime/assets/js/api.js +844 -0
  56. zerochat_runtime/assets/js/app.js +2558 -0
  57. zerochat_runtime/assets/js/attachments.js +220 -0
  58. zerochat_runtime/assets/js/charts.js +338 -0
  59. zerochat_runtime/assets/js/chat-engine.js +566 -0
  60. zerochat_runtime/assets/js/config-store.js +207 -0
  61. zerochat_runtime/assets/js/context-manager.js +584 -0
  62. zerochat_runtime/assets/js/conversation-service.js +484 -0
  63. zerochat_runtime/assets/js/cookies.js +688 -0
  64. zerochat_runtime/assets/js/data-reset-service.js +136 -0
  65. zerochat_runtime/assets/js/debug.js +508 -0
  66. zerochat_runtime/assets/js/defaults.js +16 -0
  67. zerochat_runtime/assets/js/export.js +179 -0
  68. zerochat_runtime/assets/js/file-parser.js +2088 -0
  69. zerochat_runtime/assets/js/generation-controller.js +417 -0
  70. zerochat_runtime/assets/js/i18n.js +1483 -0
  71. zerochat_runtime/assets/js/icons.js +156 -0
  72. zerochat_runtime/assets/js/ingestionEngine.js +436 -0
  73. zerochat_runtime/assets/js/markdown.js +588 -0
  74. zerochat_runtime/assets/js/mcp.js +1271 -0
  75. zerochat_runtime/assets/js/message-turns.js +62 -0
  76. zerochat_runtime/assets/js/profile-backup.js +118 -0
  77. zerochat_runtime/assets/js/profile-export-bundle.js +481 -0
  78. zerochat_runtime/assets/js/profile-repository.js +213 -0
  79. zerochat_runtime/assets/js/providers-webllm.js +486 -0
  80. zerochat_runtime/assets/js/providers.js +1560 -0
  81. zerochat_runtime/assets/js/rag-index.js +295 -0
  82. zerochat_runtime/assets/js/rag-service.js +530 -0
  83. zerochat_runtime/assets/js/rag-ui.js +997 -0
  84. zerochat_runtime/assets/js/ragStorage.js +676 -0
  85. zerochat_runtime/assets/js/sandbox.js +449 -0
  86. zerochat_runtime/assets/js/state.js +704 -0
  87. zerochat_runtime/assets/js/storage-db.js +144 -0
  88. zerochat_runtime/assets/js/tool-cards.js +330 -0
  89. zerochat_runtime/assets/js/tool-security.js +653 -0
  90. zerochat_runtime/assets/js/tools/README.md +50 -0
  91. zerochat_runtime/assets/js/tools/builtin/agent-checkpoint.tool.js +212 -0
  92. zerochat_runtime/assets/js/tools/builtin/download-pdf.tool.js +111 -0
  93. zerochat_runtime/assets/js/tools/builtin/execute-javascript.tool.js +219 -0
  94. zerochat_runtime/assets/js/tools/builtin/fetch-web-page.tool.js +112 -0
  95. zerochat_runtime/assets/js/tools/builtin/list-documents.tool.js +117 -0
  96. zerochat_runtime/assets/js/tools/builtin/read-knowledge-chunk.tool.js +129 -0
  97. zerochat_runtime/assets/js/tools/builtin/read-knowledge-image.tool.js +95 -0
  98. zerochat_runtime/assets/js/tools/builtin/render-chart.tool.js +90 -0
  99. zerochat_runtime/assets/js/tools/builtin/search-knowledge-base.tool.js +124 -0
  100. zerochat_runtime/assets/js/tools/builtin/search-web.tool.js +125 -0
  101. zerochat_runtime/assets/js/tools/tool-manifest.js +53 -0
  102. zerochat_runtime/assets/js/tools/tool-runtime.js +77 -0
  103. zerochat_runtime/assets/js/ui-composer.js +319 -0
  104. zerochat_runtime/assets/js/ui-conversation.js +655 -0
  105. zerochat_runtime/assets/js/ui-dialogs.js +132 -0
  106. zerochat_runtime/assets/js/ui-generation-status.js +119 -0
  107. zerochat_runtime/assets/js/ui-inspector.js +804 -0
  108. zerochat_runtime/assets/js/ui-mcp.js +604 -0
  109. zerochat_runtime/assets/js/ui-profiles.js +824 -0
  110. zerochat_runtime/assets/js/ui-reasoning.js +146 -0
  111. zerochat_runtime/assets/js/ui-settings.js +825 -0
  112. zerochat_runtime/assets/js/ui-shell.js +215 -0
  113. zerochat_runtime/assets/js/ui-sidebar.js +429 -0
  114. zerochat_runtime/assets/js/ui-telemetry.js +404 -0
  115. zerochat_runtime/assets/js/ui-transfer.js +262 -0
  116. zerochat_runtime/assets/js/utils.js +216 -0
  117. zerochat_runtime/assets/js/vendor/orama.browser.js +11 -0
  118. zerochat_runtime/assets/js/web-browser.js +569 -0
  119. zerochat_runtime/assets/js/web-search.js +519 -0
  120. zerochat_runtime/assets/manifest.webmanifest +20 -0
  121. zerochat_runtime/assets/services/dummy_mcp/dummy_mcp_server.py +47 -0
  122. zerochat_runtime/assets/services/dummy_mcp/service.json +22 -0
  123. zerochat_runtime/assets/services/lsp/installer.json +9 -0
  124. zerochat_runtime/assets/services/lsp/service.json +22 -0
  125. zerochat_runtime/assets/services/memory/installer.json +9 -0
  126. zerochat_runtime/assets/services/memory/service.json +24 -0
  127. zerochat_runtime/assets/services/playwright/installer.json +10 -0
  128. zerochat_runtime/assets/services/playwright/service.json +39 -0
  129. zerochat_runtime/assets/sw.js +164 -0
  130. zerochat_runtime/assets/zerochat.html +671 -0
@@ -0,0 +1,125 @@
1
+ /** Tool autocontenida: search_web. */
2
+ (function (root, factory) {
3
+ if (typeof exports === 'object' && typeof module !== 'undefined') module.exports = factory();
4
+ else root.ChatBuiltinSearchWebTool = factory();
5
+ })(typeof self !== 'undefined' ? self : this, function () {
6
+ 'use strict';
7
+
8
+ const definition = {
9
+ name: 'search_web',
10
+ description: 'Searches the internet in real time for up-to-date information, news, articles, and web links using DuckDuckGo.',
11
+ parameters: {
12
+ type: 'object',
13
+ properties: { query: { type: 'string', description: 'Search terms or query (e.g. "INE population Ceuta census", "DeepSeek R1").' } },
14
+ required: ['query']
15
+ }
16
+ };
17
+
18
+ function getQuery(args) {
19
+ return args?.query || args?.q || args?.search || args?.keyword || args?.term || args?.input || (typeof args === 'string' ? args : '');
20
+ }
21
+
22
+ function createCardWrapper(ui) {
23
+ if (ui?.createCardWrapper) return ui.createCardWrapper();
24
+ const doc = ui?.document || (typeof document !== 'undefined' ? document : null);
25
+ if (!doc) return null;
26
+ const cardDiv = doc.createElement('div');
27
+ cardDiv.className = 'tool-card-wrapper';
28
+ return cardDiv;
29
+ }
30
+
31
+ const SEARCH_ICON_SVG = '<svg class="ui-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>';
32
+ const LINK_ICON_SVG = '<svg class="ui-icon" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg>';
33
+
34
+ function getHtmlSafety() {
35
+ const Utils = (typeof window !== 'undefined' && window.ChatUtils)
36
+ || (typeof require !== 'undefined' ? (() => { try { return require('../../utils.js'); } catch (_) { return null; } })() : null);
37
+ if (Utils) return { ...Utils, renderMarkdown: value => Utils.escapeHtml(value) };
38
+ return { escapeHtml: () => '', sanitizeUrl: () => '#', renderMarkdown: () => '' };
39
+ }
40
+
41
+ function createLiveCard(args, ui) {
42
+ const cardDiv = createCardWrapper(ui);
43
+ if (!cardDiv) return null;
44
+ const Markdown = ui?.markdown || (typeof window !== 'undefined' && window.ChatMarkdown) || getHtmlSafety();
45
+ const t = ui?.t || ((key) => key);
46
+ const spinner = ui?.SPINNER_SVG || '';
47
+ const chevron = ui?.CHEVRON_SVG || '';
48
+ cardDiv.innerHTML = `<div class="web-search-card"><div class="search-card-header"><div class="search-card-title"><span>${SEARCH_ICON_SVG}</span><span>${t('tool_search_title') || 'Búsqueda en Internet'}</span></div><div class="tool-card-header-actions"><span class="search-card-badge status-loading">${spinner} <span>${t('tool_badge_searching') || 'Buscando...'}</span></span><button type="button" class="btn-tool-collapse" title="${t('tool_btn_collapse') || 'Minimizar'}">${chevron}</button></div></div><div class="tool-card-collapsible-body"><div class="search-query-section"><div class="section-label">${t('tool_search_query')}</div><div class="query-badge">${SEARCH_ICON_SVG} <strong>${Markdown.escapeHtml(getQuery(args))}</strong></div></div><div class="search-results-section"><div class="section-label search-sources-label">${t('tool_search_searching') || 'Buscando fuentes...'}</div><div class="search-results-list tool-loading-placeholder">${spinner} <span>${t('tool_loading_search') || 'Consultando motores de búsqueda...'}</span></div></div></div></div>`;
49
+ return cardDiv;
50
+ }
51
+
52
+ function updateLiveCard(cardDiv, _args, result = {}, elapsedMs = 0, ui) {
53
+ if (!cardDiv) return;
54
+ const Markdown = ui?.markdown || (typeof window !== 'undefined' && window.ChatMarkdown) || getHtmlSafety();
55
+ const t = ui?.t || ((key) => key);
56
+ const checkSvg = ui?.CHECK_SVG || '';
57
+ const errorSvg = ui?.ERROR_SVG || '';
58
+ const isSuccess = result?.success !== false && !result?.error;
59
+ const count = result?.count || (Array.isArray(result?.results) ? result.results.length : 0);
60
+ const badge = cardDiv.querySelector('.search-card-badge');
61
+ if (badge) {
62
+ badge.className = `search-card-badge ${isSuccess ? 'status-success' : 'status-error'}`;
63
+ badge.innerHTML = isSuccess
64
+ ? `${checkSvg} <span>${count} fuentes (${elapsedMs || 0}ms)</span>`
65
+ : `${errorSvg} <span>Error búsqueda (${elapsedMs || 0}ms)</span>`;
66
+ }
67
+ const label = cardDiv.querySelector('.search-sources-label');
68
+ if (label) label.textContent = t('tool_search_sources_label') || 'Fuentes y resultados encontrados:';
69
+ const list = cardDiv.querySelector('.search-results-list');
70
+ if (!list) return;
71
+ if (result?.results?.length) {
72
+ list.innerHTML = result.results.map(item => `<div class="search-result-item"><div><a href="${Markdown.sanitizeUrl(item.url)}" target="_blank" rel="noopener noreferrer">${LINK_ICON_SVG} ${Markdown.escapeHtml(item.title)}</a> <small style="opacity:0.75;">(${Markdown.escapeHtml(item.source || 'web')})</small></div>${item.snippet ? `<div class="search-result-snippet">${Markdown.escapeHtml(item.snippet)}</div>` : ''}</div>`).join('');
73
+ } else if (result?.markdown) list.innerHTML = `<div class="search-result-snippet">${Markdown.renderMarkdown(result.markdown)}</div>`;
74
+ else list.innerHTML = `<div class="search-result-snippet"><em>${t('tool_search_empty') || 'No se encontraron resultados relevantes.'}</em></div>`;
75
+ }
76
+
77
+ function renderHistoricalCard(args, toolMessage, ui) {
78
+ const cardDiv = createLiveCard(args, ui);
79
+ if (!cardDiv) return null;
80
+ let result = {};
81
+ if (toolMessage?.content) {
82
+ try { result = JSON.parse(toolMessage.content); } catch (e) { result = { markdown: toolMessage.content }; }
83
+ }
84
+ updateLiveCard(cardDiv, args, result, 0, ui);
85
+ return cardDiv;
86
+ }
87
+
88
+ function createTool(Tool) {
89
+ if (typeof Tool !== 'function') throw new Error('La clase Tool es necesaria para crear search_web.');
90
+ return new Tool({
91
+ id: definition.name,
92
+ definition,
93
+ aliases: ['searchweb', 'web_search', 'duckduckgo_search', 'duckduckgo', 'search_internet', 'internet_search', 'search'],
94
+ category: 'web',
95
+ metadata: { icon: 'search', label: definition.name },
96
+ settings: {
97
+ titleKey: 'agent_search_title', titleFallback: 'Búsqueda en DuckDuckGo en Tiempo Real',
98
+ descKey: 'agent_search_desc', descFallback: 'Permite al modelo invocar search_web para buscar información actualizada, definiciones, noticias y enlaces web mediante la API de DuckDuckGo.',
99
+ icon: 'search', defaultEnabled: true, showInSettings: true
100
+ },
101
+ promptGuide: () => '- `search_web(query="...")`: Searches up-to-date information, news, articles, and links on the internet using DuckDuckGo.',
102
+ execute: async (args, context = {}) => {
103
+ const WebSearch = context.services?.webSearch;
104
+ if (!WebSearch || !WebSearch.search) return { success: false, error: 'Módulo WebSearch no disponible.' };
105
+ return WebSearch.search(getQuery(args), context.language || context.lang || 'es');
106
+ },
107
+ result: {
108
+ toModel: (_args, result) => result?.markdown || JSON.stringify(result || {}),
109
+ toMarkdown: (args, result) => {
110
+ const resultText = result?.markdown || JSON.stringify(result || {});
111
+ return `> 🔍 **search_web** (${result?.count || 0} fuentes)\n> Query: "${args.query || ''}"\n> \`\`\`markdown\n> ${resultText.split('\n').join('\n> ')}\n> \`\`\``;
112
+ }
113
+ },
114
+ displayMode: 'collapsed',
115
+ view: { id: definition.name, displayMode: 'collapsed', createLiveCard, updateLiveCard, renderHistoricalCard }
116
+ });
117
+ }
118
+
119
+ const toolModule = { id: definition.name, definition, displayMode: 'collapsed', createTool, getQuery, view: { id: definition.name, displayMode: 'collapsed', createLiveCard, updateLiveCard, renderHistoricalCard } };
120
+ let manifestApi = null;
121
+ if (typeof window !== 'undefined' && window.ChatToolManifest) manifestApi = window.ChatToolManifest;
122
+ else if (typeof require !== 'undefined') { try { manifestApi = require('../tool-manifest.js'); } catch (e) {} }
123
+ if (manifestApi?.builtin && !manifestApi.builtin.has(toolModule.id)) manifestApi.builtin.register(toolModule);
124
+ return toolModule;
125
+ });
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Registro de módulos de tools autocontenidas.
3
+ *
4
+ * Los módulos futuros se registrarán aquí al cargarse; durante la transición
5
+ * las tools existentes continúan registrándose mediante BuiltinToolProvider.
6
+ */
7
+ (function (root, factory) {
8
+ if (typeof exports === 'object' && typeof module !== 'undefined') {
9
+ module.exports = factory();
10
+ } else {
11
+ root.ChatToolManifest = factory();
12
+ }
13
+ })(typeof self !== 'undefined' ? self : this, function () {
14
+ 'use strict';
15
+
16
+ class ToolModuleManifest {
17
+ constructor(options = {}) {
18
+ this.id = options.id || 'builtin';
19
+ this.modules = new Map();
20
+ }
21
+
22
+ register(toolModule, options = {}) {
23
+ if (!toolModule || typeof toolModule.id !== 'string' || !toolModule.id.trim()) {
24
+ throw new Error('Un módulo de tool debe declarar un id válido.');
25
+ }
26
+ const id = toolModule.id.trim();
27
+ if (this.modules.has(id) && !options.overwrite) {
28
+ throw new Error(`El módulo de tool '${id}' ya está registrado.`);
29
+ }
30
+ this.modules.set(id, toolModule);
31
+ return toolModule;
32
+ }
33
+
34
+ has(id) {
35
+ return this.modules.has(id);
36
+ }
37
+
38
+ get(id) {
39
+ return this.modules.get(id) || null;
40
+ }
41
+
42
+ list() {
43
+ return Array.from(this.modules.values());
44
+ }
45
+ }
46
+
47
+ const builtin = new ToolModuleManifest({ id: 'builtin' });
48
+
49
+ return {
50
+ ToolModuleManifest,
51
+ builtin
52
+ };
53
+ });
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Infraestructura de ejecución para módulos de tools de ZeroChat.
3
+ *
4
+ * Expone un contexto explícito y servicios resueltos de forma perezosa para
5
+ * que cada tool autocontenida declare sólo las dependencias que consume.
6
+ */
7
+ (function (root, factory) {
8
+ if (typeof exports === 'object' && typeof module !== 'undefined') {
9
+ module.exports = factory();
10
+ } else {
11
+ root.ChatToolRuntime = factory();
12
+ }
13
+ })(typeof self !== 'undefined' ? self : this, function () {
14
+ 'use strict';
15
+
16
+ const SERVICE_DEFINITIONS = {
17
+ sandbox: { globalName: 'ChatSandbox', modulePath: '../sandbox.js' },
18
+ webSearch: { globalName: 'ChatWebSearch', modulePath: '../web-search.js' },
19
+ webBrowser: { globalName: 'ChatWebBrowser', modulePath: '../web-browser.js' },
20
+ charts: { globalName: 'ChatCharts', modulePath: '../charts.js' },
21
+ ragStorage: { globalName: 'ChatRagStorage', modulePath: '../ragStorage.js' },
22
+ ragService: { globalName: 'ChatRagService', modulePath: '../rag-service.js' },
23
+ mcp: { globalName: 'ChatMCP', modulePath: '../mcp.js' }
24
+ };
25
+
26
+ function resolveService(name) {
27
+ const definition = SERVICE_DEFINITIONS[name];
28
+ if (!definition) return null;
29
+
30
+ if (typeof window !== 'undefined') {
31
+ if (window[definition.globalName]) return window[definition.globalName];
32
+ if (definition.fallbackGlobalName && window[definition.fallbackGlobalName]) {
33
+ return window[definition.fallbackGlobalName];
34
+ }
35
+ }
36
+
37
+ if (typeof require !== 'undefined') {
38
+ try { return require(definition.modulePath); } catch (e) {}
39
+ }
40
+ return null;
41
+ }
42
+
43
+ /**
44
+ * Contenedor de servicios con resolución perezosa y sustituciones para test.
45
+ */
46
+ function createToolServices(overrides = {}) {
47
+ const services = {};
48
+ Object.keys(SERVICE_DEFINITIONS).forEach(name => {
49
+ Object.defineProperty(services, name, {
50
+ enumerable: true,
51
+ get: () => Object.prototype.hasOwnProperty.call(overrides, name)
52
+ ? overrides[name]
53
+ : resolveService(name)
54
+ });
55
+ });
56
+ return services;
57
+ }
58
+
59
+ /**
60
+ * Crea el contexto estándar recibido por execute(args, context).
61
+ */
62
+ function createToolExecutionContext(options = {}, serviceOverrides = {}) {
63
+ return {
64
+ ...options,
65
+ language: options.language || options.lang || 'es',
66
+ services: options.services || createToolServices(serviceOverrides),
67
+ config: options.config || {}
68
+ };
69
+ }
70
+
71
+ return {
72
+ SERVICE_DEFINITIONS,
73
+ resolveService,
74
+ createToolServices,
75
+ createToolExecutionContext
76
+ };
77
+ });
@@ -0,0 +1,319 @@
1
+ /**
2
+ * Módulo de Interfaz de Usuario para el Compositor de Mensajes (ZeroChat).
3
+ * Gestiona el formulario del chat, textarea con auto-ajuste de altura,
4
+ * eventos de teclado (Enter / Shift+Enter), portapapeles (imágenes), arrastrar y soltar (drag & drop),
5
+ * renderizado de adjuntos, y sincronización de controles de generación (Enviar / Parar).
6
+ */
7
+ (function (root, factory) {
8
+ if (typeof exports === 'object' && typeof module !== 'undefined') {
9
+ module.exports = factory();
10
+ } else {
11
+ root.ChatUIComposer = factory();
12
+ }
13
+ }(typeof self !== 'undefined' ? self : this, function () {
14
+ 'use strict';
15
+
16
+ function resolveDep(globalName, relPath) {
17
+ if (typeof window !== 'undefined' && window[globalName]) return window[globalName];
18
+ if (typeof globalThis !== 'undefined' && globalThis[globalName]) return globalThis[globalName];
19
+ if (typeof require !== 'undefined') {
20
+ try { return require(relPath); } catch (_) {}
21
+ }
22
+ return null;
23
+ }
24
+
25
+ function getI18n() { return resolveDep('ChatI18n', './i18n.js'); }
26
+ function getAttachments() { return resolveDep('ChatAttachments', './attachments.js'); }
27
+ function getFileParser() { return resolveDep('ChatFileParser', './file-parser.js'); }
28
+ function getDialogs() { return resolveDep('ChatDialogs', './ui-dialogs.js'); }
29
+ function getState() { return resolveDep('ChatState', './state.js'); }
30
+
31
+ function t(key, params) {
32
+ const I18n = getI18n();
33
+ return I18n?.t?.(key, params) || key;
34
+ }
35
+
36
+ let activeCleanupFns = [];
37
+ let cachedElements = null;
38
+
39
+ // field-sizing:content gestiona el auto-resize en CSS (Baseline 2024).
40
+ // Esta función actúa como fallback para navegadores sin soporte.
41
+ function autoResizeTextarea(elements) {
42
+ const els = elements || cachedElements || {};
43
+ if (typeof CSS !== 'undefined' && CSS.supports && CSS.supports('field-sizing', 'content')) return;
44
+ if (!els.userInput || !els.userInput.style) return;
45
+ if (!els.userInput.value) {
46
+ els.userInput.style.height = '';
47
+ return;
48
+ }
49
+ els.userInput.style.height = 'auto';
50
+ const newHeight = Math.min(els.userInput.scrollHeight, 160);
51
+ els.userInput.style.height = `${newHeight}px`;
52
+ }
53
+
54
+ function focusInput(elements) {
55
+ const els = elements || cachedElements || {};
56
+ if (els.userInput && typeof els.userInput.focus === 'function') {
57
+ els.userInput.focus();
58
+ }
59
+ }
60
+
61
+ function clearInput(elements) {
62
+ const els = elements || cachedElements || {};
63
+ if (els.userInput) {
64
+ els.userInput.value = '';
65
+ }
66
+ autoResizeTextarea(els);
67
+ }
68
+
69
+ function getPromptValue(elements) {
70
+ const els = elements || cachedElements || {};
71
+ return (els.userInput?.value || '').trim();
72
+ }
73
+
74
+ function setPromptValue(elements, text) {
75
+ const els = elements || cachedElements || {};
76
+ if (els.userInput) {
77
+ els.userInput.value = text || '';
78
+ }
79
+ autoResizeTextarea(els);
80
+ focusInput(els);
81
+ }
82
+
83
+ function renderAttachedFiles(elements) {
84
+ const els = elements || cachedElements || {};
85
+ const Attachments = getAttachments();
86
+ if (Attachments?.renderChips) {
87
+ Attachments.renderChips(els.attachmentsContainer, () => autoResizeTextarea(els));
88
+ }
89
+ }
90
+
91
+ function clearAttachedFiles(elements) {
92
+ const els = elements || cachedElements || {};
93
+ const Attachments = getAttachments();
94
+ if (Attachments?.clearFiles) {
95
+ Attachments.clearFiles();
96
+ renderAttachedFiles(els);
97
+ }
98
+ if (els.fileInput) els.fileInput.value = '';
99
+ }
100
+
101
+ function readFileAsText(file) {
102
+ return new Promise((resolve, reject) => {
103
+ const reader = new FileReader();
104
+ reader.onload = () => resolve(reader.result);
105
+ reader.onerror = () => reject(reader.error);
106
+ reader.readAsText(file);
107
+ });
108
+ }
109
+
110
+ async function processFiles(elements, files) {
111
+ const els = elements || cachedElements || {};
112
+ const Attachments = getAttachments();
113
+ const FileParser = getFileParser();
114
+ const Dialogs = getDialogs();
115
+ const maxBytes = Attachments?.MAX_FILE_SIZE || (50 * 1024 * 1024);
116
+
117
+ for (let i = 0; i < files.length; i++) {
118
+ const file = files[i];
119
+ if (file && typeof file.size === 'number' && file.size > maxBytes) {
120
+ if (Dialogs?.alert) {
121
+ await Dialogs.alert(t('err_file_too_large', { name: file.name, max: '50 MB' }), { type: 'error' });
122
+ }
123
+ continue;
124
+ }
125
+ try {
126
+ let parsed;
127
+ if (FileParser?.parseFile) {
128
+ parsed = await FileParser.parseFile(file);
129
+ } else {
130
+ const text = await readFileAsText(file);
131
+ parsed = {
132
+ name: file.name,
133
+ size: file.size,
134
+ type: 'text',
135
+ content: text
136
+ };
137
+ }
138
+ if (Attachments?.addFile) Attachments.addFile(parsed);
139
+ } catch (err) {
140
+ console.error(`Error processing file ${file.name}:`, err);
141
+ if (Dialogs?.alert) {
142
+ Dialogs.alert(t('err_file_process', { name: file.name, err: err.message || err }), { type: 'error' });
143
+ }
144
+ }
145
+ }
146
+ renderAttachedFiles(els);
147
+ focusInput(els);
148
+ }
149
+
150
+ function handlePasteEvent(e, elements) {
151
+ const els = elements || cachedElements || {};
152
+ if (!e.clipboardData || !e.clipboardData.items) return;
153
+ const items = e.clipboardData.items;
154
+ const FileParser = getFileParser();
155
+ const Attachments = getAttachments();
156
+
157
+ for (let i = 0; i < items.length; i++) {
158
+ if (items[i].type && items[i].type.startsWith('image/')) {
159
+ const file = items[i].getAsFile();
160
+ if (file) {
161
+ e.preventDefault();
162
+ if (FileParser?.parseFile) {
163
+ FileParser.parseFile(file).then(parsed => {
164
+ if (Attachments?.addFile) Attachments.addFile(parsed);
165
+ renderAttachedFiles(els);
166
+ }).catch(err => {
167
+ console.error('Error pasting image:', err);
168
+ });
169
+ }
170
+ }
171
+ }
172
+ }
173
+ }
174
+
175
+
176
+ function syncGenerationControls(elements, isGenerating, options = {}) {
177
+ const els = elements || cachedElements || {};
178
+ const generating = Boolean(isGenerating);
179
+ if (els.btnSend) els.btnSend.disabled = generating;
180
+ if (els.btnStopStream) els.btnStopStream.style.display = generating ? 'inline-flex' : 'none';
181
+ if (!generating && typeof options.clearGenerationStatus === 'function') {
182
+ options.clearGenerationStatus();
183
+ }
184
+ }
185
+
186
+ function mount(elements, { onSendMessage, onStopGeneration } = {}) {
187
+ dispose();
188
+ cachedElements = elements || {};
189
+ const els = cachedElements;
190
+
191
+ if (els.chatForm) {
192
+ const onSubmit = function (e) {
193
+ e.preventDefault();
194
+ if (typeof onSendMessage === 'function') onSendMessage();
195
+ };
196
+ els.chatForm.addEventListener('submit', onSubmit);
197
+ activeCleanupFns.push(() => els.chatForm.removeEventListener('submit', onSubmit));
198
+
199
+ const onDragOver = function (e) {
200
+ e.preventDefault();
201
+ els.chatForm.classList.add('drag-over');
202
+ };
203
+ els.chatForm.addEventListener('dragover', onDragOver);
204
+ activeCleanupFns.push(() => els.chatForm.removeEventListener('dragover', onDragOver));
205
+
206
+ const onDragLeave = function () {
207
+ els.chatForm.classList.remove('drag-over');
208
+ };
209
+ els.chatForm.addEventListener('dragleave', onDragLeave);
210
+ activeCleanupFns.push(() => els.chatForm.removeEventListener('dragleave', onDragLeave));
211
+
212
+ const onDrop = function (e) {
213
+ e.preventDefault();
214
+ els.chatForm.classList.remove('drag-over');
215
+ if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length > 0) {
216
+ processFiles(els, Array.from(e.dataTransfer.files));
217
+ }
218
+ };
219
+ els.chatForm.addEventListener('drop', onDrop);
220
+ activeCleanupFns.push(() => els.chatForm.removeEventListener('drop', onDrop));
221
+ }
222
+
223
+ if (els.userInput) {
224
+ const onKeyDown = function (e) {
225
+ if (e.key === 'Enter' && !e.shiftKey) {
226
+ e.preventDefault();
227
+ if (typeof onSendMessage === 'function') onSendMessage();
228
+ }
229
+ };
230
+ els.userInput.addEventListener('keydown', onKeyDown);
231
+ activeCleanupFns.push(() => els.userInput.removeEventListener('keydown', onKeyDown));
232
+
233
+ const onInput = function () {
234
+ autoResizeTextarea(els);
235
+ };
236
+ els.userInput.addEventListener('input', onInput);
237
+ activeCleanupFns.push(() => els.userInput.removeEventListener('input', onInput));
238
+
239
+ const onPaste = function (e) {
240
+ handlePasteEvent(e, els);
241
+ };
242
+ els.userInput.addEventListener('paste', onPaste);
243
+ activeCleanupFns.push(() => els.userInput.removeEventListener('paste', onPaste));
244
+
245
+ const onFocus = function () {
246
+ if (typeof window !== 'undefined' && window.visualViewport) {
247
+ setTimeout(() => {
248
+ if (els.userInput) {
249
+ els.userInput.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
250
+ }
251
+ }, 300);
252
+ }
253
+ };
254
+ els.userInput.addEventListener('focus', onFocus);
255
+ activeCleanupFns.push(() => els.userInput.removeEventListener('focus', onFocus));
256
+ }
257
+
258
+ if (els.btnStopStream) {
259
+ const onStop = function () {
260
+ if (typeof onStopGeneration === 'function') onStopGeneration();
261
+ };
262
+ els.btnStopStream.addEventListener('click', onStop);
263
+ activeCleanupFns.push(() => els.btnStopStream.removeEventListener('click', onStop));
264
+ }
265
+
266
+ if (els.btnAttachFile) {
267
+ const onAttach = function () {
268
+ if (els.fileInput && typeof els.fileInput.click === 'function') {
269
+ els.fileInput.click();
270
+ }
271
+ };
272
+ els.btnAttachFile.addEventListener('click', onAttach);
273
+ activeCleanupFns.push(() => els.btnAttachFile.removeEventListener('click', onAttach));
274
+ }
275
+
276
+ if (els.fileInput) {
277
+ const onChange = function (e) {
278
+ if (e.target.files && e.target.files.length > 0) {
279
+ processFiles(els, Array.from(e.target.files));
280
+ }
281
+ };
282
+ els.fileInput.addEventListener('change', onChange);
283
+ activeCleanupFns.push(() => els.fileInput.removeEventListener('change', onChange));
284
+ }
285
+
286
+ return {
287
+ autoResizeTextarea: () => autoResizeTextarea(els),
288
+ focusInput: () => focusInput(els),
289
+ clearInput: () => clearInput(els),
290
+ getPromptValue: () => getPromptValue(els),
291
+ setPromptValue: (text) => setPromptValue(els, text),
292
+ renderAttachedFiles: () => renderAttachedFiles(els),
293
+ clearAttachedFiles: () => clearAttachedFiles(els),
294
+ syncGenerationControls: (isGenerating, opts) => syncGenerationControls(els, isGenerating, opts)
295
+ };
296
+ }
297
+
298
+ function dispose() {
299
+ activeCleanupFns.forEach(fn => { try { fn(); } catch (_) {} });
300
+ activeCleanupFns = [];
301
+ cachedElements = null;
302
+ }
303
+
304
+ return {
305
+ autoResizeTextarea,
306
+ focusInput,
307
+ clearInput,
308
+ getPromptValue,
309
+ setPromptValue,
310
+ renderAttachedFiles,
311
+ clearAttachedFiles,
312
+ readFileAsText,
313
+ processFiles,
314
+ handlePasteEvent,
315
+ syncGenerationControls,
316
+ mount,
317
+ dispose
318
+ };
319
+ }));