taskunity 2026.3__tar.gz → 2026.4__tar.gz
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.
- {taskunity-2026.3/src/taskunity.egg-info → taskunity-2026.4}/PKG-INFO +1 -1
- {taskunity-2026.3 → taskunity-2026.4}/pyproject.toml +1 -1
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity/app.py +6 -0
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity/static/app.css +176 -16
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity/templates/base.html +51 -0
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity/templates/partials/main.html +12 -34
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity/templates/partials/milestone_banner.html +1 -1
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity/templates/partials/milestone_panel.html +3 -1
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity/templates/partials/milestones.html +27 -20
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity/templates/partials/task_panel.html +2 -1
- {taskunity-2026.3 → taskunity-2026.4/src/taskunity.egg-info}/PKG-INFO +1 -1
- {taskunity-2026.3 → taskunity-2026.4}/MANIFEST.in +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/README.md +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/setup.cfg +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity/__init__.py +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity/cli.py +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity/models.py +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity/render.py +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity/static/chart.umd.min.js +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity/static/chartjs-adapter-date-fns.bundle.min.js +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity/static/htmx.min.js +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity/task_store.py +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity/templates/index.html +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity/templates/partials/board.html +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity/templates/partials/calendar.html +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity/templates/partials/projects.html +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity/templates/partials/task_list.html +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity/templates/partials/timeline.html +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity.egg-info/SOURCES.txt +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity.egg-info/dependency_links.txt +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity.egg-info/entry_points.txt +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity.egg-info/requires.txt +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/src/taskunity.egg-info/top_level.txt +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/tests/test_git_workspace_scope.py +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/tests/test_render_jsonantt.py +0 -0
- {taskunity-2026.3 → taskunity-2026.4}/tests/test_workspace_config.py +0 -0
|
@@ -320,6 +320,11 @@ def create_app(workspace: str | Path = ".") -> FastAPI:
|
|
|
320
320
|
all_tasks = load_all_tasks(workspace)
|
|
321
321
|
milestones = load_all_milestones(workspace)
|
|
322
322
|
tasks_by_id = {t.id: t for t in all_tasks}
|
|
323
|
+
panel_task_id = (selected_task.id if selected_task else (request.query_params.get("panel_task") or "").strip())
|
|
324
|
+
if selected_task is None and panel_task_id:
|
|
325
|
+
selected_task = tasks_by_id.get(panel_task_id)
|
|
326
|
+
if selected_task is None:
|
|
327
|
+
panel_task_id = ""
|
|
323
328
|
milestone_rollups = {m.id: milestone_rollup(m, tasks_by_id) for m in milestones}
|
|
324
329
|
|
|
325
330
|
selected_milestone = None
|
|
@@ -443,6 +448,7 @@ def create_app(workspace: str | Path = ".") -> FastAPI:
|
|
|
443
448
|
"calendar_next_query": build_query(projects, date_from, date_to, q, "calendar", sort, milestone, show_closed, stale_days, next_month, next_year),
|
|
444
449
|
"calendar_year_prev_query": build_query(projects, date_from, date_to, q, "calendar", sort, milestone, show_closed, stale_days, year_prev_month, year_prev_year),
|
|
445
450
|
"calendar_year_next_query": build_query(projects, date_from, date_to, q, "calendar", sort, milestone, show_closed, stale_days, year_next_month, year_next_year),
|
|
451
|
+
"panel_task": panel_task_id,
|
|
446
452
|
"show_closed": show_closed,
|
|
447
453
|
"hidden_closed_count": hidden_closed_count,
|
|
448
454
|
"toggle_closed_query": build_query(projects, date_from, date_to, q, view, sort, milestone, not show_closed, stale_days, focus_month, focus_year),
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
:root {
|
|
2
2
|
color-scheme: light;
|
|
3
|
+
--topbar-height: 4.4rem;
|
|
4
|
+
--controls-height: 5.7rem;
|
|
3
5
|
--bg: #eef1f7;
|
|
4
6
|
--bg-soft: #f7f9fc;
|
|
5
7
|
--card: #ffffff;
|
|
@@ -78,6 +80,7 @@ body {
|
|
|
78
80
|
color: var(--text);
|
|
79
81
|
-webkit-font-smoothing: antialiased;
|
|
80
82
|
text-rendering: optimizeLegibility;
|
|
83
|
+
padding-top: var(--topbar-height);
|
|
81
84
|
}
|
|
82
85
|
html[data-theme="dark"] body {
|
|
83
86
|
background: var(--bg);
|
|
@@ -96,8 +99,10 @@ html[data-theme="dark"] body {
|
|
|
96
99
|
background: rgba(255, 255, 255, .82);
|
|
97
100
|
backdrop-filter: saturate(160%) blur(10px);
|
|
98
101
|
border-bottom: 1px solid var(--line);
|
|
99
|
-
position:
|
|
102
|
+
position: fixed;
|
|
100
103
|
top: 0;
|
|
104
|
+
left: 0;
|
|
105
|
+
right: 0;
|
|
101
106
|
z-index: 10;
|
|
102
107
|
}
|
|
103
108
|
html[data-theme="dark"] .topbar {
|
|
@@ -188,6 +193,7 @@ h1, h2, h3, p { margin-top: 0; }
|
|
|
188
193
|
.theme-option.active { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }
|
|
189
194
|
.settings-anchor { position: relative; }
|
|
190
195
|
.new-task-form { display: flex; gap: .5rem; }
|
|
196
|
+
.new-task-form .new-task-btn { white-space: nowrap; }
|
|
191
197
|
input, select, textarea, button {
|
|
192
198
|
font: inherit;
|
|
193
199
|
border: 1px solid var(--line);
|
|
@@ -220,7 +226,17 @@ button:focus-visible { outline: none; box-shadow: var(--ring); }
|
|
|
220
226
|
padding: 1.1rem 1.4rem;
|
|
221
227
|
align-items: start;
|
|
222
228
|
}
|
|
223
|
-
.
|
|
229
|
+
.layout.panel-collapsed {
|
|
230
|
+
grid-template-columns: minmax(0, 1fr);
|
|
231
|
+
}
|
|
232
|
+
.layout.panel-collapsed .pane-resizer,
|
|
233
|
+
.layout.panel-collapsed .side-panel {
|
|
234
|
+
display: none;
|
|
235
|
+
}
|
|
236
|
+
.content {
|
|
237
|
+
min-width: 0;
|
|
238
|
+
padding-top: calc(var(--controls-height) + .35rem);
|
|
239
|
+
}
|
|
224
240
|
.pane-resizer {
|
|
225
241
|
align-self: stretch;
|
|
226
242
|
width: 10px;
|
|
@@ -370,6 +386,49 @@ button:focus-visible { outline: none; box-shadow: var(--ring); }
|
|
|
370
386
|
margin-top: .55rem;
|
|
371
387
|
}
|
|
372
388
|
.mini-progress div { height: 100%; background: var(--working); }
|
|
389
|
+
.content-toolbar {
|
|
390
|
+
position: fixed !important;
|
|
391
|
+
top: var(--topbar-height);
|
|
392
|
+
left: 1.4rem;
|
|
393
|
+
right: calc(var(--side-pane-width, 430px) + 10px + .55rem + 1.4rem);
|
|
394
|
+
z-index: 8;
|
|
395
|
+
background: var(--bg);
|
|
396
|
+
border-bottom: 1px solid var(--line-soft);
|
|
397
|
+
padding: 0 0 .28rem;
|
|
398
|
+
min-height: var(--controls-height);
|
|
399
|
+
}
|
|
400
|
+
.layout.panel-collapsed .content-toolbar {
|
|
401
|
+
right: 1.4rem;
|
|
402
|
+
}
|
|
403
|
+
.content-toolbar::before {
|
|
404
|
+
content: "";
|
|
405
|
+
position: absolute;
|
|
406
|
+
left: 0;
|
|
407
|
+
right: 0;
|
|
408
|
+
top: -8px;
|
|
409
|
+
height: 8px;
|
|
410
|
+
background: var(--bg);
|
|
411
|
+
pointer-events: none;
|
|
412
|
+
}
|
|
413
|
+
.content-toolbar .filter-bar,
|
|
414
|
+
.content-toolbar .view-bar {
|
|
415
|
+
flex-wrap: nowrap;
|
|
416
|
+
overflow-x: auto;
|
|
417
|
+
overflow-y: hidden;
|
|
418
|
+
scrollbar-width: thin;
|
|
419
|
+
-webkit-overflow-scrolling: touch;
|
|
420
|
+
}
|
|
421
|
+
.content-toolbar .filter-bar { margin-bottom: .55rem; }
|
|
422
|
+
.content-toolbar .view-bar { margin-bottom: .25rem; }
|
|
423
|
+
.content-toolbar .pills,
|
|
424
|
+
.content-toolbar .view-switch,
|
|
425
|
+
.content-toolbar .new-task-form,
|
|
426
|
+
.content-toolbar .git-chip,
|
|
427
|
+
.content-toolbar .btn-link,
|
|
428
|
+
.content-toolbar .filter-menu {
|
|
429
|
+
flex: 0 0 auto;
|
|
430
|
+
}
|
|
431
|
+
.content-toolbar .filter-bar .spacer { flex: 1 0 1.2rem; }
|
|
373
432
|
.filter-bar {
|
|
374
433
|
display: flex;
|
|
375
434
|
flex-wrap: wrap;
|
|
@@ -895,13 +954,21 @@ button:focus-visible { outline: none; box-shadow: var(--ring); }
|
|
|
895
954
|
.dep-id { margin-left: auto; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .68rem; color: var(--muted); }
|
|
896
955
|
.dep-empty { padding: .5rem .55rem; font-size: .82rem; }
|
|
897
956
|
.side-panel {
|
|
898
|
-
position:
|
|
957
|
+
position: fixed;
|
|
899
958
|
top: 5rem;
|
|
900
|
-
|
|
959
|
+
right: 1.4rem;
|
|
960
|
+
height: calc(100vh - 6rem);
|
|
961
|
+
width: var(--side-pane-width, 430px);
|
|
901
962
|
overflow: hidden;
|
|
902
963
|
box-shadow: var(--shadow-md);
|
|
903
964
|
}
|
|
904
|
-
.panel-scroll {
|
|
965
|
+
.panel-scroll {
|
|
966
|
+
overflow-y: auto;
|
|
967
|
+
height: 100%;
|
|
968
|
+
overscroll-behavior: contain;
|
|
969
|
+
padding: 0 1rem 1rem;
|
|
970
|
+
font-size: .93rem;
|
|
971
|
+
}
|
|
905
972
|
.empty-panel { padding: 1.25rem 1rem; color: var(--muted); }
|
|
906
973
|
.empty-panel h2 { margin: 0 0 .35rem; font-size: 1.05rem; font-weight: 650; color: var(--text); }
|
|
907
974
|
.empty-panel p { margin: 0; font-size: .88rem; }
|
|
@@ -920,6 +987,14 @@ button:focus-visible { outline: none; box-shadow: var(--ring); }
|
|
|
920
987
|
align-items: center;
|
|
921
988
|
gap: .45rem;
|
|
922
989
|
}
|
|
990
|
+
.task-collapse-btn {
|
|
991
|
+
width: 1.85rem;
|
|
992
|
+
height: 1.85rem;
|
|
993
|
+
padding: 0;
|
|
994
|
+
text-align: center;
|
|
995
|
+
line-height: 1;
|
|
996
|
+
font-size: 1rem;
|
|
997
|
+
}
|
|
923
998
|
.task-back-btn {
|
|
924
999
|
padding: .2rem .45rem;
|
|
925
1000
|
border: 1px solid var(--line);
|
|
@@ -983,25 +1058,36 @@ button:focus-visible { outline: none; box-shadow: var(--ring); }
|
|
|
983
1058
|
width: 100%;
|
|
984
1059
|
display: inline-grid;
|
|
985
1060
|
grid-template-columns: auto minmax(0, 1fr);
|
|
986
|
-
gap: .
|
|
1061
|
+
gap: .42rem;
|
|
987
1062
|
align-items: center;
|
|
1063
|
+
color: var(--text);
|
|
988
1064
|
text-align: left;
|
|
989
1065
|
background: var(--bg-soft);
|
|
990
1066
|
border: 1px solid var(--line);
|
|
991
|
-
|
|
1067
|
+
padding: .68rem .8rem;
|
|
992
1068
|
box-shadow: none;
|
|
993
1069
|
padding: .45rem .6rem;
|
|
994
1070
|
}
|
|
995
1071
|
.check-toggle:hover { filter: none; border-color: color-mix(in srgb, var(--line) 55%, var(--accent)); }
|
|
996
1072
|
.check-mark {
|
|
997
1073
|
width: 1.2rem;
|
|
998
|
-
|
|
1074
|
+
gap: .5rem;
|
|
999
1075
|
border: 1px solid color-mix(in srgb, var(--line) 60%, var(--accent));
|
|
1000
1076
|
border-radius: .3rem;
|
|
1001
1077
|
display: inline-flex;
|
|
1002
|
-
|
|
1078
|
+
font-size: .93rem;
|
|
1003
1079
|
justify-content: center;
|
|
1004
1080
|
font-weight: 700;
|
|
1081
|
+
.milestone-open-row {
|
|
1082
|
+
width: 100%;
|
|
1083
|
+
text-align: left;
|
|
1084
|
+
box-shadow: var(--shadow-sm);
|
|
1085
|
+
}
|
|
1086
|
+
.milestone-open-row:hover {
|
|
1087
|
+
filter: none;
|
|
1088
|
+
border-color: color-mix(in srgb, var(--line) 60%, var(--accent));
|
|
1089
|
+
box-shadow: var(--shadow-md);
|
|
1090
|
+
}
|
|
1005
1091
|
color: var(--accent);
|
|
1006
1092
|
background: #fff;
|
|
1007
1093
|
}
|
|
@@ -1236,19 +1322,21 @@ input[type="file"]::file-selector-button:hover {
|
|
|
1236
1322
|
}
|
|
1237
1323
|
.burndown-preview-link:hover { text-decoration: underline; }
|
|
1238
1324
|
.burndown-preview-thumb {
|
|
1239
|
-
display:
|
|
1325
|
+
display: block;
|
|
1240
1326
|
border: 1px solid var(--line);
|
|
1241
1327
|
border-radius: var(--radius-sm);
|
|
1242
1328
|
overflow: hidden;
|
|
1243
|
-
|
|
1244
|
-
max-
|
|
1329
|
+
width: 100%;
|
|
1330
|
+
max-width: 100%;
|
|
1331
|
+
max-height: 180px;
|
|
1245
1332
|
background: var(--card);
|
|
1246
1333
|
}
|
|
1247
1334
|
.burndown-preview-thumb img {
|
|
1248
1335
|
display: block;
|
|
1249
1336
|
width: 100%;
|
|
1337
|
+
max-width: 100%;
|
|
1250
1338
|
height: auto;
|
|
1251
|
-
object-fit:
|
|
1339
|
+
object-fit: contain;
|
|
1252
1340
|
}
|
|
1253
1341
|
.burndown-preview-text {
|
|
1254
1342
|
margin: 0;
|
|
@@ -1279,13 +1367,34 @@ input[type="file"]::file-selector-button:hover {
|
|
|
1279
1367
|
.pane-resizer { display: none; }
|
|
1280
1368
|
.side-panel { position: static; max-height: none; }
|
|
1281
1369
|
.panel-scroll { max-height: none; }
|
|
1370
|
+
.content { padding-top: calc(var(--controls-height) + .25rem); }
|
|
1371
|
+
.content-toolbar {
|
|
1372
|
+
top: var(--topbar-height);
|
|
1373
|
+
left: 1.4rem;
|
|
1374
|
+
right: 1.4rem;
|
|
1375
|
+
}
|
|
1282
1376
|
.board, .summary-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
1283
1377
|
}
|
|
1284
1378
|
@media (max-width: 700px) {
|
|
1285
|
-
.topbar
|
|
1379
|
+
.topbar { flex-direction: column; align-items: stretch; }
|
|
1380
|
+
.new-task-form { width: 100%; }
|
|
1381
|
+
.new-task-form .new-task-btn { width: 100%; }
|
|
1286
1382
|
.board, .summary-grid { grid-template-columns: 1fr; }
|
|
1287
1383
|
.timeline-row { grid-template-columns: 1fr; }
|
|
1288
1384
|
.layout { padding: .5rem; }
|
|
1385
|
+
.content { padding-top: calc(var(--controls-height) + .6rem); }
|
|
1386
|
+
.content-toolbar {
|
|
1387
|
+
left: .5rem;
|
|
1388
|
+
right: .5rem;
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
@media (max-width: 900px) {
|
|
1393
|
+
.mb-actions {
|
|
1394
|
+
width: 100%;
|
|
1395
|
+
justify-content: flex-start;
|
|
1396
|
+
}
|
|
1397
|
+
.mc-stats.compact { width: 100%; }
|
|
1289
1398
|
}
|
|
1290
1399
|
|
|
1291
1400
|
/* --- Milestones --- */
|
|
@@ -1316,6 +1425,55 @@ input[type="file"]::file-selector-button:hover {
|
|
|
1316
1425
|
overflow: hidden;
|
|
1317
1426
|
box-shadow: var(--shadow-sm);
|
|
1318
1427
|
}
|
|
1428
|
+
.milestone-list {
|
|
1429
|
+
display: grid;
|
|
1430
|
+
gap: .7rem;
|
|
1431
|
+
}
|
|
1432
|
+
.milestone-list-item {
|
|
1433
|
+
display: grid;
|
|
1434
|
+
gap: .42rem;
|
|
1435
|
+
background: var(--card);
|
|
1436
|
+
color: var(--text);
|
|
1437
|
+
border: 1px solid var(--line);
|
|
1438
|
+
border-left: 4px solid var(--ms-color, var(--accent));
|
|
1439
|
+
border-radius: var(--radius-sm);
|
|
1440
|
+
padding: .68rem .8rem;
|
|
1441
|
+
box-shadow: var(--shadow-sm);
|
|
1442
|
+
}
|
|
1443
|
+
.milestone-open-row {
|
|
1444
|
+
width: 100%;
|
|
1445
|
+
text-align: left;
|
|
1446
|
+
}
|
|
1447
|
+
.milestone-open-row:hover {
|
|
1448
|
+
filter: none;
|
|
1449
|
+
border-color: color-mix(in srgb, var(--line) 60%, var(--accent));
|
|
1450
|
+
box-shadow: var(--shadow-md);
|
|
1451
|
+
}
|
|
1452
|
+
.milestone-list-head {
|
|
1453
|
+
display: flex;
|
|
1454
|
+
align-items: center;
|
|
1455
|
+
justify-content: space-between;
|
|
1456
|
+
gap: .5rem;
|
|
1457
|
+
flex-wrap: wrap;
|
|
1458
|
+
}
|
|
1459
|
+
.milestone-list-title {
|
|
1460
|
+
display: flex;
|
|
1461
|
+
align-items: center;
|
|
1462
|
+
gap: .45rem;
|
|
1463
|
+
min-width: 0;
|
|
1464
|
+
}
|
|
1465
|
+
.milestone-list-title strong {
|
|
1466
|
+
font-size: .93rem;
|
|
1467
|
+
min-width: 0;
|
|
1468
|
+
}
|
|
1469
|
+
.mc-stats.compact { gap: .5rem; font-size: .74rem; }
|
|
1470
|
+
.mc-projects.compact { gap: .25rem; }
|
|
1471
|
+
.mc-projects.compact .project { font-size: .72rem; padding: .1rem .42rem; }
|
|
1472
|
+
.clamp-1 {
|
|
1473
|
+
overflow: hidden;
|
|
1474
|
+
text-overflow: ellipsis;
|
|
1475
|
+
white-space: nowrap;
|
|
1476
|
+
}
|
|
1319
1477
|
.milestone-open {
|
|
1320
1478
|
flex: 1;
|
|
1321
1479
|
display: grid;
|
|
@@ -1360,11 +1518,13 @@ input[type="file"]::file-selector-button:hover {
|
|
|
1360
1518
|
box-shadow: var(--shadow-sm);
|
|
1361
1519
|
}
|
|
1362
1520
|
.mb-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
|
|
1363
|
-
.mb-title { display: flex; align-items: flex-start; gap: .55rem; }
|
|
1521
|
+
.mb-title { display: flex; align-items: flex-start; gap: .55rem; min-width: 0; flex: 1 1 18rem; }
|
|
1364
1522
|
.mb-flag { color: var(--ms-color, var(--accent)); font-size: 1rem; line-height: 1.4; }
|
|
1365
1523
|
.mb-title h2 { margin: 0; font-size: 1.1rem; }
|
|
1366
1524
|
.mb-meta { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; margin-top: .25rem; font-size: .78rem; color: var(--muted); }
|
|
1367
|
-
.mb-actions { display: flex; align-items: center; gap: .5rem; }
|
|
1525
|
+
.mb-actions { display: flex; align-items: center; justify-content: flex-end; gap: .5rem; flex: 1 1 16rem; flex-wrap: wrap; }
|
|
1526
|
+
.mb-actions .btn-link,
|
|
1527
|
+
.mb-actions .pill { white-space: nowrap; }
|
|
1368
1528
|
.mb-rollup { display: grid; gap: .45rem; }
|
|
1369
1529
|
.mb-stats { display: flex; flex-wrap: wrap; gap: .9rem; font-size: .8rem; color: var(--muted); }
|
|
1370
1530
|
.mb-stats .done strong { color: var(--done); }
|
|
@@ -326,6 +326,57 @@
|
|
|
326
326
|
});
|
|
327
327
|
});
|
|
328
328
|
|
|
329
|
+
function getMainLayout() {
|
|
330
|
+
return document.getElementById('app-main');
|
|
331
|
+
}
|
|
332
|
+
function collapseTaskPanel() {
|
|
333
|
+
var main = getMainLayout();
|
|
334
|
+
if (!main) return;
|
|
335
|
+
main.classList.add('panel-collapsed');
|
|
336
|
+
}
|
|
337
|
+
function expandTaskPanel() {
|
|
338
|
+
var main = getMainLayout();
|
|
339
|
+
if (!main) return;
|
|
340
|
+
main.classList.remove('panel-collapsed');
|
|
341
|
+
}
|
|
342
|
+
document.addEventListener('click', function (e) {
|
|
343
|
+
var btn = e.target.closest('[data-panel-collapse]');
|
|
344
|
+
if (!btn) return;
|
|
345
|
+
collapseTaskPanel();
|
|
346
|
+
});
|
|
347
|
+
document.addEventListener('click', function (e) {
|
|
348
|
+
var panelTrigger = e.target.closest('[hx-target="#task-panel"]');
|
|
349
|
+
if (!panelTrigger) return;
|
|
350
|
+
expandTaskPanel();
|
|
351
|
+
});
|
|
352
|
+
document.addEventListener('pointerdown', function (e) {
|
|
353
|
+
var panelTrigger = e.target.closest('[hx-target="#task-panel"]');
|
|
354
|
+
if (!panelTrigger) return;
|
|
355
|
+
expandTaskPanel();
|
|
356
|
+
}, true);
|
|
357
|
+
document.body.addEventListener('htmx:beforeRequest', function (e) {
|
|
358
|
+
var trigger = e && e.detail ? e.detail.elt : null;
|
|
359
|
+
if (!trigger) return;
|
|
360
|
+
var panelTarget = trigger.getAttribute && trigger.getAttribute('hx-target');
|
|
361
|
+
if (panelTarget === '#task-panel' || trigger.closest('[hx-target="#task-panel"]')) {
|
|
362
|
+
expandTaskPanel();
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
document.body.addEventListener('htmx:beforeSwap', function (e) {
|
|
366
|
+
var target = e && e.detail ? e.detail.target : null;
|
|
367
|
+
if (!target) return;
|
|
368
|
+
var panel = target.id === 'task-panel' ? target : target.closest ? target.closest('#task-panel') : null;
|
|
369
|
+
if (panel) expandTaskPanel();
|
|
370
|
+
});
|
|
371
|
+
document.body.addEventListener('htmx:afterSwap', function (e) {
|
|
372
|
+
var target = e && e.detail ? e.detail.target : null;
|
|
373
|
+
if (!target) return;
|
|
374
|
+
var panel = target.id === 'task-panel' ? target : target.closest ? target.closest('#task-panel') : null;
|
|
375
|
+
if (!panel) return;
|
|
376
|
+
var hasContent = !!panel.children.length && (panel.textContent || '').trim().length > 0;
|
|
377
|
+
if (hasContent) expandTaskPanel();
|
|
378
|
+
});
|
|
379
|
+
|
|
329
380
|
function paneBounds() {
|
|
330
381
|
var max = Math.min(Math.floor(window.innerWidth * 0.62), 820);
|
|
331
382
|
var min = 300;
|
|
@@ -1,24 +1,6 @@
|
|
|
1
|
-
<main id="app-main" class="layout" data-resizable-layout>
|
|
1
|
+
<main id="app-main" class="layout{% if not selected_task and not selected_milestone %} panel-collapsed{% endif %}" data-resizable-layout>
|
|
2
2
|
<section class="content">
|
|
3
|
-
<
|
|
4
|
-
<div class="summary-card">
|
|
5
|
-
<span class="label">Total tasks</span>
|
|
6
|
-
<strong>{{ model.total }}</strong>
|
|
7
|
-
</div>
|
|
8
|
-
<div class="summary-card">
|
|
9
|
-
<span class="label">Done</span>
|
|
10
|
-
<strong>{{ model.counts.get('done', 0) }}</strong>
|
|
11
|
-
</div>
|
|
12
|
-
<div class="summary-card">
|
|
13
|
-
<span class="label">Working</span>
|
|
14
|
-
<strong>{{ model.counts.get('working', 0) }}</strong>
|
|
15
|
-
</div>
|
|
16
|
-
<div class="summary-card">
|
|
17
|
-
<span class="label">Blocked</span>
|
|
18
|
-
<strong>{{ model.counts.get('blocked', 0) }}</strong>
|
|
19
|
-
</div>
|
|
20
|
-
</section>
|
|
21
|
-
|
|
3
|
+
<div class="content-toolbar">
|
|
22
4
|
<div class="filter-bar">
|
|
23
5
|
<details class="filter-menu">
|
|
24
6
|
<summary>
|
|
@@ -187,12 +169,12 @@
|
|
|
187
169
|
|
|
188
170
|
<div class="view-bar">
|
|
189
171
|
<div class="view-switch">
|
|
190
|
-
<button class="{% if filters.view == 'list' %}active{% endif %}" hx-get="/partials/main?view=list{% if filters.query %}&{{ filters.query }}{% endif %}" hx-target="#app-main" hx-swap="outerHTML">Task List</button>
|
|
191
|
-
<button class="{% if filters.view == 'board' %}active{% endif %}" hx-get="/partials/main?view=board{% if filters.query %}&{{ filters.query }}{% endif %}" hx-target="#app-main" hx-swap="outerHTML">Task Board</button>
|
|
192
|
-
<button class="{% if filters.view == 'gantt' %}active{% endif %}" hx-get="/partials/main?view=gantt{% if filters.query %}&{{ filters.query }}{% endif %}" hx-target="#app-main" hx-swap="outerHTML">Gantt</button>
|
|
193
|
-
<button class="{% if filters.view == 'calendar' %}active{% endif %}" hx-get="/partials/main?view=calendar{% if filters.query %}&{{ filters.query }}{% endif %}" hx-target="#app-main" hx-swap="outerHTML">Calendar</button>
|
|
194
|
-
<button class="{% if filters.view == 'milestones' %}active{% endif %}" hx-get="/partials/main?view=milestones{% if filters.query %}&{{ filters.query }}{% endif %}" hx-target="#app-main" hx-swap="outerHTML">Milestones</button>
|
|
195
|
-
<button class="{% if filters.view == 'projects' %}active{% endif %}" hx-get="/partials/main?view=projects{% if filters.query %}&{{ filters.query }}{% endif %}" hx-target="#app-main" hx-swap="outerHTML">Projects</button>
|
|
172
|
+
<button class="{% if filters.view == 'list' %}active{% endif %}" hx-get="/partials/main?view=list{% if filters.query %}&{{ filters.query }}{% endif %}{% if filters.panel_task %}&panel_task={{ filters.panel_task }}{% endif %}" hx-target="#app-main" hx-swap="outerHTML">Task List</button>
|
|
173
|
+
<button class="{% if filters.view == 'board' %}active{% endif %}" hx-get="/partials/main?view=board{% if filters.query %}&{{ filters.query }}{% endif %}{% if filters.panel_task %}&panel_task={{ filters.panel_task }}{% endif %}" hx-target="#app-main" hx-swap="outerHTML">Task Board</button>
|
|
174
|
+
<button class="{% if filters.view == 'gantt' %}active{% endif %}" hx-get="/partials/main?view=gantt{% if filters.query %}&{{ filters.query }}{% endif %}{% if filters.panel_task %}&panel_task={{ filters.panel_task }}{% endif %}" hx-target="#app-main" hx-swap="outerHTML">Gantt</button>
|
|
175
|
+
<button class="{% if filters.view == 'calendar' %}active{% endif %}" hx-get="/partials/main?view=calendar{% if filters.query %}&{{ filters.query }}{% endif %}{% if filters.panel_task %}&panel_task={{ filters.panel_task }}{% endif %}" hx-target="#app-main" hx-swap="outerHTML">Calendar</button>
|
|
176
|
+
<button class="{% if filters.view == 'milestones' %}active{% endif %}" hx-get="/partials/main?view=milestones{% if filters.query %}&{{ filters.query }}{% endif %}{% if filters.panel_task %}&panel_task={{ filters.panel_task }}{% endif %}" hx-target="#app-main" hx-swap="outerHTML">Milestones</button>
|
|
177
|
+
<button class="{% if filters.view == 'projects' %}active{% endif %}" hx-get="/partials/main?view=projects{% if filters.query %}&{{ filters.query }}{% endif %}{% if filters.panel_task %}&panel_task={{ filters.panel_task }}{% endif %}" hx-target="#app-main" hx-swap="outerHTML">Projects</button>
|
|
196
178
|
</div>
|
|
197
179
|
{% if filters.view == 'calendar' %}
|
|
198
180
|
<div class="calendar-nav">
|
|
@@ -226,7 +208,7 @@
|
|
|
226
208
|
</div>
|
|
227
209
|
{% endif %}
|
|
228
210
|
<form class="new-task-form" hx-post="/tasks/create" hx-target="#app-main" hx-swap="outerHTML">
|
|
229
|
-
<input name="title"
|
|
211
|
+
<input type="hidden" name="title" value="New task">
|
|
230
212
|
{% for p in filters.projects %}<input type="hidden" name="f_project" value="{{ p }}">{% endfor %}
|
|
231
213
|
<input type="hidden" name="f_from" value="{{ filters.date_from }}">
|
|
232
214
|
<input type="hidden" name="f_to" value="{{ filters.date_to }}">
|
|
@@ -237,9 +219,10 @@
|
|
|
237
219
|
<input type="hidden" name="f_stale_days" value="{{ filters.stale_days }}">
|
|
238
220
|
<input type="hidden" name="f_calendar_month" value="{{ filters.calendar_month }}">
|
|
239
221
|
<input type="hidden" name="f_calendar_year" value="{{ filters.calendar_year }}">
|
|
240
|
-
<button type="submit">
|
|
222
|
+
<button type="submit" class="new-task-btn">New task</button>
|
|
241
223
|
</form>
|
|
242
224
|
</div>
|
|
225
|
+
</div>
|
|
243
226
|
|
|
244
227
|
{% if selected_milestone and filters.view != 'milestones' %}
|
|
245
228
|
{% include "partials/milestone_banner.html" %}
|
|
@@ -268,16 +251,11 @@
|
|
|
268
251
|
title="Drag to resize details pane. Double-click to reset."
|
|
269
252
|
aria-orientation="vertical"
|
|
270
253
|
></button>
|
|
271
|
-
<aside id="task-panel" class="side-panel">
|
|
254
|
+
<aside id="task-panel" class="side-panel{% if not selected_task and not selected_milestone %} side-panel-empty{% endif %}">
|
|
272
255
|
{% if selected_task %}
|
|
273
256
|
{% include "partials/task_panel.html" %}
|
|
274
257
|
{% elif selected_milestone %}
|
|
275
258
|
{% include "partials/milestone_panel.html" %}
|
|
276
|
-
{% else %}
|
|
277
|
-
<div class="empty-panel">
|
|
278
|
-
<h2>Select a task</h2>
|
|
279
|
-
<p>Details will appear when a task is selected.</p>
|
|
280
|
-
</div>
|
|
281
259
|
{% endif %}
|
|
282
260
|
</aside>
|
|
283
261
|
</main>
|
{taskunity-2026.3 → taskunity-2026.4}/src/taskunity/templates/partials/milestone_banner.html
RENAMED
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
return;
|
|
110
110
|
}
|
|
111
111
|
if (ext === 'pdf') {
|
|
112
|
-
hoverCard.innerHTML += '<
|
|
112
|
+
hoverCard.innerHTML += '<div class="burndown-preview-body muted">PDF preview is not shown inline here. Use “Open in new tab” to view it.</div>';
|
|
113
113
|
return;
|
|
114
114
|
}
|
|
115
115
|
if (isTextExt(ext)) {
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
<span class="ms-status {{ selected_milestone.status }}">{{ selected_milestone.status }}</span>
|
|
5
5
|
<h2>{{ selected_milestone.title }}</h2>
|
|
6
6
|
</div>
|
|
7
|
-
<
|
|
7
|
+
<div class="task-panel-title-row">
|
|
8
|
+
<button type="button" class="btn-link task-collapse-btn" data-panel-collapse title="Collapse details" aria-label="Collapse details">×</button>
|
|
9
|
+
</div>
|
|
8
10
|
</div>
|
|
9
11
|
|
|
10
12
|
<form hx-post="/milestones/{{ selected_milestone.id }}/save" hx-target="#app-main" hx-swap="outerHTML" class="task-form">
|
|
@@ -5,38 +5,45 @@
|
|
|
5
5
|
</div>
|
|
6
6
|
|
|
7
7
|
{% if milestones %}
|
|
8
|
-
<div class="milestone-
|
|
8
|
+
<div class="milestone-list">
|
|
9
9
|
{% for m in milestones %}
|
|
10
10
|
{% set r = milestone_rollups.get(m.id) %}
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
<button
|
|
12
|
+
class="milestone-list-item milestone-open-row {{ m.status }}"
|
|
13
|
+
style="--ms-color: {{ m.color or '#3567e0' }}"
|
|
14
|
+
hx-get="/partials/main?milestone={{ m.id }}&view=board{% if filters.show_closed %}&show_closed=1{% endif %}&stale_days={{ filters.stale_days }}"
|
|
15
|
+
hx-target="#app-main"
|
|
16
|
+
hx-swap="outerHTML"
|
|
17
|
+
title="Open milestone view"
|
|
18
|
+
>
|
|
19
|
+
<div class="milestone-list-head">
|
|
20
|
+
<div class="milestone-list-title">
|
|
14
21
|
<span class="mc-flag" aria-hidden="true">◆</span>
|
|
15
22
|
<strong>{{ m.title }}</strong>
|
|
16
23
|
<span class="ms-status {{ m.status }}">{{ m.status }}</span>
|
|
17
24
|
</div>
|
|
18
|
-
|
|
19
|
-
<div class="mc-projects">
|
|
20
|
-
{% for proj in m.projects %}
|
|
21
|
-
<span class="project" style="background: {{ project_colors.get(proj, '#2e6fd8') }}22; color: {{ project_colors.get(proj, '#2e6fd8') }}">{{ proj }}</span>
|
|
22
|
-
{% else %}
|
|
23
|
-
<span class="muted">No projects assigned</span>
|
|
24
|
-
{% endfor %}
|
|
25
|
-
</div>
|
|
26
|
-
<div class="mc-progress" title="{{ r.progress }}% complete">
|
|
27
|
-
<div style="width: {{ r.progress }}%; background: {{ m.color or '#3567e0' }}"></div>
|
|
28
|
-
</div>
|
|
29
|
-
<div class="mc-stats">
|
|
25
|
+
<div class="mc-stats compact">
|
|
30
26
|
<span>{{ r.total }} task{{ '' if r.total == 1 else 's' }}</span>
|
|
31
27
|
<span>{{ r.done }} done</span>
|
|
32
28
|
<span>{{ r.progress }}%</span>
|
|
33
29
|
{% if m.target_date %}<span class="mc-target">🎯 {{ m.target_date }}</span>{% endif %}
|
|
34
30
|
</div>
|
|
35
|
-
</button>
|
|
36
|
-
<div class="mc-foot">
|
|
37
|
-
<button class="btn-link" hx-get="/milestones/{{ m.id }}/panel?view=milestones{% if filters.show_closed %}&show_closed=1{% endif %}&stale_days={{ filters.stale_days }}" hx-target="#task-panel" hx-swap="innerHTML">Details</button>
|
|
38
31
|
</div>
|
|
39
|
-
|
|
32
|
+
{% if m.summary %}<p class="mc-summary clamp-1">{{ m.summary }}</p>{% endif %}
|
|
33
|
+
<div class="mc-projects compact">
|
|
34
|
+
{% for proj in m.projects[:3] %}
|
|
35
|
+
<span class="project" style="background: {{ project_colors.get(proj, '#2e6fd8') }}22; color: {{ project_colors.get(proj, '#2e6fd8') }}">{{ proj }}</span>
|
|
36
|
+
{% endfor %}
|
|
37
|
+
{% if m.projects|length > 3 %}
|
|
38
|
+
<span class="muted">+{{ m.projects|length - 3 }} more</span>
|
|
39
|
+
{% elif m.projects|length == 0 %}
|
|
40
|
+
<span class="muted">No projects assigned</span>
|
|
41
|
+
{% endif %}
|
|
42
|
+
</div>
|
|
43
|
+
<div class="mc-progress" title="{{ r.progress }}% complete">
|
|
44
|
+
<div style="width: {{ r.progress }}%; background: {{ m.color or '#3567e0' }}"></div>
|
|
45
|
+
</div>
|
|
46
|
+
</button>
|
|
40
47
|
{% endfor %}
|
|
41
48
|
</div>
|
|
42
49
|
{% else %}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<div class="panel-scroll">
|
|
2
2
|
<div class="panel-heading sticky">
|
|
3
3
|
<div class="task-panel-title-row">
|
|
4
|
+
<button type="button" class="btn-link task-collapse-btn" data-panel-collapse title="Collapse details" aria-label="Collapse details">×</button>
|
|
4
5
|
{% if filters.milestone %}
|
|
5
6
|
<button
|
|
6
7
|
type="button"
|
|
@@ -313,7 +314,7 @@
|
|
|
313
314
|
return;
|
|
314
315
|
}
|
|
315
316
|
if (ext === 'pdf') {
|
|
316
|
-
hoverCard.innerHTML += '<
|
|
317
|
+
hoverCard.innerHTML += '<div class="burndown-preview-body muted">PDF preview is not shown inline here. Use “Open in new tab” to view it.</div>';
|
|
317
318
|
return;
|
|
318
319
|
}
|
|
319
320
|
if (isTextExt(ext)) {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{taskunity-2026.3 → taskunity-2026.4}/src/taskunity/static/chartjs-adapter-date-fns.bundle.min.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|