@@ -2,15 +2,80 @@ import { KeyEventDefinition } from "./base";
22
33export const archive : KeyEventDefinition = {
44 context : 'navigation' ,
5- keys : [ 'shift + x' ] ,
6- description : 'Mark a node and all its children as "archived" ' ,
5+ keys : [ 'shift + x' , 'ctrl + x' ] ,
6+ description : 'Mark a node as archived; ctrl+x also completes task ' ,
77 action : async args => {
88 const { e, outline, cursor, api } = args ;
99 e . preventDefault ( ) ;
1010 // toggle "strikethrough" of node
1111 cursor . get ( ) . classList . toggle ( 'strikethrough' ) ;
12- outline . getContentNode ( cursor . getIdOfNode ( ) ) . toggleArchiveStatus ( ) ;
13- api . saveContentNode ( outline . getContentNode ( cursor . getIdOfNode ( ) ) ) ;
12+ const node = outline . getContentNode ( cursor . getIdOfNode ( ) ) ;
13+ node . toggleArchiveStatus ( ) ;
14+
15+ if ( node . task ) {
16+ if ( e . ctrlKey || node . archived ) {
17+ node . markComplete ( ) ;
18+ }
19+ else {
20+ node . markIncomplete ( ) ;
21+ }
22+ }
23+
24+ // re-render content to reflect completion checkbox for the currently focused node (tasks aggregate)
25+ const contentEl = cursor . get ( ) . querySelector ( '.nodeContent' ) as HTMLElement ;
26+ contentEl . innerHTML = await outline . renderContent ( cursor . getIdOfNode ( ) ) ;
27+
28+ // Also update the original outline node's content and strikethrough state
29+ const nodeId = cursor . getIdOfNode ( ) ;
30+ const originalNodes = Array . from ( document . querySelectorAll ( `.node[data-id="${ nodeId } "]` ) ) as HTMLElement [ ] ;
31+ const original = originalNodes . find ( n => ! n . closest ( '#id-tasks-aggregate' ) ) ;
32+ if ( original ) {
33+ const originalContentEl = original . querySelector ( '.nodeContent' ) as HTMLElement ;
34+ if ( originalContentEl ) {
35+ originalContentEl . innerHTML = await outline . renderContent ( nodeId ) ;
36+ }
37+ const isCompletedTask = ! ! node . completionDate ;
38+ if ( node . isArchived ( ) || isCompletedTask ) {
39+ original . classList . add ( 'strikethrough' ) ;
40+ }
41+ else {
42+ original . classList . remove ( 'strikethrough' ) ;
43+ }
44+ }
45+
46+ // Keep tasklist in sync for incremental updates without full render
47+ if ( node . task ) {
48+ outline . tasklist [ nodeId ] = node ;
49+ }
50+ else {
51+ delete outline . tasklist [ nodeId ] ;
52+ }
53+
54+ // Refresh Tasks aggregate at the top
55+ const tasksHtml = await outline . renderTasksFromTasklist ( ) ;
56+ const tasksContainer = document . getElementById ( 'id-tasks-aggregate' ) ;
57+ if ( tasksHtml . length === 0 ) {
58+ if ( tasksContainer ) {
59+ tasksContainer . remove ( ) ;
60+ }
61+ }
62+ else {
63+ if ( tasksContainer ) {
64+ tasksContainer . outerHTML = tasksHtml ;
65+ }
66+ else {
67+ const root = document . querySelector ( '#outliner' ) ;
68+ if ( root ) {
69+ root . insertAdjacentHTML ( 'afterbegin' , tasksHtml ) ;
70+ }
71+ }
72+ }
73+
74+ // Keep cursor where the user was interacting: tasks aggregate vs main outline
75+ const inTasksAggregate = ! ! cursor . get ( ) ?. closest ( '#id-tasks-aggregate' ) ;
76+ cursor . set ( inTasksAggregate ? `#tasks-id-${ nodeId } ` : `#id-${ nodeId } ` ) ;
77+
78+ api . saveContentNode ( node ) ;
1479 api . save ( outline ) ;
1580
1681 }
0 commit comments