@trustquery/browser 0.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/LICENSE +21 -0
- package/README.md +152 -0
- package/dist/trustquery.js +2904 -0
- package/dist/trustquery.js.map +1 -0
- package/package.json +49 -0
- package/src/AutoGrow.js +66 -0
- package/src/BubbleManager.js +219 -0
- package/src/CommandHandlers.js +350 -0
- package/src/CommandScanner.js +285 -0
- package/src/DropdownManager.js +592 -0
- package/src/InteractionHandler.js +225 -0
- package/src/OverlayRenderer.js +241 -0
- package/src/StyleManager.js +523 -0
- package/src/TrustQuery.js +402 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// BubbleManager - Handles hover bubble tooltips for matched words
|
|
2
|
+
|
|
3
|
+
export default class BubbleManager {
|
|
4
|
+
/**
|
|
5
|
+
* Create bubble manager
|
|
6
|
+
* @param {Object} options - Configuration
|
|
7
|
+
*/
|
|
8
|
+
constructor(options = {}) {
|
|
9
|
+
this.options = {
|
|
10
|
+
bubbleDelay: options.bubbleDelay || 200,
|
|
11
|
+
styleManager: options.styleManager || null,
|
|
12
|
+
commandHandlers: options.commandHandlers || null,
|
|
13
|
+
...options
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
this.currentBubble = null;
|
|
17
|
+
this.hoverTimeout = null;
|
|
18
|
+
|
|
19
|
+
console.log('[BubbleManager] Initialized');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Show bubble for a match element
|
|
24
|
+
* @param {HTMLElement} matchEl - Match element
|
|
25
|
+
* @param {Object} matchData - Match data
|
|
26
|
+
*/
|
|
27
|
+
showBubble(matchEl, matchData) {
|
|
28
|
+
// Remove any existing bubble
|
|
29
|
+
this.hideBubble();
|
|
30
|
+
|
|
31
|
+
// Get bubble content
|
|
32
|
+
const content = this.getBubbleContent(matchData);
|
|
33
|
+
|
|
34
|
+
if (!content) {
|
|
35
|
+
return; // No content to show
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Create bubble
|
|
39
|
+
const bubble = document.createElement('div');
|
|
40
|
+
bubble.className = 'tq-bubble';
|
|
41
|
+
|
|
42
|
+
// Add header container based on message-state
|
|
43
|
+
const messageState = matchData.intent?.handler?.['message-state'] || 'info';
|
|
44
|
+
this.createBubbleHeader(bubble, messageState);
|
|
45
|
+
|
|
46
|
+
// Add content container
|
|
47
|
+
const contentContainer = document.createElement('div');
|
|
48
|
+
contentContainer.className = 'tq-bubble-content';
|
|
49
|
+
contentContainer.innerHTML = content;
|
|
50
|
+
bubble.appendChild(contentContainer);
|
|
51
|
+
|
|
52
|
+
// Apply inline styles via StyleManager
|
|
53
|
+
if (this.options.styleManager) {
|
|
54
|
+
this.options.styleManager.applyBubbleStyles(bubble);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Add to document
|
|
58
|
+
document.body.appendChild(bubble);
|
|
59
|
+
this.currentBubble = bubble;
|
|
60
|
+
|
|
61
|
+
// Position bubble relative to match element
|
|
62
|
+
this.positionBubble(bubble, matchEl);
|
|
63
|
+
|
|
64
|
+
// Auto-hide when mouse leaves bubble
|
|
65
|
+
bubble.addEventListener('mouseleave', () => {
|
|
66
|
+
this.hideBubble();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
console.log('[BubbleManager] Bubble shown for:', matchData.text);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Create header container for bubble
|
|
74
|
+
* @param {HTMLElement} bubble - Bubble element
|
|
75
|
+
* @param {string} messageState - Message state (error, warning, info)
|
|
76
|
+
*/
|
|
77
|
+
createBubbleHeader(bubble, messageState) {
|
|
78
|
+
const headerContainer = document.createElement('div');
|
|
79
|
+
headerContainer.className = 'bubble-header-container';
|
|
80
|
+
headerContainer.setAttribute('data-type', messageState);
|
|
81
|
+
|
|
82
|
+
// Create image
|
|
83
|
+
const img = document.createElement('img');
|
|
84
|
+
img.src = `./assets/trustquery-${messageState}.svg`;
|
|
85
|
+
img.style.height = '24px';
|
|
86
|
+
img.style.width = 'auto';
|
|
87
|
+
|
|
88
|
+
// Create text span
|
|
89
|
+
const span = document.createElement('span');
|
|
90
|
+
const textMap = {
|
|
91
|
+
'error': 'TrustQuery Stop',
|
|
92
|
+
'warning': 'TrustQuery Clarify',
|
|
93
|
+
'info': 'TrustQuery Quick Link'
|
|
94
|
+
};
|
|
95
|
+
span.textContent = textMap[messageState] || 'TrustQuery';
|
|
96
|
+
|
|
97
|
+
// Append to header
|
|
98
|
+
headerContainer.appendChild(img);
|
|
99
|
+
headerContainer.appendChild(span);
|
|
100
|
+
|
|
101
|
+
// Apply styles to header via StyleManager
|
|
102
|
+
if (this.options.styleManager) {
|
|
103
|
+
this.options.styleManager.applyBubbleHeaderStyles(headerContainer, messageState);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
bubble.appendChild(headerContainer);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Hide current bubble
|
|
111
|
+
*/
|
|
112
|
+
hideBubble() {
|
|
113
|
+
if (this.currentBubble) {
|
|
114
|
+
this.currentBubble.remove();
|
|
115
|
+
this.currentBubble = null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Get bubble content using command handler
|
|
121
|
+
* @param {Object} matchData - Match data including command and intent
|
|
122
|
+
* @returns {string|null} HTML content or null
|
|
123
|
+
*/
|
|
124
|
+
getBubbleContent(matchData) {
|
|
125
|
+
// Check for new simplified format first (intent.handler.message)
|
|
126
|
+
if (matchData.intent && matchData.intent.handler) {
|
|
127
|
+
const handler = matchData.intent.handler;
|
|
128
|
+
const message = handler.message || handler['message-content'] || matchData.intent.description;
|
|
129
|
+
|
|
130
|
+
if (message) {
|
|
131
|
+
// Return just the message text - header is added separately
|
|
132
|
+
return this.escapeHtml(message);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Use command handler if available (legacy support)
|
|
137
|
+
if (this.options.commandHandlers && matchData.commandType) {
|
|
138
|
+
const content = this.options.commandHandlers.getBubbleContent(matchData.commandType, matchData);
|
|
139
|
+
if (content) {
|
|
140
|
+
return content;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Fallback to legacy method
|
|
145
|
+
const command = matchData.command || {};
|
|
146
|
+
|
|
147
|
+
if (command.content) {
|
|
148
|
+
return command.content;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (command.bubbleContent) {
|
|
152
|
+
return command.bubbleContent;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (command.description) {
|
|
156
|
+
return `<div class="tq-bubble-description">${this.escapeHtml(command.description)}</div>`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Position bubble relative to match element
|
|
164
|
+
* @param {HTMLElement} bubble - Bubble element
|
|
165
|
+
* @param {HTMLElement} matchEl - Match element
|
|
166
|
+
*/
|
|
167
|
+
positionBubble(bubble, matchEl) {
|
|
168
|
+
const rect = matchEl.getBoundingClientRect();
|
|
169
|
+
const bubbleRect = bubble.getBoundingClientRect();
|
|
170
|
+
|
|
171
|
+
// Position above match by default (since input is at bottom)
|
|
172
|
+
let top = rect.top + window.scrollY - bubbleRect.height - 8;
|
|
173
|
+
let left = rect.left + window.scrollX;
|
|
174
|
+
|
|
175
|
+
// If bubble goes off top edge, position below instead
|
|
176
|
+
if (top < window.scrollY) {
|
|
177
|
+
top = rect.bottom + window.scrollY + 8;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Check if bubble goes off right edge
|
|
181
|
+
if (left + bubbleRect.width > window.innerWidth) {
|
|
182
|
+
left = window.innerWidth - bubbleRect.width - 10;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
bubble.style.top = `${top}px`;
|
|
186
|
+
bubble.style.left = `${left}px`;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Escape HTML
|
|
191
|
+
* @param {string} text - Text to escape
|
|
192
|
+
* @returns {string} Escaped text
|
|
193
|
+
*/
|
|
194
|
+
escapeHtml(text) {
|
|
195
|
+
const div = document.createElement('div');
|
|
196
|
+
div.textContent = text;
|
|
197
|
+
return div.innerHTML;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Cleanup
|
|
202
|
+
*/
|
|
203
|
+
cleanup() {
|
|
204
|
+
this.hideBubble();
|
|
205
|
+
|
|
206
|
+
if (this.hoverTimeout) {
|
|
207
|
+
clearTimeout(this.hoverTimeout);
|
|
208
|
+
this.hoverTimeout = null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Destroy
|
|
214
|
+
*/
|
|
215
|
+
destroy() {
|
|
216
|
+
this.cleanup();
|
|
217
|
+
console.log('[BubbleManager] Destroyed');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
// CommandHandlers - Handler registry for different command types
|
|
2
|
+
// Each command type has specific styling and behavior
|
|
3
|
+
|
|
4
|
+
export class CommandHandlerRegistry {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.handlers = new Map();
|
|
7
|
+
this.registerDefaultHandlers();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Register default handlers
|
|
12
|
+
*/
|
|
13
|
+
registerDefaultHandlers() {
|
|
14
|
+
this.register('not-allowed', new NotAllowedHandler());
|
|
15
|
+
this.register('show-warning', new ShowWarningHandler());
|
|
16
|
+
this.register('user-select-oneOf-and-warn', new UserSelectWithWarningHandler());
|
|
17
|
+
this.register('user-select-oneOf', new UserSelectHandler());
|
|
18
|
+
this.register('api-json-table', new ApiJsonTableHandler());
|
|
19
|
+
this.register('api-md-table', new ApiMdTableHandler());
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Register a handler
|
|
24
|
+
* @param {string} commandType - Command type (e.g., 'not-allowed', 'show-warning')
|
|
25
|
+
* @param {CommandHandler} handler - Handler instance
|
|
26
|
+
*/
|
|
27
|
+
register(commandType, handler) {
|
|
28
|
+
this.handlers.set(commandType, handler);
|
|
29
|
+
console.log(`[CommandHandlerRegistry] Registered handler: ${commandType}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Get handler for a command type
|
|
34
|
+
* @param {string} commandType - Command type
|
|
35
|
+
* @returns {CommandHandler|null} Handler or null
|
|
36
|
+
*/
|
|
37
|
+
getHandler(commandType) {
|
|
38
|
+
return this.handlers.get(commandType) || null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get styles for a command type (or based on message-state)
|
|
43
|
+
* @param {string} commandType - Command type
|
|
44
|
+
* @param {Object} matchData - Match data including intent/handler
|
|
45
|
+
* @returns {Object} Style configuration
|
|
46
|
+
*/
|
|
47
|
+
getStyles(commandType, matchData = null) {
|
|
48
|
+
// If matchData is provided, check message-state first
|
|
49
|
+
if (matchData && matchData.intent && matchData.intent.handler) {
|
|
50
|
+
const messageState = matchData.intent.handler['message-state'];
|
|
51
|
+
if (messageState) {
|
|
52
|
+
return this.getStylesForMessageState(messageState);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Fall back to command type handler
|
|
57
|
+
const handler = this.getHandler(commandType);
|
|
58
|
+
return handler ? handler.getStyles() : this.getDefaultStyles();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get styles based on message-state
|
|
63
|
+
* @param {string} messageState - Message state (error, warning, info)
|
|
64
|
+
* @returns {Object} Style configuration
|
|
65
|
+
*/
|
|
66
|
+
getStylesForMessageState(messageState) {
|
|
67
|
+
const stateStyles = {
|
|
68
|
+
'error': {
|
|
69
|
+
backgroundColor: 'rgba(220, 38, 38, 0.15)', // Red
|
|
70
|
+
color: '#991b1b',
|
|
71
|
+
textDecoration: 'none',
|
|
72
|
+
borderBottom: '2px solid #dc2626',
|
|
73
|
+
borderRadius: '0',
|
|
74
|
+
cursor: 'not-allowed'
|
|
75
|
+
},
|
|
76
|
+
'warning': {
|
|
77
|
+
backgroundColor: 'rgba(245, 158, 11, 0.15)', // Orange
|
|
78
|
+
color: '#92400e',
|
|
79
|
+
textDecoration: 'none',
|
|
80
|
+
borderBottom: '2px solid #f59e0b',
|
|
81
|
+
borderRadius: '0',
|
|
82
|
+
cursor: 'help'
|
|
83
|
+
},
|
|
84
|
+
'info': {
|
|
85
|
+
backgroundColor: 'rgba(16, 185, 129, 0.15)', // Green
|
|
86
|
+
color: '#065f46',
|
|
87
|
+
textDecoration: 'none',
|
|
88
|
+
borderBottom: '2px solid #10b981',
|
|
89
|
+
borderRadius: '0',
|
|
90
|
+
cursor: 'pointer'
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
return stateStyles[messageState] || this.getDefaultStyles();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Get bubble content for a match
|
|
99
|
+
* @param {string} commandType - Command type
|
|
100
|
+
* @param {Object} matchData - Match data including intent info
|
|
101
|
+
* @returns {string} HTML content for bubble
|
|
102
|
+
*/
|
|
103
|
+
getBubbleContent(commandType, matchData) {
|
|
104
|
+
const handler = this.getHandler(commandType);
|
|
105
|
+
return handler ? handler.getBubbleContent(matchData) : null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get default styles (fallback)
|
|
110
|
+
*/
|
|
111
|
+
getDefaultStyles() {
|
|
112
|
+
return {
|
|
113
|
+
backgroundColor: 'rgba(74, 144, 226, 0.15)',
|
|
114
|
+
color: '#2b6cb0',
|
|
115
|
+
textDecoration: 'none',
|
|
116
|
+
borderBottom: 'none'
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Base handler class
|
|
123
|
+
*/
|
|
124
|
+
class CommandHandler {
|
|
125
|
+
getStyles() {
|
|
126
|
+
return {};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
getBubbleContent(matchData) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
shouldBlockSubmit(matchData) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Handler for not-allowed commands (errors/forbidden)
|
|
140
|
+
* Red background, red underline
|
|
141
|
+
*/
|
|
142
|
+
class NotAllowedHandler extends CommandHandler {
|
|
143
|
+
getStyles() {
|
|
144
|
+
return {
|
|
145
|
+
backgroundColor: 'rgba(220, 38, 38, 0.15)', // Red background
|
|
146
|
+
color: '#991b1b', // Dark red text
|
|
147
|
+
textDecoration: 'none',
|
|
148
|
+
borderBottom: '2px solid #dc2626', // Red underline
|
|
149
|
+
borderRadius: '0', // No radius to avoid curved bottom border
|
|
150
|
+
cursor: 'not-allowed'
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
getBubbleContent(matchData) {
|
|
155
|
+
const intent = matchData.intent || {};
|
|
156
|
+
const handler = intent.handler || {};
|
|
157
|
+
|
|
158
|
+
const description = intent.description || 'Not allowed';
|
|
159
|
+
const message = handler['message-content'] || description;
|
|
160
|
+
|
|
161
|
+
return `
|
|
162
|
+
<div style="color: #991b1b;">
|
|
163
|
+
<div style="font-weight: 600; margin-bottom: 4px; display: flex; align-items: center;">
|
|
164
|
+
<span style="margin-right: 6px;">⛔</span>
|
|
165
|
+
Not Allowed
|
|
166
|
+
</div>
|
|
167
|
+
<div style="font-size: 12px; line-height: 1.4;">
|
|
168
|
+
${this.escapeHtml(message)}
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
shouldBlockSubmit(matchData) {
|
|
175
|
+
const handler = matchData.intent?.handler || {};
|
|
176
|
+
return handler['block-submit'] === true;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
escapeHtml(text) {
|
|
180
|
+
const div = document.createElement('div');
|
|
181
|
+
div.textContent = text;
|
|
182
|
+
return div.innerHTML;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Handler for show-warning commands
|
|
188
|
+
* Yellow/orange background, orange underline
|
|
189
|
+
*/
|
|
190
|
+
class ShowWarningHandler extends CommandHandler {
|
|
191
|
+
getStyles() {
|
|
192
|
+
return {
|
|
193
|
+
backgroundColor: 'rgba(245, 158, 11, 0.15)', // Amber/orange background
|
|
194
|
+
color: '#92400e', // Dark amber text
|
|
195
|
+
textDecoration: 'none',
|
|
196
|
+
borderBottom: '2px solid #f59e0b', // Orange underline
|
|
197
|
+
borderRadius: '0', // No radius to avoid curved bottom border
|
|
198
|
+
cursor: 'help'
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
getBubbleContent(matchData) {
|
|
203
|
+
const intent = matchData.intent || {};
|
|
204
|
+
const handler = intent.handler || {};
|
|
205
|
+
|
|
206
|
+
const description = intent.description || 'Warning';
|
|
207
|
+
const message = handler['message-content'] || description;
|
|
208
|
+
|
|
209
|
+
return `
|
|
210
|
+
<div style="color: #92400e;">
|
|
211
|
+
<div style="font-weight: 600; margin-bottom: 4px; display: flex; align-items: center;">
|
|
212
|
+
<span style="margin-right: 6px;">⚠️</span>
|
|
213
|
+
Warning
|
|
214
|
+
</div>
|
|
215
|
+
<div style="font-size: 12px; line-height: 1.4;">
|
|
216
|
+
${this.escapeHtml(message)}
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
`;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
shouldBlockSubmit(matchData) {
|
|
223
|
+
const handler = matchData.intent?.handler || {};
|
|
224
|
+
return handler['block-submit'] === true;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
escapeHtml(text) {
|
|
228
|
+
const div = document.createElement('div');
|
|
229
|
+
div.textContent = text;
|
|
230
|
+
return div.innerHTML;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Handler for user-select-oneOf-and-warn commands
|
|
236
|
+
* Shows warning + dropdown
|
|
237
|
+
*/
|
|
238
|
+
class UserSelectWithWarningHandler extends CommandHandler {
|
|
239
|
+
getStyles() {
|
|
240
|
+
return {
|
|
241
|
+
backgroundColor: 'rgba(245, 158, 11, 0.15)',
|
|
242
|
+
color: '#92400e',
|
|
243
|
+
textDecoration: 'none',
|
|
244
|
+
borderBottom: '2px solid #f59e0b',
|
|
245
|
+
cursor: 'pointer'
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
getBubbleContent(matchData) {
|
|
250
|
+
// Will show dropdown on click instead of bubble
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Handler for user-select-oneOf commands
|
|
257
|
+
* Shows dropdown without warning
|
|
258
|
+
*/
|
|
259
|
+
class UserSelectHandler extends CommandHandler {
|
|
260
|
+
getStyles() {
|
|
261
|
+
return {
|
|
262
|
+
backgroundColor: 'rgba(59, 130, 246, 0.15)', // Blue background
|
|
263
|
+
color: '#1e40af', // Dark blue text
|
|
264
|
+
textDecoration: 'none',
|
|
265
|
+
borderBottom: '2px solid #3b82f6', // Blue underline
|
|
266
|
+
cursor: 'pointer'
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
getBubbleContent(matchData) {
|
|
271
|
+
// Will show dropdown on click instead of bubble
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Handler for api-json-table commands
|
|
278
|
+
*/
|
|
279
|
+
class ApiJsonTableHandler extends CommandHandler {
|
|
280
|
+
getStyles() {
|
|
281
|
+
return {
|
|
282
|
+
backgroundColor: 'rgba(16, 185, 129, 0.15)', // Green background
|
|
283
|
+
color: '#065f46', // Dark green text
|
|
284
|
+
textDecoration: 'none',
|
|
285
|
+
borderBottom: '2px solid #10b981',
|
|
286
|
+
cursor: 'pointer'
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
getBubbleContent(matchData) {
|
|
291
|
+
const intent = matchData.intent || {};
|
|
292
|
+
const description = intent.description || 'Click to view data';
|
|
293
|
+
|
|
294
|
+
return `
|
|
295
|
+
<div style="color: #065f46;">
|
|
296
|
+
<div style="font-weight: 600; margin-bottom: 4px;">
|
|
297
|
+
📊 Data Table
|
|
298
|
+
</div>
|
|
299
|
+
<div style="font-size: 12px;">
|
|
300
|
+
${this.escapeHtml(description)}
|
|
301
|
+
</div>
|
|
302
|
+
</div>
|
|
303
|
+
`;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
escapeHtml(text) {
|
|
307
|
+
const div = document.createElement('div');
|
|
308
|
+
div.textContent = text;
|
|
309
|
+
return div.innerHTML;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Handler for api-md-table commands
|
|
315
|
+
*/
|
|
316
|
+
class ApiMdTableHandler extends CommandHandler {
|
|
317
|
+
getStyles() {
|
|
318
|
+
return {
|
|
319
|
+
backgroundColor: 'rgba(16, 185, 129, 0.15)',
|
|
320
|
+
color: '#065f46',
|
|
321
|
+
textDecoration: 'none',
|
|
322
|
+
borderBottom: '2px solid #10b981',
|
|
323
|
+
cursor: 'pointer'
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
getBubbleContent(matchData) {
|
|
328
|
+
const intent = matchData.intent || {};
|
|
329
|
+
const description = intent.description || 'Click to view data';
|
|
330
|
+
|
|
331
|
+
return `
|
|
332
|
+
<div style="color: #065f46;">
|
|
333
|
+
<div style="font-weight: 600; margin-bottom: 4px;">
|
|
334
|
+
📊 Data Table
|
|
335
|
+
</div>
|
|
336
|
+
<div style="font-size: 12px;">
|
|
337
|
+
${this.escapeHtml(description)}
|
|
338
|
+
</div>
|
|
339
|
+
</div>
|
|
340
|
+
`;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
escapeHtml(text) {
|
|
344
|
+
const div = document.createElement('div');
|
|
345
|
+
div.textContent = text;
|
|
346
|
+
return div.innerHTML;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export default CommandHandlerRegistry;
|