ework-web 0.10.119 → 0.10.121

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": "ework-web",
3
- "version": "0.10.119",
3
+ "version": "0.10.121",
4
4
  "type": "module",
5
5
  "description": "ework-web — standalone multi-project issue tracker. Local SQLite-backed, no external API dependency. Bun + TypeScript + SSR HTML.",
6
6
  "license": "MIT",
package/src/fileview.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { realpathSync, statSync, readFileSync, openSync, readSync, closeSync, readdirSync } from "fs";
2
- import { isAbsolute, relative, join, dirname } from "path";
2
+ import { isAbsolute, relative, join, dirname, resolve } from "path";
3
3
  import hljs from "highlight.js";
4
4
  import { THEME_CSS, escapeHtml, escapeAttr } from "./render/layout";
5
5
  import { renderMarkdown } from "./render/markdown";
@@ -472,6 +472,20 @@ function buildDownloadView(cfg: Config, rp: string): { html: string } {
472
472
  return { html };
473
473
  }
474
474
 
475
+ export // Repo-relative refs (figures/x.png) in /file-rendered markdown 404 against /file;
476
+ // rewritten targets re-pass the allowlist+denylist gate at request time.
477
+ const REL_REF_RE = /(\s(?:src|href)=")([^"]+)(")/g;
478
+ const SKIP_REF_RE = /^(?:[a-z][a-z0-9+.-]*:|\/|#)/i;
479
+
480
+ export function rewriteRelativeRefs(html: string, dir: string): string {
481
+ return html.replace(REL_REF_RE, (full: string, pre: string, ref: string, post: string) => {
482
+ if (SKIP_REF_RE.test(ref)) return full;
483
+ const abs = resolve(dir, ref);
484
+ const q = encodeURIComponent(abs);
485
+ return pre + (pre.includes("src") ? `/file/raw?path=${q}` : `/file?path=${q}`) + post;
486
+ });
487
+ }
488
+
475
489
  export function buildFileView(
476
490
  cfg: Config,
477
491
  rawPath: string,
@@ -505,8 +519,9 @@ export function buildFileView(
505
519
  const lang = extToLang(rawPath);
506
520
  const shownBytes = chunk.rows.reduce((s, r) => s + r.t.length + 1, 0);
507
521
  const fullText = chunk.rows.map((r) => r.t).join("\n");
522
+ const dir = dirname(rawPath);
508
523
  const body = mdRender
509
- ? `<div class="md-render">${renderMarkdown(fullText, "")}</div>`
524
+ ? `<div class="md-render">${rewriteRelativeRefs(renderMarkdown(fullText, dir), dir)}</div>`
510
525
  : lang && shownBytes <= 100000
511
526
  ? renderHighlighted(chunk, lang)
512
527
  : `<pre><code>${chunk.rows.map((r) => lineRow(r.t, r.n)).join("")}</code></pre>`;
@@ -11,6 +11,7 @@ import {
11
11
  ensureUser,
12
12
  postComment,
13
13
  editIssue,
14
+ updateIssueAiStatus,
14
15
  updateUpstreamSyncState,
15
16
  listEnabledUpstreamSyncs,
16
17
  } from "./store";
@@ -155,6 +156,10 @@ export class UpstreamSync {
155
156
  const target: "open" | "closed" = gi.state === "closed" ? "closed" : "open";
156
157
  if (existing.state === target) return false;
157
158
  await editIssue(existing.id, { state: target });
159
+ // stale AI badges on closed rows read as noise on the issue list
160
+ if (target === "closed" && existing.ai_status && existing.ai_status !== "completed") {
161
+ await updateIssueAiStatus(existing.id, "");
162
+ }
158
163
  if (emit) {
159
164
  void emitIssueEvent(this.project.id, existing.id, target === "closed" ? "closed" : "reopened", this.origin);
160
165
  }