dsh-jira-tasks 1.1.0 → 1.2.0
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/README.en.md +8 -9
- package/README.md +10 -9
- package/lib/client.js +65 -21
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
[中文](README.md) · **English**
|
|
6
6
|
|
|
7
|
-
Shows the current JIRA project's **open / reopened** issues **assigned to the current user** below the DSH composer input. The JIRA base URL and token are configured in **Settings →
|
|
7
|
+
Shows the current JIRA project's **open / reopened** issues **assigned to the current user** below the DSH composer input. The JIRA base URL and token are configured in **Settings → JIRA 配置** (the settings dialog's left navigation) (`JIRA_BASE_URL` / `JIRA_API_TOKEN` act as fallback); the project key and JQL are **configured per workspace** and persisted.
|
|
8
8
|
|
|
9
|
-
> **DSH version targeted: 0.1.7 (verified on `0.1.7-rc.2`).** The client slots `conversation.input.dock` / `plugins.item`, the settings service `ctx.configForms` (namespace = profile entry id `jira-tasks`), and the `ctx.effect` / `configForms.whileServed` disposal contracts all follow the 0.1.7 interfaces; the 0.1.6-era `settingsScope` / `settings.register` / `settings.plugin.item` APIs are gone and no longer used.
|
|
9
|
+
> **DSH version targeted: 0.1.7 (verified on `0.1.7-rc.2`).** The client slots `conversation.input.dock` / `plugins.item` / `settings.section`, the settings service `ctx.configForms` (namespace = profile entry id `jira-tasks`), and the `ctx.effect` / `configForms.whileServed` disposal contracts all follow the 0.1.7 interfaces; the 0.1.6-era `settingsScope` / `settings.register` / `settings.plugin.item` APIs are gone and no longer used.
|
|
10
10
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
13
13
|
- 📋 Panel shown below the composer in both new and active sessions (aligned with the input width in new sessions)
|
|
14
14
|
- 👤 Defaults to the current user (`assignee = currentUser()`) with status `开启 / 重新开启` (Open / Reopened)
|
|
15
|
-
- ⚙️ **Settings →
|
|
15
|
+
- ⚙️ **Settings → JIRA 配置** (left navigation, below Agent presets) edits the base URL and access token (the token is written to the credential store and never sent back to the browser); the same JIRA card stays available in the Plugins panel
|
|
16
16
|
- 🟢 The card auto-probes the connection and shows a status light: green = usable, red = unusable, grey = unconfigured; **Test connection** verifies unsaved drafts immediately
|
|
17
17
|
- ⚙️ Project key and JQL are saved per workspace; unconfigured workspaces show "unconfigured"
|
|
18
18
|
- 🔄 Auto-query on every new session, with a one-click refresh (⟳)
|
|
@@ -62,7 +62,7 @@ In a DSH session, use the Cordis tools: `cordis_define` (`kind: new`, `idPrefix:
|
|
|
62
62
|
|
|
63
63
|
### 1. JIRA base URL and token
|
|
64
64
|
|
|
65
|
-
Open **Settings →
|
|
65
|
+
Open **Settings → JIRA 配置** (the last item in the left navigation, below Agent presets; the JIRA card in the Plugins panel is the same form) and fill in:
|
|
66
66
|
|
|
67
67
|
- **JIRA base URL**: e.g. `http://jira.example.com/` (written to this plugin entry's `Config.baseUrl` — the `jira-tasks` row in the profile's `cordis.patch.yml` — and read back by the form; a host-refused write now surfaces as an error instead of failing silently)
|
|
68
68
|
- **Access token / PAT**: written to the credential store (`$DSH_HOME/.credentials.yaml`) under the plugin-owned ref `JIRA_TASKS_TOKEN`; the browser only ever sees "configured", never the token itself
|
|
@@ -163,13 +163,12 @@ JIRA_TASKS_TOKEN (settings card) > JIRA_API_TOKEN > JIRA_TOKEN
|
|
|
163
163
|
│ on mount/refresh: fetch POST │ │ credentials.resolve(TOKEN_REFS) │
|
|
164
164
|
│ render: list / error / unconfigured │ │ subprocess.spawn(curl ...) │
|
|
165
165
|
│ localStorage: per-workspace key/JQL │ │ parse stdout JSON │
|
|
166
|
-
│ plugins.item (
|
|
166
|
+
│ plugins.item (Plugins panel card) │ │ return {ok, issues} │
|
|
167
|
+
│ settings.section (Settings nav page) │ │ │
|
|
167
168
|
└──────────────────────────────────────────────┘ └────────────────────────────────────┘
|
|
168
|
-
└────────────────────────────────┘
|
|
169
|
-
```
|
|
170
169
|
|
|
171
170
|
- **Host**: declares the entry's own `Config` (`baseUrl`, `volatile` — since DSH 0.1.5 a settings page is built from it; `settings.configure({ auto: false }, ctx.fiber)` turns the auto-generated page off and hands its disposer back to `ctx.effect`) plus `webServer` routes `POST /jira/api/search` and `POST /jira/api/test`, each wrapped in its own `ctx.effect` (a reload otherwise hits "duplicate route" and fails activation; non-POST requests get 405); the token comes from the `credentials` service resolved in the order `JIRA_TASKS_TOKEN` (written by the Settings card) → `JIRA_API_TOKEN` → `JIRA_TOKEN` (`$DSH_HOME/.credentials.yaml` / environment, hot-reloaded), so a saved card token overrides the environment; queries run through `subprocess` spawning `curl` directly, with the auth header passed via stdin (`--config -`) so the token never appears in argv.
|
|
172
|
-
- **Client**: a standard `window.__ModuleLoader__.load({ id, factory })` web bundle requiring only `"react"`; registers **only `conversation.input.dock`** (`order: 10`) — that seat renders in both new and active sessions and is a full-width row of `composerStack` (`flex-direction: column`), where the panel's own CSS `order: 99` places it below the input card at the card's width. `conversation.composer.dock` is deliberately NOT used: in 0.1.7 it renders into InputBar's `.dock` **row**, side by side with the context meter, so a full-width panel there gets its right-hand content covered by the meter.
|
|
171
|
+
- **Client**: a standard `window.__ModuleLoader__.load({ id, factory })` web bundle requiring only `"react"`; registers **only `conversation.input.dock`** (`order: 10`) — that seat renders in both new and active sessions and is a full-width row of `composerStack` (`flex-direction: column`), where the panel's own CSS `order: 99` places it below the input card at the card's width. `conversation.composer.dock` is deliberately NOT used: in 0.1.7 it renders into InputBar's `.dock` **row**, side by side with the context meter, so a full-width panel there gets its right-hand content covered by the meter. The address/token form comes from `ctx.configForms.get("jira-tasks")` and the token is written through `remote.credentials` to `JIRA_TASKS_TOKEN`; `configForms.whileServed` keeps it hidden when the host serves no such namespace. It is registered in **two places**: the Plugins-panel card `plugins.item` (`id: "jira-tasks"`, `order: 41`) and a Settings-navigation section `settings.section` (`id: "jira-tasks"`, `order: 30` — above Agent presets' 20, so it sits below it; `label: "JIRA 配置"`; ids outside the shell's `navIcon` allow-list fall back to the default gear icon). Both share one `JiraSettingsPage`, whose outer container is chosen by the external `variant: "page"` prop (`li.jt-set-card` card vs `div.jt-set-page` page) while the field block is the same array. The card's `summary` view is rendered by a dispatcher that calls **no hooks**, while the form lives in a separate `JiraSettingsPage`, so flipping `view` on one mounted instance never changes its hook count.
|
|
173
172
|
- **Why not the `shell` service**: `shell` wraps commands with `sandbox-exec`, which is broken on some macOS versions (`sandbox_apply: Operation not permitted`); `subprocess` is the raw process seam without this issue.
|
|
174
173
|
- **One registration covers both states**: `conversation.input.dock` renders whenever a session and its input exist, so new and active sessions need no separate registrations (the older `composer.dock` + blank-session de-duplication was both unnecessary under 0.1.7 and the cause of the meter-row collision).
|
|
175
174
|
|
|
@@ -180,7 +179,7 @@ JIRA_TASKS_TOKEN (settings card) > JIRA_API_TOKEN > JIRA_TOKEN
|
|
|
180
179
|
| Persistence | Lost on restart | Survives restart |
|
|
181
180
|
| Client→Host | `host.call` / `harness.handle` | `webServer` route + `fetch` |
|
|
182
181
|
| Client bundle | Injected per session | `/plugins/dsh-jira-tasks/client.js` |
|
|
183
|
-
| Config / credentials | Env / `.credentials.yaml` only (no Settings card) | Settings card + same `.credentials.yaml` fallback |
|
|
182
|
+
| Config / credentials | Env / `.credentials.yaml` only (no Settings card) | Settings nav page + Plugins-panel card + same `.credentials.yaml` fallback |
|
|
184
183
|
</details>
|
|
185
184
|
|
|
186
185
|
## License
|
package/README.md
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
**中文** · [English](README.en.md)
|
|
6
6
|
|
|
7
|
-
在 DSH 会话**输入框下方**展示当前 JIRA 项目**指派给当前用户**的「开启 / 重新开启」任务列表。JIRA 地址与令牌在**设置 →
|
|
7
|
+
在 DSH 会话**输入框下方**展示当前 JIRA 项目**指派给当前用户**的「开启 / 重新开启」任务列表。JIRA 地址与令牌在**设置 → JIRA 配置**(设置对话框左侧导航)中配置(`JIRA_BASE_URL` / `JIRA_API_TOKEN` 作为回退);项目 Key 与 JQL **按工作区配置**并持久化。
|
|
8
8
|
|
|
9
|
-
> **适配的 DSH 版本:0.1.7(`0.1.7-rc.2` 实测)。** 客户端槽位 `conversation.input.dock` / `plugins.item`、设置服务 `ctx.configForms`(命名空间 = profile 条目 id `jira-tasks`)、`ctx.effect` / `configForms.whileServed` 的注销契约均按 0.1.7 的接口实现;0.1.6 及更早版本的 `settingsScope` / `settings.register` / `settings.plugin.item` 已不再使用。
|
|
9
|
+
> **适配的 DSH 版本:0.1.7(`0.1.7-rc.2` 实测)。** 客户端槽位 `conversation.input.dock` / `plugins.item` / `settings.section`、设置服务 `ctx.configForms`(命名空间 = profile 条目 id `jira-tasks`)、`ctx.effect` / `configForms.whileServed` 的注销契约均按 0.1.7 的接口实现;0.1.6 及更早版本的 `settingsScope` / `settings.register` / `settings.plugin.item` 已不再使用。
|
|
10
10
|
|
|
11
11
|
## 功能
|
|
12
12
|
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
<img width="1972" height="746" alt="image" src="https://github.com/user-attachments/assets/9f543e04-a5ad-4b86-85c8-baa2de26f44e" />
|
|
17
17
|
|
|
18
18
|
- 👤 默认仅显示当前用户(`assignee = currentUser()`)的「开启 / 重新开启」任务
|
|
19
|
-
- ⚙️ **设置 →
|
|
19
|
+
- ⚙️ **设置 → JIRA 配置**(设置对话框左侧导航,位于「Agent 预设」下方)配置 JIRA 地址与访问令牌(令牌写入凭据存储,不回传前端);插件面板里的同一张 JIRA 卡片仍然可用
|
|
20
20
|
- 🟢 设置卡片自动探测连接并显示状态灯:绿=可用、红=不可用、灰=未配置;点「测试连接」可用**未保存的草稿值**即时验证
|
|
21
21
|
|
|
22
22
|
<img width="1590" height="1574" alt="image" src="https://github.com/user-attachments/assets/af5f11c6-aa9a-4c3b-84ca-cff6c0017b22" />
|
|
@@ -71,7 +71,7 @@ dsh plugin --profile web add github:liu3734/jira-tasks-dsh-plugin
|
|
|
71
71
|
|
|
72
72
|
### 1. JIRA 地址与令牌
|
|
73
73
|
|
|
74
|
-
打开 **设置 →
|
|
74
|
+
打开 **设置 → JIRA 配置**(左侧导航最后一项,在「Agent 预设」下面;插件面板里的 JIRA 卡片是同一张表单),填写:
|
|
75
75
|
|
|
76
76
|
- **JIRA 地址**:如 `http://jira.example.com/`(写入本插件 profile 条目 `jira-tasks` 的 `Config.baseUrl`,即 profile 的 `cordis.patch.yml`,可在界面回读;保存被宿主拒绝时会直接报错,不会静默失败)
|
|
77
77
|
- **访问令牌 / PAT**:写入凭据存储(`$DSH_HOME/.credentials.yaml`,引用名是插件自有的 `JIRA_TASKS_TOKEN`),前端只显示"已配置",不回传令牌本身
|
|
@@ -134,8 +134,8 @@ dsh plugin --profile web remove dsh-jira-tasks
|
|
|
134
134
|
| 提示 | 处理 |
|
|
135
135
|
|---|---|
|
|
136
136
|
| 未设置项目 Key | 面板未配置项目,点标题右侧 ⚙ 填写项目 Key |
|
|
137
|
-
| 未配置 JIRA 地址(设置 →
|
|
138
|
-
| 未配置 JIRA 令牌(设置 →
|
|
137
|
+
| 未配置 JIRA 地址(设置 → JIRA 配置,或环境变量 JIRA_BASE_URL) | 地址未写入,见上文「配置 1」 |
|
|
138
|
+
| 未配置 JIRA 令牌(设置 → JIRA 配置,或环境变量 JIRA_API_TOKEN) | 令牌未写入,见上文「配置 1」 |
|
|
139
139
|
| 401 … | 令牌无效或认证方式不对;先 `curl -H "Authorization: Bearer <token>" <base>/rest/api/2/myself` 验证 |
|
|
140
140
|
| 无法解析 JIRA 响应:… | 网络 / 代理问题,curl 无输出 |
|
|
141
141
|
</details>
|
|
@@ -175,12 +175,13 @@ JIRA_TASKS_TOKEN(设置页) > JIRA_API_TOKEN > JIRA_TOKEN
|
|
|
175
175
|
│ ↓ 挂载 / 刷新时 fetch POST │ │ credentials.resolve(TOKEN_REFS) │
|
|
176
176
|
│ 渲染:任务列表 / 错误 / 未配置 │ │ subprocess.spawn(curl …) │
|
|
177
177
|
│ localStorage 按工作区存取项目 Key/JQL │ │ ↓ stdout JSON │
|
|
178
|
-
│ plugins.item
|
|
178
|
+
│ plugins.item(插件面板卡片) │ │ 解析 issues → 返回 {ok,issues} │
|
|
179
|
+
│ settings.section(设置导航整页) │ │ │
|
|
179
180
|
└────────────────────────────────────────┘ └────────────────────────────────────┘
|
|
180
181
|
```
|
|
181
182
|
|
|
182
183
|
- **Host**:声明条目自身的 `Config`(`baseUrl`,`volatile`,DSH 0.1.5 起设置页的表单直接来自它;`settings.configure({ auto: false }, ctx.fiber)` 关掉自动生成页,并把返回的注销函数交回 `ctx.effect`)与 `webServer` 路由 `POST /jira/api/search`、`POST /jira/api/test`(两条路由各自包在 `ctx.effect` 里,卸载/重载时先释放,否则重挂载会撞上「重复路由」直接抛错;非 POST 返回 405);令牌经 `credentials` 服务按 `JIRA_TASKS_TOKEN`(设置页写入)→ `JIRA_API_TOKEN` → `JIRA_TOKEN` 的顺序解析(`$DSH_HOME/.credentials.yaml` / 环境变量,热加载),因此设置页保存的令牌能覆盖环境变量;查询用 `subprocess` 直接 `spawn curl`,认证头经 stdin(`--config -`)传入,令牌不进入命令行参数。
|
|
183
|
-
- **Client**:`window.__ModuleLoader__.load({ id, factory })` 标准 web bundle,仅 `require("react")`;**只注册 `conversation.input.dock` 一处**(`order: 10`)。该槽位在新会话与活跃会话下都会渲染,属于 `composerStack`(`flex-direction: column`)的整宽纵向行;面板元素自身 `flex order: 99` 排到输入卡之后,即输入框下方,并以 `--dsh-composer-side-clearance` / `--dsh-composer-card-max-width` 与输入卡等宽。之所以不用 `conversation.composer.dock`:0.1.7 里那一槽位渲染进 InputBar 的 `.dock`**横向** flex
|
|
184
|
+
- **Client**:`window.__ModuleLoader__.load({ id, factory })` 标准 web bundle,仅 `require("react")`;**只注册 `conversation.input.dock` 一处**(`order: 10`)。该槽位在新会话与活跃会话下都会渲染,属于 `composerStack`(`flex-direction: column`)的整宽纵向行;面板元素自身 `flex order: 99` 排到输入卡之后,即输入框下方,并以 `--dsh-composer-side-clearance` / `--dsh-composer-card-max-width` 与输入卡等宽。之所以不用 `conversation.composer.dock`:0.1.7 里那一槽位渲染进 InputBar 的 `.dock`**横向** flex 行,与上下文占用环并排,整宽面板会被占用环压住右侧内容。插件地址与令牌的表单来自 `ctx.configForms.get("jira-tasks")`(地址)与 `remote.credentials`(令牌写入 `JIRA_TASKS_TOKEN`),并用 `configForms.whileServed` 保证宿主未提供该命名空间时不显示。表单**注册在两处**:插件面板卡片 `plugins.item`(`id: "jira-tasks"`,`order: 41`,原入口保留)与设置对话框左侧导航项 `settings.section`(`id: "jira-tasks"`,`order: 30` —— 大于「Agent 预设」的 20,所以显示在它下方;`label: "JIRA 配置"`;未在 `navIcon` 白名单里的 id 由设置外壳回退成默认齿轮图标)。两者共用同一个 `JiraSettingsPage`:由外部 prop `variant: "page"` 决定外层容器(`li.jt-set-card` 卡片 / `div.jt-set-page` 整页),字段区是同一份数组。`plugins.item` 的摘要位由一个**不调用任何 hook** 的分发组件负责,表单在独立的 `JiraSettingsPage` 里,避免同一实例在 `view` 切换时改变 hook 数量。
|
|
184
185
|
- **为什么不用 `shell` 服务**:`shell` 会套 `sandbox-exec`,部分 macOS 上不可用(`sandbox_apply: Operation not permitted`);`subprocess` 是原始进程缝,无此问题。
|
|
185
186
|
- **一处注册覆盖两种会话**:`conversation.input.dock` 只要有 session + input 就会渲染,新会话与活跃会话无需分别注册(旧版曾用 `composer.dock` + 空白判定去重,0.1.7 下既不必要、又会与占用环抢同一行)。
|
|
186
187
|
|
|
@@ -191,7 +192,7 @@ JIRA_TASKS_TOKEN(设置页) > JIRA_API_TOKEN > JIRA_TOKEN
|
|
|
191
192
|
| 持久性 | 重启丢失 | 重启保留 |
|
|
192
193
|
| Client→Host 通信 | `host.call` / `harness.handle` | `webServer` 路由 + `fetch` |
|
|
193
194
|
| 客户端 bundle | 会话内注入 | `/plugins/dsh-jira-tasks/client.js` |
|
|
194
|
-
| 配置 / 凭据 | 仅环境变量 / `.credentials.yaml
|
|
195
|
+
| 配置 / 凭据 | 仅环境变量 / `.credentials.yaml`(无设置页表单) | 设置导航「JIRA 配置」页 + 插件面板卡片 + 同一 `.credentials.yaml` 回退 |
|
|
195
196
|
</details>
|
|
196
197
|
|
|
197
198
|
## License
|
package/lib/client.js
CHANGED
|
@@ -53,7 +53,12 @@ window.__ModuleLoader__.load({
|
|
|
53
53
|
+ ".jt-dot{flex:none;width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-label-secondary, #999)}"
|
|
54
54
|
+ ".jt-dot-ok{background:var(--dsw-alias-state-success-primary, #1a9d4f)}"
|
|
55
55
|
+ ".jt-dot-fail{background:var(--dsw-alias-state-error-primary, #d93026)}"
|
|
56
|
-
+ ".jt-dot-testing{background:var(--dsw-alias-state-warn-primary, #b76e00)}"
|
|
56
|
+
+ ".jt-dot-testing{background:var(--dsw-alias-state-warn-primary, #b76e00)}"
|
|
57
|
+
// 设置对话框左侧导航的整页形态(settings.section):
|
|
58
|
+
+ ".jt-set-page{box-sizing:border-box;width:100%;max-width:760px;display:flex;flex-direction:column;gap:12px}"
|
|
59
|
+
+ ".jt-page-title{margin:0;font-size:18px;font-weight:600;line-height:26px;color:var(--dsw-alias-label-primary, #222)}"
|
|
60
|
+
+ ".jt-page-intro{margin:0;font-size:13px;line-height:20px;color:var(--dsw-alias-label-secondary, #666)}"
|
|
61
|
+
+ ".jt-set-form{display:flex;flex-direction:column}";
|
|
57
62
|
|
|
58
63
|
var STORAGE_KEY = "dsh.jiraTasks.config.v1";
|
|
59
64
|
|
|
@@ -307,7 +312,9 @@ window.__ModuleLoader__.load({
|
|
|
307
312
|
return snapshot;
|
|
308
313
|
}
|
|
309
314
|
|
|
310
|
-
|
|
315
|
+
// 返回两处渲染位的组件:{ card } 给 plugins.item,{ section } 给 settings.section。
|
|
316
|
+
// 两者共用同一个 JiraSettingsPage 与同一份 scope / credentials 句柄。
|
|
317
|
+
function createJiraSettingsSurfaces(scope, credentials) {
|
|
311
318
|
function readTokenInfo() {
|
|
312
319
|
return credentials.describe(TOKEN_REFS).then(function (res) {
|
|
313
320
|
if (!res || !res.ok || !res.value) return { configured: false, writable: true };
|
|
@@ -337,11 +344,15 @@ window.__ModuleLoader__.load({
|
|
|
337
344
|
});
|
|
338
345
|
}
|
|
339
346
|
|
|
340
|
-
//
|
|
341
|
-
//
|
|
342
|
-
//
|
|
343
|
-
//
|
|
344
|
-
|
|
347
|
+
// 同一个表单服务两处渲染位:
|
|
348
|
+
// - plugins.item:列表里的卡片摘要(view="summary")与点开后的详情(其余 view);
|
|
349
|
+
// - settings.section:设置导航里的整页(variant="page")。
|
|
350
|
+
// 分发组件自身不调用任何 hook,钩子全部留在 JiraSettingsPage 里,这样同一个实例
|
|
351
|
+
// 的 hook 数量与 view 取值无关——这是 React 的硬性要求,早期版本把摘要提前 return
|
|
352
|
+
// 写在 hook 之前会踩这条规则。
|
|
353
|
+
function JiraSettingsPage(props) {
|
|
354
|
+
// "page" = 设置对话框左侧导航的整页(settings.section);默认 = 插件面板里的卡片。
|
|
355
|
+
var asPage = !!(props && props.variant === "page");
|
|
345
356
|
var snapshot = useScopeSnapshot(scope);
|
|
346
357
|
var tokenInfoState = React.useState(null);
|
|
347
358
|
var tokenInfo = tokenInfoState[0], setTokenInfo = tokenInfoState[1];
|
|
@@ -479,11 +490,9 @@ window.__ModuleLoader__.load({
|
|
|
479
490
|
: probe.phase === "fail" ? "jt-dot jt-dot-fail"
|
|
480
491
|
: probe.phase === "testing" ? "jt-dot jt-dot-testing" : "jt-dot";
|
|
481
492
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
h("span", { className: "jt-set-desc" }, "JIRA 地址与访问令牌。项目 Key / JQL 仍按工作区在会话面板的 ⚙ 中配置。")
|
|
486
|
-
),
|
|
493
|
+
// 字段区在两种形态下完全一致,只有外层容器不同:插件面板卡片(li.jt-set-card)
|
|
494
|
+
// vs 设置导航整页(div.jt-set-page)。h.apply 用来展开字段数组,保持 ES5 风格。
|
|
495
|
+
var fields = [
|
|
487
496
|
h("div", { className: "jt-field" },
|
|
488
497
|
h("label", { htmlFor: "jira-tasks-base-url" }, "JIRA 地址"),
|
|
489
498
|
h("input", {
|
|
@@ -531,14 +540,36 @@ window.__ModuleLoader__.load({
|
|
|
531
540
|
h("button", { className: "jt-btn", disabled: !dirty || busy, onClick: discard }, "放弃"),
|
|
532
541
|
h("button", { className: "jt-btn jt-btn-primary", disabled: !dirty || busy || (baseDirty && !writable) || (tokenDirty && !tokenWritable), onClick: save }, busy ? "保存中…" : "保存")
|
|
533
542
|
)
|
|
534
|
-
|
|
543
|
+
];
|
|
544
|
+
|
|
545
|
+
if (asPage) {
|
|
546
|
+
return h("div", { className: "jt-set-page" },
|
|
547
|
+
h("h2", { className: "jt-page-title" }, "JIRA 配置"),
|
|
548
|
+
h("p", { className: "jt-page-intro" }, "JIRA 地址与访问令牌(令牌写入凭据存储,不回传前端)。项目 Key / JQL 仍按工作区在会话面板的 ⚙ 中配置。"),
|
|
549
|
+
h.apply(null, ["div", { className: "jt-set-form" }].concat(fields))
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
return h.apply(null, ["li", { className: "jt-set-card" },
|
|
554
|
+
h("div", { className: "jt-set-head" },
|
|
555
|
+
h("span", { className: "jt-set-title" }, "JIRA"),
|
|
556
|
+
h("span", { className: "jt-set-desc" }, "JIRA 地址与访问令牌。项目 Key / JQL 仍按工作区在会话面板的 ⚙ 中配置。")
|
|
557
|
+
)
|
|
558
|
+
].concat(fields));
|
|
535
559
|
}
|
|
536
560
|
|
|
537
|
-
//
|
|
538
|
-
|
|
561
|
+
// 槽位组件:摘要位只回一行文字,详情位挂载真正的表单(不调用任何 hook)。
|
|
562
|
+
function JiraSettingsCard(props) {
|
|
539
563
|
if (props && props.view === "summary") return "JIRA 地址、令牌与连接状态";
|
|
540
564
|
return h(JiraSettingsPage, null);
|
|
541
|
-
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// 设置对话框左侧导航的一项(settings.section):整页形态的同一个表单。
|
|
568
|
+
function JiraSettingsSection() {
|
|
569
|
+
return h(JiraSettingsPage, { variant: "page" });
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
return { card: JiraSettingsCard, section: JiraSettingsSection };
|
|
542
573
|
}
|
|
543
574
|
|
|
544
575
|
function apply(ctx) {
|
|
@@ -566,20 +597,33 @@ window.__ModuleLoader__.load({
|
|
|
566
597
|
return slots.register({ name: "conversation.input.dock", id: "jira-open-tasks", order: 10 }, JiraTasksDock);
|
|
567
598
|
});
|
|
568
599
|
|
|
569
|
-
//
|
|
570
|
-
//
|
|
571
|
-
//
|
|
600
|
+
// 设置表单的两处注册。DSH 0.1.5+ :设置表单的服务是 configForms,命名空间就是
|
|
601
|
+
// 本插件的 profile 条目 id(jira-tasks);两处都只在宿主确实提供了该命名空间
|
|
602
|
+
// 时才出现(whileServed 的 disposer 交回各自的 effect)。
|
|
603
|
+
//
|
|
604
|
+
// 1) 插件面板卡片(plugins.item):插件自己的配置项,原来就在这里。
|
|
605
|
+
// 2) 设置对话框左侧导航项(settings.section):id/order/label 是导航身份,
|
|
606
|
+
// order 大于 Agent 预设(20),所以排在它下面;label 直接给字符串
|
|
607
|
+
// (槽位用 resolveSlotLabel 读,字符串与 thunk 都接受)。未知 id 由设置
|
|
608
|
+
// 外壳回退到默认齿轮图标,无需注册图标。
|
|
572
609
|
var configForms = ctx.get("configForms");
|
|
573
610
|
var remote = ctx.get("remote");
|
|
574
611
|
if (configForms && remote && remote.credentials) {
|
|
575
|
-
var
|
|
612
|
+
var surfaces = createJiraSettingsSurfaces(configForms.get(SETTINGS_NS), remote.credentials);
|
|
576
613
|
ctx.effect(function () {
|
|
577
614
|
return configForms.whileServed([SETTINGS_NS], function () {
|
|
578
615
|
return slots.inject("plugins.item", function () {
|
|
579
|
-
return slots.register({ name: "plugins.item", id: SETTINGS_NS, order: 41, label: "JIRA" }, card);
|
|
616
|
+
return slots.register({ name: "plugins.item", id: SETTINGS_NS, order: 41, label: "JIRA" }, surfaces.card);
|
|
580
617
|
});
|
|
581
618
|
});
|
|
582
619
|
}, "jira-tasks: plugins page card");
|
|
620
|
+
ctx.effect(function () {
|
|
621
|
+
return configForms.whileServed([SETTINGS_NS], function () {
|
|
622
|
+
return slots.inject("settings.section", function () {
|
|
623
|
+
return slots.register({ name: "settings.section", id: SETTINGS_NS, order: 30, label: "JIRA 配置" }, surfaces.section);
|
|
624
|
+
});
|
|
625
|
+
});
|
|
626
|
+
}, "jira-tasks: settings section");
|
|
583
627
|
}
|
|
584
628
|
}
|
|
585
629
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-jira-tasks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "JIRA open tasks panel for DeepSeek Harness (DSH) 0.1.7: shows the current user's open/reopened issues below the composer, per-workspace project key, persistent profile bundle.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|