@leeandrew94/ccm 0.3.0 → 0.3.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.
Files changed (2) hide show
  1. package/bin/ccm.js +74 -6
  2. package/package.json +1 -1
package/bin/ccm.js CHANGED
@@ -1530,10 +1530,37 @@ function escHtml(s) {
1530
1530
 
1531
1531
  function renderMarkdown(text) {
1532
1532
  if (!text) return '';
1533
- var h = escHtml(text);
1534
1533
  var BT = String.fromCharCode(96);
1535
- h = h.replace(new RegExp(BT + BT + BT + '(\\\\w*)\\\\n([\\\\s\\\\S]*?)' + BT + BT + BT, 'g'), function(m, lang, code) { return '<pre><code>' + highlightCode(code) + '</code></pre>'; });
1536
- h = h.replace(new RegExp(BT + '([^\\\\n]+?)' + BT, 'g'), '<code>$1</code>');
1534
+ var h = text;
1535
+
1536
+ // Placeholder delimiters: Unicode PUA (Private Use Area) codepoints.
1537
+ // Control chars (\\x00, \\x01) would be replaced with \uFFFD (\uFFFD) by the
1538
+ // browser's HTML parser, so we use printable PUA chars that survive intact.
1539
+ var PS = '\uE000'; // placeholder start
1540
+ var PE = '\uE001'; // placeholder end
1541
+
1542
+ // Step 1: Extract fenced code blocks from RAW text (before any HTML escaping)
1543
+ // Apply syntax highlighting to raw code so highlightCode regexes work correctly
1544
+ var codeBlocks = [];
1545
+ h = h.replace(new RegExp(BT + BT + BT + '(\\\\w*)\\\\n([\\\\s\\\\S]*?)' + BT + BT + BT, 'g'), function(m, lang, code) {
1546
+ var idx = codeBlocks.length;
1547
+ codeBlocks.push('<pre><code>' + highlightCode(code) + '</code></pre>');
1548
+ return PS + 'CCM_CB' + idx + PE;
1549
+ });
1550
+
1551
+ // Step 2: Extract inline code from RAW text
1552
+ // escHtml the code content so it displays literally
1553
+ var inlineCodes = [];
1554
+ h = h.replace(new RegExp(BT + '([^\\\\n]+?)' + BT, 'g'), function(m, code) {
1555
+ var idx = inlineCodes.length;
1556
+ inlineCodes.push('<code>' + escHtml(code) + '</code>');
1557
+ return PS + 'CCM_IC' + idx + PE;
1558
+ });
1559
+
1560
+ // Step 3: Now HTML-escape the remaining text (blockquotes, etc.)
1561
+ h = escHtml(h);
1562
+
1563
+ // Step 4: Apply markdown formatting
1537
1564
  h = h.replace(/^### (.+)$/gm, '<h3>$1</h3>');
1538
1565
  h = h.replace(/^## (.+)$/gm, '<h2>$1</h2>');
1539
1566
  h = h.replace(/^# (.+)$/gm, '<h1>$1</h1>');
@@ -1565,16 +1592,56 @@ function renderMarkdown(text) {
1565
1592
  h = h.replace(/(<\\/blockquote>)<\\/p>/g, '$1');
1566
1593
  h = h.replace(/<p>(<hr>)/g, '$1');
1567
1594
  h = h.replace(/(<hr>)<\\/p>/g, '$1');
1595
+
1596
+ // Step 5: Restore code blocks and inline code
1597
+ h = h.replace(new RegExp(PS + 'CCM_CB(\\d+)' + PE, 'g'), function(m, idx) { return codeBlocks[parseInt(idx)]; });
1598
+ h = h.replace(new RegExp(PS + 'CCM_IC(\\d+)' + PE, 'g'), function(m, idx) { return inlineCodes[parseInt(idx)]; });
1599
+
1568
1600
  return h;
1569
1601
  }
1570
1602
 
1571
1603
  function highlightCode(code) {
1572
1604
  var h = code;
1573
- h = h.replace(/(\\/\\/.*$)/gm, '<span class="cm">$1</span>');
1574
- h = h.replace(/(#.*$)/gm, '<span class="cm">$1</span>');
1575
- h = h.replace(/("(?:[^"\\\\\\\\]|\\\\\\\\.)*"|'(?:[^'\\\\\\\\]|\\\\\\\\.)*')/g, '<span class="str">$1</span>');
1605
+ var p = [];
1606
+
1607
+ // Same PUA delimiters used in renderMarkdown
1608
+ var PS = '\uE000';
1609
+ var PE = '\uE001';
1610
+
1611
+ // Protect strings with placeholders (before other regexes, to avoid
1612
+ // keywords / comments inside strings being highlighted)
1613
+ h = h.replace(/("[^"]*"|'[^']*')/g, function(m) {
1614
+ var i = p.length;
1615
+ p.push('<span class="str">' + escHtml(m) + '</span>');
1616
+ return PS + 'HL' + i + PE;
1617
+ });
1618
+
1619
+ // Protect // comments
1620
+ h = h.replace(/(\\/\\/.*$)/gm, function(m) {
1621
+ var i = p.length;
1622
+ p.push('<span class="cm">' + escHtml(m) + '</span>');
1623
+ return PS + 'HL' + i + PE;
1624
+ });
1625
+
1626
+ // Protect # comments
1627
+ h = h.replace(/(#.*$)/gm, function(m) {
1628
+ var i = p.length;
1629
+ p.push('<span class="cm">' + escHtml(m) + '</span>');
1630
+ return PS + 'HL' + i + PE;
1631
+ });
1632
+
1633
+ // HTML-escape remaining literal code characters
1634
+ h = escHtml(h);
1635
+
1636
+ // Keywords (safe: placeholders contain no keywords)
1576
1637
  h = h.replace(/\\b(import|export|from|const|let|var|function|return|if|else|for|while|class|extends|new|async|await|try|catch|throw|switch|case|break|default|typeof|instanceof|void|null|undefined|true|false|def|self|print|raise|with|as|in|not|and|or|is|lambda|yield|assert|del|global|nonlocal|pass|elif|except|finally)\\b/g, '<span class="kw">$1</span>');
1638
+
1639
+ // Numbers
1577
1640
  h = h.replace(/\\b(\\d+\\.?\\d*)\\b/g, '<span class="num">$1</span>');
1641
+
1642
+ // Restore protected fragments
1643
+ h = h.replace(new RegExp(PS + 'HL(\\d+)' + PE, 'g'), function(m, i) { return p[parseInt(i)]; });
1644
+
1578
1645
  return h;
1579
1646
  }
1580
1647
 
@@ -1694,6 +1761,7 @@ body {
1694
1761
  .main-header h1 {
1695
1762
  font-size: 16px; font-weight: 600; margin-bottom: 10px;
1696
1763
  line-height: 1.5; color: var(--td-text-primary);
1764
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
1697
1765
  }
1698
1766
  .main-header .meta {
1699
1767
  display: flex; gap: 18px; font-size: 12px;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leeandrew94/ccm",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Claude Code Model Manager - switch between AI models per terminal",
5
5
  "type": "module",
6
6
  "bin": {