@gitsense/gsc-utils 0.2.24 → 0.2.25

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitsense/gsc-utils",
3
- "version": "0.2.24",
3
+ "version": "0.2.25",
4
4
  "description": "Utilities for GitSense Chat (GSC)",
5
5
  "main": "dist/gsc-utils.cjs.js",
6
6
  "module": "dist/gsc-utils.esm.js",
@@ -2,11 +2,11 @@
2
2
  * Component: CodeBlockUtils Constants
3
3
  * Block-UUID: 01b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d
4
4
  * Parent-UUID: 144d19a5-139a-4e80-8abe-84965583e35e
5
- * Version: 1.0.1
5
+ * Version: 1.0.2
6
6
  * Description: Defines constants used across the CodeBlockUtils modules, such as comment styles for various languages.
7
7
  * Language: JavaScript
8
8
  * Created-at: 2025-04-15T15:51:16.973Z
9
- * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Flash Thinking (v1.0.1)
9
+ * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Flash Thinking (v1.0.1), Qwen 3 Coder 480B - Cerebras (v1.0.2)
10
10
  */
11
11
 
12
12
 
@@ -40,6 +40,12 @@ const COMMENT_STYLES = {
40
40
  'r': { type: 'hash' },
41
41
  'julia': { type: 'hash' },
42
42
 
43
+ // HTML/XML/SVG/Markdown comment style
44
+ 'html': { type: 'html' },
45
+ 'xml': { type: 'html' },
46
+ 'svg': { type: 'html' },
47
+ 'markdown': { type: 'html' }, // For metadata in Markdown files
48
+
43
49
  // Other language styles
44
50
  'lisp': { type: 'semicolon' },
45
51
  'clojure': { type: 'semicolon' },
@@ -58,5 +64,3 @@ const COMMENT_STYLES = {
58
64
  module.exports = {
59
65
  COMMENT_STYLES
60
66
  };
61
-
62
-
@@ -2,11 +2,11 @@
2
2
  * Component: CodeBlockUtils Header Utilities
3
3
  * Block-UUID: 2565f708-feec-4b59-88fd-984084410fd4
4
4
  * Parent-UUID: 01147ddf-320f-498a-a744-198d42a9d2ee
5
- * Version: 1.2.0
5
+ * Version: 1.2.1
6
6
  * Description: Provides utility functions for parsing metadata headers from code blocks, validating timestamps, and calculating header line counts.
7
7
  * Language: JavaScript
8
8
  * Created-at: 2025-07-21T00:23:28.169Z
9
- * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Flash Thinking (v1.1.0), Gemini 2.5 Flash Thinking (v1.2.0)
9
+ * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Flash Thinking (v1.1.0), Gemini 2.5 Flash Thinking (v1.2.0), Qwen 3 Coder 480B - Cerebras (v1.2.1)
10
10
  */
11
11
 
12
12
 
@@ -113,6 +113,22 @@ function parseHeader(header, language) {
113
113
  .map(line => line.replace(/^\s*'\s?/, ''))
114
114
  .join('\n');
115
115
  break;
116
+
117
+ // --- New Case for HTML/XML/Markdown Comments ---
118
+ case 'html':
119
+ // Remove opening <!-- and closing --> tags.
120
+ // The regex handles cases where the tags might be on the same line as content
121
+ // or on their own lines.
122
+ cleanHeader = header.replace(/^<!--\s*/, '')
123
+ .replace(/\s*-->$/, '')
124
+ .split('\n')
125
+ // Remove leading whitespace and potential dashes (common in HTML comments)
126
+ // This is a heuristic, as HTML comments don't typically have line prefixes.
127
+ // It's included to be robust against minor formatting variations.
128
+ .map(line => line.replace(/^\s*(-|\*)\s?/, ''))
129
+ .join('\n');
130
+ break;
131
+ // --- End of New Case ---
116
132
 
117
133
  case 'unknown':
118
134
  // Attempt basic detection if language wasn't found in COMMENT_STYLES
@@ -170,7 +186,7 @@ function parseHeader(header, language) {
170
186
  const requiredFields = [
171
187
  'Component',
172
188
  'Block-UUID',
173
- 'Version', // Keep Version as string here, handle parsing elsewhere if needed
189
+ 'Version',
174
190
  'Language',
175
191
  'Created-at',
176
192
  'Authors'
@@ -141,7 +141,7 @@ function parseContextSection(sectionText) {
141
141
  if (properties.includes(property)) {
142
142
  let value = trimmedLine.split(':').slice(1).join(':').trim();
143
143
 
144
- if (property.match(/id/))
144
+ if (property.match(/id/) || property.match(/tokens/))
145
145
  value = parseInt(value);
146
146
 
147
147
  contextSection[property] = value;
@@ -0,0 +1,485 @@
1
+ /**
2
+ * Component: DomUtils Helper Functions
3
+ * Block-UUID: e85d4e1d-f45e-450b-a0f2-941af362c2be
4
+ * Parent-UUID: fe462d17-e1c8-45d6-9fb0-f53322c9cda9
5
+ * Version: 1.1.0
6
+ * Description: Provides helper functions for creating and manipulating DOM elements.
7
+ * Language: JavaScript
8
+ * Created-at: 2025-09-11T19:03:50.026Z
9
+ * Authors: Gemini 2.5 Pro (v1.0.0), Gemini 2.5 Flash (v1.1.0)
10
+ */
11
+
12
+
13
+ function createElement(type, params) {
14
+ let elem = document.createElement(type);
15
+
16
+ if (!params) return elem;
17
+
18
+ // Extract all standard properties
19
+ let {
20
+ id,
21
+ ariaLabel,
22
+ role,
23
+ html,
24
+ text,
25
+ cls,
26
+ className,
27
+ colSpan,
28
+ pointerEvents,
29
+ title,
30
+ style,
31
+ append,
32
+ type: inputType,
33
+ value,
34
+ name,
35
+ checked,
36
+ selected,
37
+ disabled,
38
+ placeholder,
39
+ "for": _for,
40
+ "data-message-id": dmi,
41
+ "data-message-role": dmr,
42
+ attrs = {},
43
+ dataset,
44
+ } = params;
45
+
46
+ // Set standard attributes
47
+ if (id != null) elem.id = id;
48
+ if (ariaLabel) elem.setAttribute("aria-label", ariaLabel);
49
+ if (role) elem.setAttribute("role", role);
50
+ if (placeholder) elem.setAttribute("placeholder", placeholder);
51
+ if (cls || className) elem.setAttribute("class", cls || className);
52
+ if (pointerEvents) elem.setAttribute("pointer-events", pointerEvents);
53
+ if (title != null) elem.setAttribute("title", title);
54
+ if (inputType) elem.setAttribute("type", inputType);
55
+ if (name != null) elem.setAttribute("name", name);
56
+ if (value != null) elem.value = value;
57
+ if (checked != null && checked) elem.checked = true; // Use property for checked
58
+ if (selected != null && selected) elem.selected = true; // Use property for selected
59
+ if (disabled != null) elem.disabled = disabled; // Use property for disabled
60
+ if (dmi != null) elem.setAttribute("data-message-id", dmi);
61
+ if (dmr != null) elem.setAttribute("data-message-role", dmr);
62
+ if (_for != null ) elem.setAttribute("for", _for);
63
+ if (colSpan != null) elem.colSpan = colSpan;
64
+
65
+ // Set content
66
+ if (html != null) {
67
+ if (typeof html === "object" && html instanceof Node) { // Ensure html is a Node
68
+ elem.appendChild(html);
69
+ } else if (typeof html === "string") {
70
+ elem.innerHTML = html;
71
+ }
72
+ } else if (text != null) {
73
+ elem.innerText = text;
74
+ }
75
+
76
+ // Set style properties
77
+ if (style) {
78
+ for (const name in style) {
79
+ elem.style[name] = style[name];
80
+ }
81
+ }
82
+
83
+ // Handle append (now supports both arrays and single elements)
84
+ if (append) {
85
+ const appendItems = Array.isArray(append) ? append : [append];
86
+ appendItems.forEach(e => {
87
+ if (e instanceof Node) { // Ensure item to append is a Node
88
+ elem.appendChild(e);
89
+ } else if (e !== undefined && e !== null) {
90
+ console.warn("Attempted to append non-Node item:", e);
91
+ }
92
+ });
93
+ }
94
+
95
+ // NEW: Set additional attributes from attrs object
96
+ if (attrs && typeof attrs === 'object') {
97
+ for (const [key, value] of Object.entries(attrs)) {
98
+ if (value !== undefined && value !== null) {
99
+ // Skip if already set by standard properties or handled directly
100
+ if (!['id', 'class', 'role', 'aria-label', 'title', 'style', 'checked', 'selected', 'disabled', 'value', 'name', 'type', 'for'].includes(key) && !key.startsWith('data-')) {
101
+ elem.setAttribute(key, value);
102
+ }
103
+ }
104
+ }
105
+ }
106
+
107
+ // Handle dataset properties
108
+ if (dataset && typeof dataset === 'object') {
109
+ for (const [key, value] of Object.entries(dataset)) {
110
+ if (value !== undefined && value !== null) {
111
+ elem.dataset[key] = value;
112
+ }
113
+ }
114
+ }
115
+
116
+ // Handle event handlers (like onclick, onchange, etc.)
117
+ for (const [key, value] of Object.entries(params)) {
118
+ // Check if the property is an event handler (starts with 'on')
119
+ if (key.startsWith('on') && typeof value === 'function') {
120
+ const eventName = key.slice(2).toLowerCase(); // Remove 'on' prefix and convert to lowercase
121
+ elem.addEventListener(eventName, value);
122
+ }
123
+ }
124
+
125
+ return elem;
126
+ }
127
+
128
+ // Update all creator functions to pass through attrs:
129
+ const h = {
130
+ elemById: (id) => {
131
+ return document.getElementById(id);
132
+ },
133
+ createA: (params) => {
134
+ return h.createLink(params);
135
+ },
136
+ createArticle: (params) => {
137
+ return createElement("article", params);
138
+ },
139
+ createBr: () => {
140
+ return createElement("br");
141
+ },
142
+ createBreak: () => {
143
+ return createElement("br");
144
+ },
145
+ createButton: (params) => {
146
+ return createElement("button", params);
147
+ },
148
+ createCheckbox: (params) => {
149
+ // Use createElement and let it handle attributes like checked/disabled
150
+ return createElement("input", { ...params, type: "checkbox" });
151
+ },
152
+ createCode: (params) => {
153
+ return createElement("code", params);
154
+ },
155
+ createDetail: (params) => {
156
+ return createElement("details", params);
157
+ },
158
+ createDiv: (params) => {
159
+ return createElement("div", params);
160
+ },
161
+ createEm: (params) => { // Added
162
+ return createElement("em", params);
163
+ },
164
+ createForm: (params) => {
165
+ return createElement("form", params);
166
+ },
167
+ createInput: (params) => {
168
+ return createElement("input", params);
169
+ },
170
+ createImg : (params) => {
171
+ const img = createElement("img", params);
172
+ // src is handled by createElement via attrs or direct property if needed
173
+ return img;
174
+ },
175
+ createH1: (params) => {
176
+ return createElement("h1", params);
177
+ },
178
+ createH2: (params) => {
179
+ return createElement("h2", params);
180
+ },
181
+ createH3: (params) => {
182
+ return createElement("h3", params);
183
+ },
184
+ createH4: (params) => {
185
+ return createElement("h4", params);
186
+ },
187
+ createH5: (params) => {
188
+ return createElement("h5", params);
189
+ },
190
+ createHeader: (header, params) => {
191
+ // Ensure header is a string like 'h1', 'h2' etc.
192
+ if (typeof header === 'string' && /^h[1-6]$/i.test(header)) {
193
+ return createElement(header, params);
194
+ }
195
+ console.error("Invalid header type provided to createHeader:", header);
196
+ return createElement("div", params); // Fallback or throw error
197
+ },
198
+ createHr: (params) => {
199
+ return createElement("hr", params);
200
+ },
201
+ createLabel : (params) => {
202
+ // 'for' is handled by createElement
203
+ return createElement("label", params);
204
+ },
205
+ createLI: (params) => {
206
+ return createElement("li", params);
207
+ },
208
+ createLi: (params) => { // Alias for createLI
209
+ return createElement("li", params);
210
+ },
211
+ createLink: (params) => {
212
+ const link = createElement("a", params);
213
+ // href and target are handled by createElement
214
+ if (params?.href) link.href = params.href; // Ensure href is set if provided
215
+ return link;
216
+ },
217
+ createNav: (params) => {
218
+ return createElement("nav", params);
219
+ },
220
+ createOL: (params) => { // Added
221
+ return createElement("ol", params);
222
+ },
223
+ createOption: (params) => { // Added
224
+ // value, text, selected are handled by createElement
225
+ return createElement("option", params);
226
+ },
227
+ createP: (params) => {
228
+ return createElement("p", params);
229
+ },
230
+ createParagraph: (params) => { // Alias for createP
231
+ return createElement("p", params);
232
+ },
233
+ createPre: (params) => {
234
+ return createElement("pre", params);
235
+ },
236
+ createSelect: (params) => { // Added
237
+ // name, disabled etc handled by createElement
238
+ return createElement("select", params);
239
+ },
240
+ createSpan: (params) => {
241
+ return createElement("span", params);
242
+ },
243
+ createStrong: (params) => { // Added
244
+ return createElement("strong", params);
245
+ },
246
+ createSummary: (params) => {
247
+ return createElement("summary", params);
248
+ },
249
+ createSup: (params) => {
250
+ return createElement("sup", params);
251
+ },
252
+ createText: (text) => {
253
+ return document.createTextNode(text);
254
+ },
255
+ createTextArea: (params) => {
256
+ const textArea = createElement("textarea", params);
257
+ return textArea;
258
+ },
259
+ createTextNode: (text) => {
260
+ return document.createTextNode(text);
261
+ },
262
+ createTable: (params) => {
263
+ return createElement("table", params);
264
+ },
265
+ createTableBody: (params) => {
266
+ return createElement("tbody", params);
267
+ },
268
+ createTableCell: (params) => {
269
+ return createElement("td", params);
270
+ },
271
+ createTableHead: (params) => {
272
+ return createElement("thead", params);
273
+ },
274
+ createTableHeadCell: (params) => {
275
+ return createElement("th", params);
276
+ },
277
+ createTbody: (params) => {
278
+ return createElement("tbody", params);
279
+ },
280
+ createTd: (params) => {
281
+ return createElement("td", params);
282
+ },
283
+ createTh: (params) => {
284
+ return createElement("th", params);
285
+ },
286
+ createThead: (params) => {
287
+ return createElement("thead", params);
288
+ },
289
+ createTr: (params) => {
290
+ return createElement("tr", params);
291
+ },
292
+ createTableRow: (params) => {
293
+ return createElement("tr", params);
294
+ },
295
+ createTextInput: (params) => {
296
+ // Use createElement and let it handle attributes like value/disabled
297
+ return createElement("input", { ...params, type: "text" });
298
+ },
299
+ createUl: (params) => {
300
+ return createElement("ul", params);
301
+ },
302
+ createUL: (params) => {
303
+ return createElement("ul", params);
304
+ },
305
+ hide: (elem) => {
306
+ if (elem && elem.style) {
307
+ elem.style.display = "none";
308
+ }
309
+ },
310
+ show: (elem) => {
311
+ if (elem && elem.style) {
312
+ elem.style.display = ""; // Reset to default display
313
+ }
314
+ },
315
+ updateDOM: (sourceNode, targetNode) => {
316
+ // Step 1: Check if the node type or tag name is different
317
+ if (!sourceNode || !targetNode) return; // Basic guard
318
+ if (sourceNode.nodeType !== targetNode.nodeType || sourceNode.nodeName !== targetNode.nodeName) {
319
+ targetNode.replaceWith(sourceNode.cloneNode(true));
320
+ return;
321
+ }
322
+
323
+ // Step 2: Check if it's an element node
324
+ if (sourceNode.nodeType === Node.ELEMENT_NODE) {
325
+ // Step 3: Compare and update attributes
326
+ let sourceAttributes = sourceNode.attributes;
327
+ let targetAttributes = targetNode.attributes;
328
+ const targetAttrMap = new Map();
329
+ for (let i = 0; i < targetAttributes.length; i++) {
330
+ targetAttrMap.set(targetAttributes[i].name, targetAttributes[i].value);
331
+ }
332
+
333
+ // Update or add attributes from sourceNode to targetNode
334
+ for (let i = 0; i < sourceAttributes.length; i++) {
335
+ let attr = sourceAttributes[i];
336
+ if (targetAttrMap.get(attr.name) !== attr.value) {
337
+ targetNode.setAttribute(attr.name, attr.value);
338
+ }
339
+ targetAttrMap.delete(attr.name); // Mark as processed
340
+ }
341
+
342
+ // Remove attributes that are in target but not in source
343
+ for (const attrName of targetAttrMap.keys()) {
344
+ targetNode.removeAttribute(attrName);
345
+ }
346
+
347
+ // Special handling for properties like value, checked, selected, disabled
348
+ if (sourceNode.value !== targetNode.value) {
349
+ targetNode.value = sourceNode.value;
350
+ }
351
+ if (sourceNode.checked !== targetNode.checked) {
352
+ targetNode.checked = sourceNode.checked;
353
+ }
354
+ if (sourceNode.selected !== targetNode.selected) {
355
+ targetNode.selected = sourceNode.selected;
356
+ }
357
+ if (sourceNode.disabled !== targetNode.disabled) {
358
+ targetNode.disabled = sourceNode.disabled;
359
+ }
360
+
361
+
362
+ // Step 4: Recursively compare and update children
363
+ let sourceChildren = sourceNode.childNodes;
364
+ let targetChildren = targetNode.childNodes;
365
+ let maxLen = Math.max(sourceChildren.length, targetChildren.length);
366
+
367
+ for (let i = 0; i < maxLen; i++) {
368
+ if (i >= targetChildren.length) {
369
+ // If the target has fewer children, append the new child
370
+ if (sourceChildren[i]) {
371
+ targetNode.appendChild(sourceChildren[i].cloneNode(true));
372
+ }
373
+ } else if (i >= sourceChildren.length) {
374
+ // If the source has fewer children, remove extra target child
375
+ targetNode.removeChild(targetChildren[i]);
376
+ } else {
377
+ // Otherwise, update the existing child
378
+ h.updateDOM(sourceChildren[i], targetChildren[i]);
379
+ }
380
+ }
381
+
382
+ } else if (sourceNode.nodeType === Node.TEXT_NODE) {
383
+ // Step 5: Update text content if the text nodes are different
384
+ if (sourceNode.nodeValue !== targetNode.nodeValue) {
385
+ targetNode.nodeValue = sourceNode.nodeValue;
386
+ }
387
+ }
388
+ // Other node types (comments, etc.) are ignored for updates in this version
389
+ },
390
+ safeEscape: (html, x) => {
391
+ if ( x )
392
+ console.log(x);
393
+
394
+ if (typeof html !== 'string') return html; // Return non-strings as-is
395
+
396
+ // Generated by Claude 3.5 Sonnet
397
+ const preserveTags = [
398
+ 'pre',
399
+ 'code',
400
+ 'script',
401
+ 'style',
402
+ 'textarea',
403
+ 'math',
404
+ 'mi',
405
+ 'mo',
406
+ 'mn',
407
+ 'ms',
408
+ 'mtext',
409
+ 'samp',
410
+ 'kbd',
411
+ 'var',
412
+ 'svg',
413
+ 'xmp'
414
+ ].join('|');
415
+
416
+ // Track whether we're inside a preserve tag
417
+ let isInPreserveTag = false;
418
+ let currentTag = '';
419
+
420
+ // Split the input into tokens that are either tags or text
421
+ // Improved regex to handle self-closing tags and attributes better
422
+ return html.split(/(<\/?(?:${preserveTags})(?:\s+[^>]*)?\/?>|<\/?[a-zA-Z][^>]*>)/i)
423
+ .filter(part => part) // Remove empty strings from split
424
+ .map(part => {
425
+ // Check if this part is a preserve tag
426
+ const preserveTagMatch = part.match(new RegExp(`^<(/?)(${preserveTags})([\\s>/])`, 'i'));
427
+
428
+ if (preserveTagMatch) {
429
+ // Toggle the preserve state based on opening/closing tags
430
+ if (!preserveTagMatch[1] && !part.endsWith('/>')) { // Opening tag, not self-closing
431
+ isInPreserveTag = true;
432
+ currentTag = preserveTagMatch[2];
433
+ } else if (preserveTagMatch[1]) { // Closing tag
434
+ // Only close if it matches the current open tag
435
+ if (isInPreserveTag && currentTag.toLowerCase() === preserveTagMatch[2].toLowerCase()) {
436
+ isInPreserveTag = false;
437
+ currentTag = '';
438
+ }
439
+ }
440
+ return part; // Return the tag itself unmodified
441
+ }
442
+
443
+ // If we're not in a preserve tag and this isn't an HTML tag, escape <
444
+ if (!isInPreserveTag && !part.startsWith('<')) {
445
+ return part.replace(/</g, '&lt;');
446
+ }
447
+
448
+ // Return other tags or content within preserve tags unmodified
449
+ return part;
450
+ })
451
+ .join('');
452
+ },
453
+
454
+ /**
455
+ * Calculates the distance between an element's edge and the viewport's corresponding edge
456
+ * @param {HTMLElement} elem - The element to measure from
457
+ * @param {"left"|"right"} edge - Which edge to measure from
458
+ * @returns {number} The distance in pixels
459
+ * @throws {Error} If invalid edge parameter is provided or element not found
460
+ */
461
+ getDistanceFromViewportEdge: (elem, edge = "right") => {
462
+ // Input validation
463
+ if (!elem || typeof elem.getBoundingClientRect !== 'function') {
464
+ throw new Error('Invalid element provided.');
465
+ }
466
+ if ( !["left", "right"].includes(edge) ) {
467
+ throw new Error('Edge parameter must be either "left" or "right"');
468
+ }
469
+
470
+ // Get the bounding rectangle of the elem
471
+ const rect = elem.getBoundingClientRect();
472
+
473
+ // Get the width of the viewport
474
+ const viewportWidth = window.innerWidth;
475
+
476
+ // Calculate distance based on specified edge
477
+ if (edge === "right") {
478
+ return viewportWidth - rect.right;
479
+ } else { // edge === "left"
480
+ return rect.left;
481
+ }
482
+ }
483
+ }
484
+
485
+ module.exports = { h };