codeharbor 0.1.17 → 0.1.18

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/dist/cli.js +124 -11
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -3128,7 +3128,7 @@ function renderMatrixHtml(body, msgtype) {
3128
3128
  let match;
3129
3129
  while ((match = codeFencePattern.exec(normalized)) !== null) {
3130
3130
  const before = normalized.slice(cursor, match.index);
3131
- const renderedBefore = renderTextSection(before);
3131
+ const renderedBefore = renderMarkdownSection(before);
3132
3132
  if (renderedBefore) {
3133
3133
  sections.push(renderedBefore);
3134
3134
  }
@@ -3141,29 +3141,142 @@ function renderMatrixHtml(body, msgtype) {
3141
3141
  cursor = match.index + match[0].length;
3142
3142
  }
3143
3143
  const tail = normalized.slice(cursor);
3144
- const renderedTail = renderTextSection(tail);
3144
+ const renderedTail = renderMarkdownSection(tail);
3145
3145
  if (renderedTail) {
3146
3146
  sections.push(renderedTail);
3147
3147
  }
3148
3148
  if (sections.length === 0) {
3149
3149
  sections.push("<p>(\u7A7A\u6D88\u606F)</p>");
3150
3150
  }
3151
- const badge = msgtype === "m.notice" ? `<p><font color="#8a5a00"><b>\u{1F4E3} CodeHarbor \u63D0\u793A</b></font></p>` : `<p><font color="#1f7a5a"><b>\u{1F916} AI \u56DE\u590D</b></font></p>`;
3151
+ const badge = msgtype === "m.notice" ? `<p><font color="#8a5a00"><b>CodeHarbor \u63D0\u793A</b></font></p>` : `<p><font color="#1f7a5a"><b>CodeHarbor AI \u56DE\u590D</b></font></p>`;
3152
3152
  return `<div>${badge}${sections.join("")}</div>`;
3153
3153
  }
3154
- function renderTextSection(raw) {
3154
+ function renderMarkdownSection(raw) {
3155
3155
  if (!raw.trim()) {
3156
3156
  return "";
3157
3157
  }
3158
- const normalized = raw.replace(/\r\n/g, "\n").trim();
3159
- const paragraphs = normalized.split(/\n{2,}/);
3160
- const rendered = paragraphs.map((paragraph) => {
3161
- const escaped = escapeHtml(paragraph);
3162
- const inlineCode = escaped.replace(/`([^`\n]+)`/g, "<code>$1</code>");
3163
- return `<p>${inlineCode.replace(/\n/g, "<br/>")}</p>`;
3164
- }).join("");
3158
+ const lines = raw.replace(/\r\n/g, "\n").trim().split("\n");
3159
+ const blocks = [];
3160
+ let index = 0;
3161
+ while (index < lines.length) {
3162
+ const line = lines[index];
3163
+ const trimmed = line.trim();
3164
+ if (!trimmed) {
3165
+ index += 1;
3166
+ continue;
3167
+ }
3168
+ const headingMatch = /^(#{1,6})\s+(.+)$/.exec(trimmed);
3169
+ if (headingMatch) {
3170
+ const level = Math.min(6, headingMatch[1].length + 1);
3171
+ blocks.push(`<h${level}>${renderInlineMarkup(headingMatch[2])}</h${level}>`);
3172
+ index += 1;
3173
+ continue;
3174
+ }
3175
+ if (/^(?:-{3,}|\*{3,}|_{3,})$/.test(trimmed)) {
3176
+ blocks.push("<hr/>");
3177
+ index += 1;
3178
+ continue;
3179
+ }
3180
+ if (/^>\s?/.test(trimmed)) {
3181
+ const quoteLines = [];
3182
+ while (index < lines.length) {
3183
+ const current = lines[index].trim();
3184
+ if (!current) {
3185
+ break;
3186
+ }
3187
+ if (!/^>\s?/.test(current)) {
3188
+ break;
3189
+ }
3190
+ quoteLines.push(current.replace(/^>\s?/, ""));
3191
+ index += 1;
3192
+ }
3193
+ if (quoteLines.length > 0) {
3194
+ blocks.push(`<blockquote><p>${quoteLines.map((entry) => renderInlineMarkup(entry)).join("<br/>")}</p></blockquote>`);
3195
+ }
3196
+ continue;
3197
+ }
3198
+ if (/^\s*[-*]\s+/.test(line)) {
3199
+ const items = [];
3200
+ while (index < lines.length && /^\s*[-*]\s+/.test(lines[index])) {
3201
+ items.push(lines[index].replace(/^\s*[-*]\s+/, "").trim());
3202
+ index += 1;
3203
+ }
3204
+ blocks.push(`<ul>${items.map((item) => `<li>${renderInlineMarkup(item)}</li>`).join("")}</ul>`);
3205
+ continue;
3206
+ }
3207
+ if (/^\s*\d+\.\s+/.test(line)) {
3208
+ const items = [];
3209
+ while (index < lines.length && /^\s*\d+\.\s+/.test(lines[index])) {
3210
+ items.push(lines[index].replace(/^\s*\d+\.\s+/, "").trim());
3211
+ index += 1;
3212
+ }
3213
+ blocks.push(`<ol>${items.map((item) => `<li>${renderInlineMarkup(item)}</li>`).join("")}</ol>`);
3214
+ continue;
3215
+ }
3216
+ const paragraphLines = [];
3217
+ while (index < lines.length) {
3218
+ const current = lines[index];
3219
+ if (!current.trim()) {
3220
+ break;
3221
+ }
3222
+ if (isBlockBoundaryLine(current)) {
3223
+ break;
3224
+ }
3225
+ paragraphLines.push(current.trimEnd());
3226
+ index += 1;
3227
+ }
3228
+ if (paragraphLines.length > 0) {
3229
+ blocks.push(`<p>${paragraphLines.map((entry) => renderInlineMarkup(entry)).join("<br/>")}</p>`);
3230
+ continue;
3231
+ }
3232
+ index += 1;
3233
+ }
3234
+ return blocks.join("");
3235
+ }
3236
+ function isBlockBoundaryLine(line) {
3237
+ const trimmed = line.trim();
3238
+ if (!trimmed) {
3239
+ return false;
3240
+ }
3241
+ return /^(#{1,6})\s+/.test(trimmed) || /^(?:-{3,}|\*{3,}|_{3,})$/.test(trimmed) || /^>\s?/.test(trimmed) || /^\s*[-*]\s+/.test(trimmed) || /^\s*\d+\.\s+/.test(trimmed);
3242
+ }
3243
+ function renderInlineMarkup(raw) {
3244
+ if (!raw) {
3245
+ return "";
3246
+ }
3247
+ const inlineCodeSegments = [];
3248
+ const withPlaceholders = raw.replace(/`([^`\n]+)`/g, (_match, code) => {
3249
+ const token = `@@CHCODE${inlineCodeSegments.length}@@`;
3250
+ inlineCodeSegments.push(`<code>${escapeHtml(code)}</code>`);
3251
+ return token;
3252
+ });
3253
+ let rendered = escapeHtml(withPlaceholders);
3254
+ rendered = rendered.replace(/\[([^\]\n]+)\]\((https?:\/\/[^\s)]+)\)/g, (_match, label, url) => {
3255
+ const safeUrl = sanitizeLinkUrl(url);
3256
+ if (!safeUrl) {
3257
+ return escapeHtml(label);
3258
+ }
3259
+ return `<a href="${escapeHtml(safeUrl)}">${escapeHtml(label)}</a>`;
3260
+ });
3261
+ rendered = rendered.replace(/\*\*([^*\n]+)\*\*/g, "<strong>$1</strong>");
3262
+ rendered = rendered.replace(/(^|[^*])\*([^*\n]+)\*/g, "$1<em>$2</em>");
3263
+ rendered = rendered.replace(/(^|[^_])_([^_\n]+)_/g, "$1<em>$2</em>");
3264
+ for (let i = 0; i < inlineCodeSegments.length; i += 1) {
3265
+ rendered = rendered.replace(`@@CHCODE${i}@@`, inlineCodeSegments[i]);
3266
+ }
3165
3267
  return rendered;
3166
3268
  }
3269
+ function sanitizeLinkUrl(url) {
3270
+ try {
3271
+ const parsed = new URL(url);
3272
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
3273
+ return null;
3274
+ }
3275
+ return parsed.toString();
3276
+ } catch {
3277
+ return null;
3278
+ }
3279
+ }
3167
3280
  function escapeHtml(value) {
3168
3281
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
3169
3282
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeharbor",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "description": "Instant-messaging bridge for Codex CLI sessions",
5
5
  "license": "MIT",
6
6
  "main": "dist/cli.js",