@qnote/q-ai-note 1.0.20 → 1.0.21

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
@@ -1157,6 +1157,74 @@ function renderWorkTree() {
1157
1157
  });
1158
1158
  await loadSandbox(state.currentSandbox.id);
1159
1159
  },
1160
+ onMoveSibling: treeReadonly ? undefined : async (nodeId, direction) => {
1161
+ if (!state.currentSandbox) return;
1162
+ const delta = direction === 'up' ? -1 : direction === 'down' ? 1 : 0;
1163
+ if (!delta) return;
1164
+ const byId = new Map((state.currentSandbox.items || []).map((item) => [item.id, item]));
1165
+ const node = byId.get(nodeId);
1166
+ if (!node) return;
1167
+ if (!node.parent_id) {
1168
+ if (state.laneReorderPending) return;
1169
+ const roots = (state.currentSandbox.items || []).filter((item) => !item.parent_id);
1170
+ const orderedRoots = [...roots].sort((a, b) => compareSiblingOrder(a, b, 'lane_order_key'));
1171
+ const fromIdx = orderedRoots.findIndex((item) => item.id === nodeId);
1172
+ if (fromIdx < 0) return;
1173
+ const toIdx = fromIdx + delta;
1174
+ if (toIdx < 0 || toIdx >= orderedRoots.length) return;
1175
+ const [movedRoot] = orderedRoots.splice(fromIdx, 1);
1176
+ orderedRoots.splice(toIdx, 0, movedRoot);
1177
+ const rootIds = orderedRoots.map((item) => item.id);
1178
+ const previousRootIds = roots
1179
+ .sort((a, b) => compareSiblingOrder(a, b, 'lane_order_key'))
1180
+ .map((item) => item.id);
1181
+ if (JSON.stringify(rootIds) === JSON.stringify(previousRootIds)) return;
1182
+ state.laneReorderPending = true;
1183
+ renderWorkTree();
1184
+ try {
1185
+ const result = await apiRequest(`${API_BASE}/sandboxes/${state.currentSandbox.id}/items/reorder-roots`, {
1186
+ method: 'POST',
1187
+ body: JSON.stringify({ root_ids: rootIds }),
1188
+ });
1189
+ const hasChangedCount = Object.prototype.hasOwnProperty.call(result || {}, 'changed_count');
1190
+ if (hasChangedCount && Number(result?.changed_count || 0) === 0) {
1191
+ state.laneReorderPending = false;
1192
+ renderWorkTree();
1193
+ return;
1194
+ }
1195
+ await loadSandbox(state.currentSandbox.id);
1196
+ } catch (error) {
1197
+ alert(`泳道排序失败:${error?.message || error}`);
1198
+ } finally {
1199
+ state.laneReorderPending = false;
1200
+ renderWorkTree();
1201
+ }
1202
+ return;
1203
+ }
1204
+ const parentId = node.parent_id || null;
1205
+ const siblings = (state.currentSandbox.items || [])
1206
+ .filter((item) => (item.parent_id || null) === parentId)
1207
+ .sort((a, b) => compareSiblingOrder(a, b, 'order_key'));
1208
+ const fromIdx = siblings.findIndex((item) => item.id === nodeId);
1209
+ if (fromIdx < 0) return;
1210
+ const toIdx = fromIdx + delta;
1211
+ if (toIdx < 0 || toIdx >= siblings.length) return;
1212
+ const peers = siblings.filter((item) => item.id !== nodeId);
1213
+ const left = peers[toIdx - 1] || null;
1214
+ const right = peers[toIdx] || null;
1215
+ const nextOrderKey = rankBetween(getNodeOrderKey(left, 'order_key'), getNodeOrderKey(right, 'order_key'));
1216
+ if (nextOrderKey === getNodeOrderKey(node, 'order_key')) return;
1217
+ await apiRequest(`${API_BASE}/items/${nodeId}`, {
1218
+ method: 'PUT',
1219
+ body: JSON.stringify({
1220
+ extra_data: {
1221
+ ...(node.extra_data || {}),
1222
+ order_key: nextOrderKey,
1223
+ },
1224
+ }),
1225
+ });
1226
+ await loadSandbox(state.currentSandbox.id);
1227
+ },
1160
1228
  onReorderSiblings: treeReadonly ? undefined : async (dragNodeId, targetNodeId, position) => {
1161
1229
  if (!state.currentSandbox) return;
1162
1230
  if (!dragNodeId || !targetNodeId || dragNodeId === targetNodeId) return;
@@ -2487,6 +2487,11 @@ h2 {
2487
2487
  background: #dbe8ff;
2488
2488
  }
2489
2489
 
2490
+ .node-action-btn.move {
2491
+ font-size: 11px;
2492
+ font-weight: 700;
2493
+ }
2494
+
2490
2495
  .node-status.pending { background: #9aa0a6; }
2491
2496
  .node-status.in_progress { background: var(--primary); }
2492
2497
  .node-status.done { background: var(--success); }
@@ -212,6 +212,13 @@ function renderEntityPreviewBoxes(nodeId, entityRowsByNodeId, mode) {
212
212
  `;
213
213
  }
214
214
 
215
+ function renderSiblingMoveActions(node) {
216
+ return `
217
+ <button class="node-action-btn move" data-action="move-up" data-id="${esc(node.id)}" title="上移">↑</button>
218
+ <button class="node-action-btn move" data-action="move-down" data-id="${esc(node.id)}" title="下移">↓</button>
219
+ `;
220
+ }
221
+
215
222
  function renderTreeNode(node, byParent, expandedIdSet, entitySummaryByNodeId, showAssignee = false, entityRowsByNodeId = {}, elementPreviewMode = 'none', readonly = false, selectedId = '') {
216
223
  const children = byParent.get(node.id) || [];
217
224
  const hasChildren = children.length > 0;
@@ -234,6 +241,7 @@ function renderTreeNode(node, byParent, expandedIdSet, entitySummaryByNodeId, sh
234
241
  <button class="node-action-btn add-child" data-action="add-child" data-id="${esc(node.id)}" title="添加子任务">+</button>
235
242
  <button class="node-action-btn" data-action="edit" data-id="${esc(node.id)}" title="编辑">✎</button>
236
243
  <button class="node-action-btn delete" data-action="delete" data-id="${esc(node.id)}" title="删除">✕</button>
244
+ ${renderSiblingMoveActions(node)}
237
245
  </div>
238
246
  `}
239
247
  </div>
@@ -266,6 +274,7 @@ function renderTreeNode(node, byParent, expandedIdSet, entitySummaryByNodeId, sh
266
274
  <button class="node-action-btn add-child" data-action="add-child" data-id="${esc(node.id)}" title="添加子任务">+</button>
267
275
  <button class="node-action-btn" data-action="edit" data-id="${esc(node.id)}" title="编辑">✎</button>
268
276
  <button class="node-action-btn delete" data-action="delete" data-id="${esc(node.id)}" title="删除">✕</button>
277
+ ${renderSiblingMoveActions(node)}
269
278
  </div>
270
279
  `}
271
280
  </div>
@@ -292,6 +301,7 @@ function renderTreeNode(node, byParent, expandedIdSet, entitySummaryByNodeId, sh
292
301
  <button class="node-action-btn add-child" data-action="add-child" data-id="${esc(child.id)}" title="添加子任务">+</button>
293
302
  <button class="node-action-btn" data-action="edit" data-id="${esc(child.id)}" title="编辑">✎</button>
294
303
  <button class="node-action-btn delete" data-action="delete" data-id="${esc(child.id)}" title="删除">✕</button>
304
+ ${renderSiblingMoveActions(child)}
295
305
  </div>
296
306
  `}
297
307
  </div>
@@ -390,6 +400,7 @@ function renderDenseLaneNode(node, byParent, expandedIdSet, entitySummaryByNodeI
390
400
  <button class="node-action-btn add-child" data-action="add-child" data-id="${esc(node.id)}" title="添加子任务">+</button>
391
401
  <button class="node-action-btn" data-action="edit" data-id="${esc(node.id)}" title="编辑">✎</button>
392
402
  <button class="node-action-btn delete" data-action="delete" data-id="${esc(node.id)}" title="删除">✕</button>
403
+ ${renderSiblingMoveActions(node)}
393
404
  </div>
394
405
  `}
395
406
  </div>
@@ -469,6 +480,7 @@ export function mountWorkTree(targetId, options) {
469
480
  onSelect,
470
481
  onSelectEntity,
471
482
  onMoveNode,
483
+ onMoveSibling,
472
484
  onReorderSiblings,
473
485
  onReorderLanes,
474
486
  entitySummaryByNodeId = {},
@@ -499,7 +511,7 @@ export function mountWorkTree(targetId, options) {
499
511
  } else {
500
512
  const orderKeyA = keyOf(a, 'order_key');
501
513
  const orderKeyB = keyOf(b, 'order_key');
502
- if (orderKeyA && orderKeyB && orderKeyA !== orderKeyB) return orderKeyA.localeCompare(orderKeyB);
514
+ if (orderKeyA && orderKeyB && orderKeyA !== orderKeyB) return orderKeyA < orderKeyB ? -1 : 1;
503
515
  if (orderKeyA && !orderKeyB) return -1;
504
516
  if (!orderKeyA && orderKeyB) return 1;
505
517
  }
@@ -547,6 +559,8 @@ export function mountWorkTree(targetId, options) {
547
559
  if (action === 'edit') onEdit?.(id);
548
560
  if (action === 'delete') onDelete?.(id);
549
561
  if (action === 'quick-chat') onQuickChat?.(id, el);
562
+ if (action === 'move-up') onMoveSibling?.(id, 'up');
563
+ if (action === 'move-down') onMoveSibling?.(id, 'down');
550
564
  });
551
565
  });
552
566
  container.querySelectorAll('[data-select-id]').forEach((el) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qnote/q-ai-note",
3
- "version": "1.0.20",
3
+ "version": "1.0.21",
4
4
  "type": "module",
5
5
  "description": "AI-assisted personal work sandbox and diary system",
6
6
  "main": "dist/server/index.js",