@qnote/q-ai-note 1.0.17 → 1.0.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.
package/dist/web/app.js CHANGED
@@ -804,6 +804,9 @@ function applyReadonlyMode() {
804
804
  closeQuickChatPopover();
805
805
  }
806
806
  applySandboxChatVisibility();
807
+ if (state.selectedNodeId) {
808
+ renderDrawerWorkItemQuickEdit(state.selectedNodeId);
809
+ }
807
810
  if (aserRuntimeView) {
808
811
  aserRuntimeView.setAccessContext({
809
812
  readonly: state.readonly,
@@ -1642,6 +1645,37 @@ function renderQuickDiaryTargetLabel() {
1642
1645
  el.textContent = `快速日记:${state.currentSandbox.name} / ${getWorkItemNameById(nodeId)}`;
1643
1646
  }
1644
1647
 
1648
+ function renderDrawerWorkItemQuickEdit(nodeId) {
1649
+ const nameInput = document.getElementById('drawer-work-item-name');
1650
+ const assigneeInput = document.getElementById('drawer-work-item-assignee');
1651
+ const statusInput = document.getElementById('drawer-work-item-status');
1652
+ const priorityInput = document.getElementById('drawer-work-item-priority');
1653
+ const descriptionInput = document.getElementById('drawer-work-item-description');
1654
+ const saveBtn = document.getElementById('save-drawer-work-item-btn');
1655
+ if (!nameInput || !assigneeInput || !statusInput || !priorityInput || !descriptionInput || !saveBtn) return;
1656
+ const row = getNodeById(nodeId);
1657
+ const canEdit = !state.readonly && state.currentSandboxWritable;
1658
+ if (!row) {
1659
+ nameInput.value = '';
1660
+ assigneeInput.value = '';
1661
+ statusInput.value = 'pending';
1662
+ priorityInput.value = 'medium';
1663
+ descriptionInput.value = '';
1664
+ [nameInput, assigneeInput, statusInput, priorityInput, descriptionInput, saveBtn].forEach((el) => {
1665
+ el.disabled = true;
1666
+ });
1667
+ return;
1668
+ }
1669
+ nameInput.value = String(row.name || '');
1670
+ assigneeInput.value = String(row.assignee || '');
1671
+ statusInput.value = String(row.status || 'pending');
1672
+ priorityInput.value = String(row.priority || 'medium');
1673
+ descriptionInput.value = String(row.description || '');
1674
+ [nameInput, assigneeInput, statusInput, priorityInput, descriptionInput, saveBtn].forEach((el) => {
1675
+ el.disabled = !canEdit;
1676
+ });
1677
+ }
1678
+
1645
1679
  async function saveDiaryEntry({ content, sandboxId = null, workItemId = null }) {
1646
1680
  const payload = {
1647
1681
  sandbox_id: sandboxId,
@@ -2092,6 +2126,7 @@ function showNodeEntityDrawer(nodeId, preferredFilter = 'all') {
2092
2126
  const filter = ['all', 'todo', 'issue', 'knowledge', 'capability', 'diary'].includes(preferredFilter) ? preferredFilter : 'all';
2093
2127
  state.nodeEntityFilter = filter;
2094
2128
  title.textContent = node.name || nodeId;
2129
+ renderDrawerWorkItemQuickEdit(nodeId);
2095
2130
  renderNodeEntitySummary(nodeId);
2096
2131
  renderWorkTree();
2097
2132
  renderQuickDiaryTargetLabel();
@@ -4004,6 +4039,44 @@ async function initApp() {
4004
4039
  setButtonState(btn, { disabled: false, text: originalText || '保存' });
4005
4040
  }
4006
4041
  });
4042
+
4043
+ document.getElementById('save-drawer-work-item-btn')?.addEventListener('click', async () => {
4044
+ if (state.readonly || !state.currentSandboxWritable) return;
4045
+ if (!state.currentSandbox || !state.selectedNodeId) return;
4046
+ const nodeId = String(state.selectedNodeId || '');
4047
+ const current = getNodeById(nodeId);
4048
+ if (!current) return;
4049
+ const name = String(document.getElementById('drawer-work-item-name')?.value || '').trim();
4050
+ if (!name) {
4051
+ alert('请输入任务名称');
4052
+ return;
4053
+ }
4054
+ const payload = {
4055
+ name,
4056
+ description: String(document.getElementById('drawer-work-item-description')?.value || '').trim(),
4057
+ assignee: String(document.getElementById('drawer-work-item-assignee')?.value || '').trim(),
4058
+ status: String(document.getElementById('drawer-work-item-status')?.value || 'pending'),
4059
+ priority: String(document.getElementById('drawer-work-item-priority')?.value || 'medium'),
4060
+ parent_id: current.parent_id || null,
4061
+ extra_data: {
4062
+ ...(current.extra_data || {}),
4063
+ },
4064
+ };
4065
+ const saveBtn = document.getElementById('save-drawer-work-item-btn');
4066
+ const originalText = saveBtn?.textContent || '保存工作项';
4067
+ if (saveBtn) setButtonState(saveBtn, { disabled: true, text: '保存中...' });
4068
+ try {
4069
+ await apiRequest(`${API_BASE}/items/${nodeId}`, {
4070
+ method: 'PUT',
4071
+ body: JSON.stringify(payload),
4072
+ });
4073
+ const activeFilter = state.nodeEntityFilter || 'all';
4074
+ await loadSandbox(state.currentSandbox.id);
4075
+ showNodeEntityDrawer(nodeId, activeFilter);
4076
+ } finally {
4077
+ if (saveBtn) setButtonState(saveBtn, { disabled: false, text: originalText });
4078
+ }
4079
+ });
4007
4080
 
4008
4081
  document.getElementById('rollback-btn')?.addEventListener('click', async () => {
4009
4082
  if (state.readonly) return;
@@ -150,6 +150,30 @@
150
150
  <button class="btn btn-secondary btn-sm" id="close-node-drawer-btn">关闭</button>
151
151
  </div>
152
152
  </div>
153
+ <section class="drawer-section">
154
+ <div class="drawer-section-title">工作项快速编辑</div>
155
+ <div class="drawer-work-item-quick-edit">
156
+ <div class="drawer-work-item-quick-edit-grid">
157
+ <input type="text" id="drawer-work-item-name" placeholder="工作项名称">
158
+ <input type="text" id="drawer-work-item-assignee" placeholder="负责人(可选)">
159
+ <select id="drawer-work-item-status">
160
+ <option value="pending">待处理</option>
161
+ <option value="in_progress">进行中</option>
162
+ <option value="done">已完成</option>
163
+ <option value="archived">已归档</option>
164
+ </select>
165
+ <select id="drawer-work-item-priority">
166
+ <option value="low">低优先级</option>
167
+ <option value="medium">中优先级</option>
168
+ <option value="high">高优先级</option>
169
+ </select>
170
+ </div>
171
+ <textarea id="drawer-work-item-description" rows="3" placeholder="描述(可选)"></textarea>
172
+ <div class="drawer-work-item-quick-edit-actions">
173
+ <button class="btn btn-primary btn-sm" id="save-drawer-work-item-btn" type="button">保存工作项</button>
174
+ </div>
175
+ </div>
176
+ </section>
153
177
  <section class="drawer-section">
154
178
  <div class="drawer-section-title">节点概览</div>
155
179
  <div class="summary-strip compact" id="node-entity-summary"></div>
@@ -3670,6 +3670,37 @@ dialog::backdrop {
3670
3670
  margin-bottom: 8px;
3671
3671
  }
3672
3672
 
3673
+ .drawer-work-item-quick-edit {
3674
+ display: grid;
3675
+ gap: 8px;
3676
+ padding: 10px;
3677
+ background: #f8f9fb;
3678
+ border: 1px solid var(--border);
3679
+ border-radius: 10px;
3680
+ }
3681
+
3682
+ .drawer-work-item-quick-edit-grid {
3683
+ display: grid;
3684
+ grid-template-columns: 1fr 1fr;
3685
+ gap: 8px;
3686
+ }
3687
+
3688
+ .drawer-work-item-quick-edit input,
3689
+ .drawer-work-item-quick-edit select,
3690
+ .drawer-work-item-quick-edit textarea {
3691
+ width: 100%;
3692
+ border: 1px solid var(--border);
3693
+ border-radius: 8px;
3694
+ padding: 8px 10px;
3695
+ font-size: 13px;
3696
+ background: #fff;
3697
+ }
3698
+
3699
+ .drawer-work-item-quick-edit-actions {
3700
+ display: flex;
3701
+ justify-content: flex-end;
3702
+ }
3703
+
3673
3704
  .drawer-create-form {
3674
3705
  display: grid;
3675
3706
  gap: 8px;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qnote/q-ai-note",
3
- "version": "1.0.17",
3
+ "version": "1.0.18",
4
4
  "type": "module",
5
5
  "description": "AI-assisted personal work sandbox and diary system",
6
6
  "main": "dist/server/index.js",