ework-web 0.10.116 → 0.10.118

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.116",
3
+ "version": "0.10.118",
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",
@@ -246,7 +246,7 @@ export function renderLayout(props: LayoutProps, inner: string, initialItems: st
246
246
  <span style="opacity:.5">/</span>
247
247
  <a href="${escapeAttr(repoIssuesHref)}" style="color:var(--header-text)">${escapeHtml(props.repoPath)}</a>
248
248
  <span class="num">#${props.issueNumber}</span>
249
- ${props.upstreamUrl ? `<a href="${escapeAttr(props.upstreamUrl)}" target="_blank" rel="noopener" title="upstream" style="color:var(--header-text);text-decoration:none;font-size:.85em">↗</a>` : ""}
249
+ ${props.upstreamUrl ? `<a href="${escapeAttr(props.upstreamUrl)}" target="_blank" rel="noopener" title="跳转到上游项目" style="color:var(--header-text);text-decoration:none;font-size:.85em">↗</a>` : ""}
250
250
  </header>
251
251
  <div class="meta-bar">
252
252
  <h1>${escapeHtml(props.issueTitle)}</h1>
@@ -264,7 +264,7 @@ export function renderLayout(props: LayoutProps, inner: string, initialItems: st
264
264
  }).join("")}</span>` : ""}
265
265
  ${(labelsHtml || labelPickerBtn) ? `<span class="label-group">${labelsHtml}${labelPickerBtn}</span>` : ""}
266
266
  <span class="count" id="count">…</span>
267
- ${props.upstreamWebUrl ? `<a class="upstream-link" href="${escapeAttr(props.upstreamWebUrl)}" target="_blank" rel="noopener noreferrer" title="跳转到上游仓库">🔗 查看上游</a>` : ""}
267
+ ${props.upstreamWebUrl ? `<a class="upstream-link" href="${escapeAttr(props.upstreamWebUrl)}" target="_blank" rel="noopener noreferrer" title="跳转到上游 issue">🔗 查看上游</a>` : ""}
268
268
  </div>
269
269
  </div>
270
270
  ${props.descriptionHtml.trim() ? `<div class="desc-wrap">
package/src/store.ts CHANGED
@@ -451,6 +451,9 @@ export async function listAllIssues(opts: ListIssuesOpts = {}): Promise<IssueWit
451
451
  }
452
452
 
453
453
  export interface CreateIssueOpts {
454
+ /** Preferred local number (upstream imports keep local==upstream). Falls
455
+ * back to autoincrement when the number is taken. */
456
+ number?: number;
454
457
  createdAt?: string;
455
458
  updatedAt?: string;
456
459
  state?: "open" | "closed";
@@ -484,9 +487,15 @@ export async function createIssue(
484
487
  const state: "open" | "closed" = opts.state ?? "open";
485
488
  const closedAt = state === "closed" ? isoOr(opts.closedAt ?? undefined, updatedAt) : null;
486
489
  return await getDB().transaction(async () => {
487
- const next = (await getDB().get<{ n: number }>(
488
- "SELECT COALESCE(MAX(number), 0) + 1 AS n FROM {{issues}} WHERE project_id = ?", [projectId]
489
- ))!;
490
+ const want = opts.number && opts.number > 0 ? opts.number : null;
491
+ const taken = want
492
+ ? await getDB().get<{ id: number }>("SELECT id FROM {{issues}} WHERE project_id = ? AND number = ?", [projectId, want])
493
+ : undefined;
494
+ const next = want && !taken
495
+ ? { n: want }
496
+ : (await getDB().get<{ n: number }>(
497
+ "SELECT COALESCE(MAX(number), 0) + 1 AS n FROM {{issues}} WHERE project_id = ?", [projectId]
498
+ ))!;
490
499
  const info = await getDB().run(
491
500
  "INSERT INTO {{issues}} (project_id, number, title, body, state, author, created_at, updated_at, closed_at, model, runtime, upstream_issue_number) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
492
501
  [projectId, next.n, title, body, state, author, createdAt, updatedAt, closedAt, opts.model ?? "", opts.runtime ?? "", opts.upstreamIssueNumber ?? null]
@@ -139,6 +139,7 @@ export class UpstreamSync {
139
139
  updatedAt: gi.updated_at,
140
140
  state: gi.state === "closed" ? "closed" : "open",
141
141
  upstreamIssueNumber: gi.number,
142
+ number: gi.number,
142
143
  }
143
144
  );
144
145
  // PRs the sandbox agent opens itself carry the ework-agent-pr marker;
@@ -21,7 +21,6 @@ import {
21
21
  type ProjectRow,
22
22
  } from "../store";
23
23
  import { getConfigAll } from "../db";
24
- import { webUrlFromClone } from "./projectUpstreams";
25
24
 
26
25
  export const PAGE_SIZE = 30;
27
26
 
@@ -134,10 +133,14 @@ export async function buildIssueThread(
134
133
  const payload = payloadFromComments(issue, displayViews, currentPage, hasOlder, cfg.commentSort);
135
134
  const descriptionHtml = renderMarkdown(issue.body, "", issuePath);
136
135
  const descriptionCollapsed = issue.body.length > 1200;
136
+ // 🔗 查看上游 points at the mapped upstream ISSUE (falling back to the
137
+ // repo root when this issue has no upstream mapping). The header ↗ is the
138
+ // project-level jump (see upstreamUrl below).
137
139
  const upstreamWebUrl = (() => {
138
- const clone = getDefaultUpstreamUrl(project);
139
- if (!clone) return null;
140
- return webUrlFromClone(clone);
140
+ const base = upstreamRefBase(project);
141
+ if (!base) return null;
142
+ if (issue.upstream_issue_number) return `${base}/issues/${issue.upstream_issue_number}`;
143
+ return base;
141
144
  })();
142
145
  const labels = await listLabelsForIssue(issue.id);
143
146
  const viewerUser = viewerLogin ? await getUserByLogin(viewerLogin) : null;
@@ -192,9 +195,7 @@ export async function buildIssueThread(
192
195
  {
193
196
  title: `${issue.title} · ${owner}/${repo}#${number}`,
194
197
  issueTitle: issue.title,
195
- upstreamUrl: issue.upstream_issue_number && upstreamRefBase(project)
196
- ? `${upstreamRefBase(project)}/issues/${issue.upstream_issue_number}`
197
- : undefined,
198
+ upstreamUrl: upstreamRefBase(project) ?? undefined,
198
199
  repoPath: `${owner}/${repo}`,
199
200
  issueNumber: number,
200
201
  state: issue.state,
@@ -21,7 +21,7 @@ function issueRow(it: IssueWithMeta, q: string, state: string, labels: LabelRow[
21
21
  const modelChip = it.model ? ` · <span class="row-model" title="模型">${escapeHtml(it.model)}</span>` : "";
22
22
  return `<a class="row" href="${escapeAttr(href)}">
23
23
  <div class="row-title">${title}${chips}</div>
24
- <div class="row-meta">${projectStr} · #${it.number} · 💬 ${it.comment_count} · ${relTime(it.updated_at)}${aiBadge ? ` · ${aiBadge}` : ""}${modelChip}</div>
24
+ <div class="row-meta">${projectStr} · #${it.number}${it.author ? ` · <span class="row-author" title="创建者">👤 ${escapeHtml(it.author)}</span>` : ""} · 💬 ${it.comment_count} · ${relTime(it.updated_at)}${aiBadge ? ` · ${aiBadge}` : ""}${modelChip}</div>
25
25
  </a>`;
26
26
  }
27
27