taskunity 2026.3__tar.gz → 2026.5__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.5}/PKG-INFO +1 -1
- {taskunity-2026.3 → taskunity-2026.5}/pyproject.toml +1 -1
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity/app.py +6 -0
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity/static/app.css +154 -32
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity/templates/base.html +51 -0
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity/templates/partials/main.html +12 -34
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity/templates/partials/milestone_banner.html +1 -1
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity/templates/partials/milestone_panel.html +3 -1
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity/templates/partials/milestones.html +27 -20
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity/templates/partials/task_panel.html +2 -1
- {taskunity-2026.3 → taskunity-2026.5/src/taskunity.egg-info}/PKG-INFO +1 -1
- {taskunity-2026.3 → taskunity-2026.5}/MANIFEST.in +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/README.md +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/setup.cfg +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity/__init__.py +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity/cli.py +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity/models.py +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity/render.py +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity/static/chart.umd.min.js +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity/static/chartjs-adapter-date-fns.bundle.min.js +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity/static/htmx.min.js +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity/task_store.py +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity/templates/index.html +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity/templates/partials/board.html +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity/templates/partials/calendar.html +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity/templates/partials/projects.html +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity/templates/partials/task_list.html +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity/templates/partials/timeline.html +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity.egg-info/SOURCES.txt +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity.egg-info/dependency_links.txt +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity.egg-info/entry_points.txt +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity.egg-info/requires.txt +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/src/taskunity.egg-info/top_level.txt +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/tests/test_git_workspace_scope.py +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/tests/test_render_jsonantt.py +0 -0
- {taskunity-2026.3 → taskunity-2026.5}/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);
|
|
@@ -1236,19 +1311,21 @@ input[type="file"]::file-selector-button:hover {
|
|
|
1236
1311
|
}
|
|
1237
1312
|
.burndown-preview-link:hover { text-decoration: underline; }
|
|
1238
1313
|
.burndown-preview-thumb {
|
|
1239
|
-
display:
|
|
1314
|
+
display: block;
|
|
1240
1315
|
border: 1px solid var(--line);
|
|
1241
1316
|
border-radius: var(--radius-sm);
|
|
1242
1317
|
overflow: hidden;
|
|
1243
|
-
|
|
1244
|
-
max-
|
|
1318
|
+
width: 100%;
|
|
1319
|
+
max-width: 100%;
|
|
1320
|
+
max-height: 180px;
|
|
1245
1321
|
background: var(--card);
|
|
1246
1322
|
}
|
|
1247
1323
|
.burndown-preview-thumb img {
|
|
1248
1324
|
display: block;
|
|
1249
1325
|
width: 100%;
|
|
1326
|
+
max-width: 100%;
|
|
1250
1327
|
height: auto;
|
|
1251
|
-
object-fit:
|
|
1328
|
+
object-fit: contain;
|
|
1252
1329
|
}
|
|
1253
1330
|
.burndown-preview-text {
|
|
1254
1331
|
margin: 0;
|
|
@@ -1279,13 +1356,34 @@ input[type="file"]::file-selector-button:hover {
|
|
|
1279
1356
|
.pane-resizer { display: none; }
|
|
1280
1357
|
.side-panel { position: static; max-height: none; }
|
|
1281
1358
|
.panel-scroll { max-height: none; }
|
|
1359
|
+
.content { padding-top: calc(var(--controls-height) + .25rem); }
|
|
1360
|
+
.content-toolbar {
|
|
1361
|
+
top: var(--topbar-height);
|
|
1362
|
+
left: 1.4rem;
|
|
1363
|
+
right: 1.4rem;
|
|
1364
|
+
}
|
|
1282
1365
|
.board, .summary-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
1283
1366
|
}
|
|
1284
1367
|
@media (max-width: 700px) {
|
|
1285
|
-
.topbar
|
|
1368
|
+
.topbar { flex-direction: column; align-items: stretch; }
|
|
1369
|
+
.new-task-form { width: 100%; }
|
|
1370
|
+
.new-task-form .new-task-btn { width: 100%; }
|
|
1286
1371
|
.board, .summary-grid { grid-template-columns: 1fr; }
|
|
1287
1372
|
.timeline-row { grid-template-columns: 1fr; }
|
|
1288
1373
|
.layout { padding: .5rem; }
|
|
1374
|
+
.content { padding-top: calc(var(--controls-height) + .6rem); }
|
|
1375
|
+
.content-toolbar {
|
|
1376
|
+
left: .5rem;
|
|
1377
|
+
right: .5rem;
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
@media (max-width: 900px) {
|
|
1382
|
+
.mb-actions {
|
|
1383
|
+
width: 100%;
|
|
1384
|
+
justify-content: flex-start;
|
|
1385
|
+
}
|
|
1386
|
+
.mc-stats.compact { width: 100%; }
|
|
1289
1387
|
}
|
|
1290
1388
|
|
|
1291
1389
|
/* --- Milestones --- */
|
|
@@ -1305,32 +1403,55 @@ input[type="file"]::file-selector-button:hover {
|
|
|
1305
1403
|
.ms-status.done { background: color-mix(in srgb, var(--done) 14%, white); color: var(--done); border-color: color-mix(in srgb, var(--done) 35%, var(--line)); }
|
|
1306
1404
|
.ms-status.planned { background: var(--bg-soft); color: var(--muted); }
|
|
1307
1405
|
|
|
1308
|
-
.milestone-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1406
|
+
.milestone-list {
|
|
1407
|
+
display: grid;
|
|
1408
|
+
gap: .7rem;
|
|
1409
|
+
}
|
|
1410
|
+
.milestone-list-item {
|
|
1411
|
+
display: grid;
|
|
1412
|
+
gap: .42rem;
|
|
1312
1413
|
background: var(--card);
|
|
1414
|
+
color: var(--text);
|
|
1313
1415
|
border: 1px solid var(--line);
|
|
1314
1416
|
border-left: 4px solid var(--ms-color, var(--accent));
|
|
1315
|
-
border-radius: var(--radius);
|
|
1316
|
-
|
|
1417
|
+
border-radius: var(--radius-sm);
|
|
1418
|
+
padding: .68rem .8rem;
|
|
1317
1419
|
box-shadow: var(--shadow-sm);
|
|
1318
1420
|
}
|
|
1319
|
-
.milestone-open {
|
|
1320
|
-
|
|
1321
|
-
display: grid;
|
|
1322
|
-
gap: .5rem;
|
|
1421
|
+
.milestone-open-row {
|
|
1422
|
+
width: 100%;
|
|
1323
1423
|
text-align: left;
|
|
1324
|
-
background: transparent;
|
|
1325
|
-
color: var(--text);
|
|
1326
|
-
border: none;
|
|
1327
|
-
border-radius: 0;
|
|
1328
|
-
padding: .9rem 1rem;
|
|
1329
|
-
box-shadow: none;
|
|
1330
1424
|
}
|
|
1331
|
-
.milestone-open:hover {
|
|
1332
|
-
|
|
1333
|
-
|
|
1425
|
+
.milestone-open-row:hover {
|
|
1426
|
+
filter: none;
|
|
1427
|
+
border-color: color-mix(in srgb, var(--line) 60%, var(--accent));
|
|
1428
|
+
box-shadow: var(--shadow-md);
|
|
1429
|
+
}
|
|
1430
|
+
.milestone-list-head {
|
|
1431
|
+
display: flex;
|
|
1432
|
+
align-items: center;
|
|
1433
|
+
justify-content: space-between;
|
|
1434
|
+
gap: .5rem;
|
|
1435
|
+
flex-wrap: wrap;
|
|
1436
|
+
}
|
|
1437
|
+
.milestone-list-title {
|
|
1438
|
+
display: flex;
|
|
1439
|
+
align-items: center;
|
|
1440
|
+
gap: .45rem;
|
|
1441
|
+
min-width: 0;
|
|
1442
|
+
}
|
|
1443
|
+
.milestone-list-title strong {
|
|
1444
|
+
font-size: .93rem;
|
|
1445
|
+
min-width: 0;
|
|
1446
|
+
}
|
|
1447
|
+
.mc-stats.compact { gap: .5rem; font-size: .74rem; }
|
|
1448
|
+
.mc-projects.compact { gap: .25rem; }
|
|
1449
|
+
.mc-projects.compact .project { font-size: .72rem; padding: .1rem .42rem; }
|
|
1450
|
+
.clamp-1 {
|
|
1451
|
+
overflow: hidden;
|
|
1452
|
+
text-overflow: ellipsis;
|
|
1453
|
+
white-space: nowrap;
|
|
1454
|
+
}
|
|
1334
1455
|
.mc-flag { color: var(--ms-color, var(--accent)); font-size: .8rem; }
|
|
1335
1456
|
.mc-summary { margin: 0; color: var(--muted); font-size: .85rem; }
|
|
1336
1457
|
.mc-projects { display: flex; flex-wrap: wrap; gap: .3rem; }
|
|
@@ -1343,7 +1464,6 @@ input[type="file"]::file-selector-button:hover {
|
|
|
1343
1464
|
.mc-progress > div, .mb-progress > div { height: 100%; background: var(--accent); border-radius: 999px; }
|
|
1344
1465
|
.mc-stats { display: flex; flex-wrap: wrap; gap: .6rem; font-size: .76rem; color: var(--muted); }
|
|
1345
1466
|
.mc-target { margin-left: auto; }
|
|
1346
|
-
.mc-foot { border-top: 1px solid var(--line); padding: .4rem 1rem; display: flex; justify-content: flex-end; }
|
|
1347
1467
|
.new-milestone-form { margin-top: 1.1rem; border-top: 1px solid var(--line); padding-top: 1rem; display: grid; gap: .6rem; }
|
|
1348
1468
|
.new-milestone-form h3 { margin: 0; font-size: .95rem; }
|
|
1349
1469
|
|
|
@@ -1360,11 +1480,13 @@ input[type="file"]::file-selector-button:hover {
|
|
|
1360
1480
|
box-shadow: var(--shadow-sm);
|
|
1361
1481
|
}
|
|
1362
1482
|
.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; }
|
|
1483
|
+
.mb-title { display: flex; align-items: flex-start; gap: .55rem; min-width: 0; flex: 1 1 18rem; }
|
|
1364
1484
|
.mb-flag { color: var(--ms-color, var(--accent)); font-size: 1rem; line-height: 1.4; }
|
|
1365
1485
|
.mb-title h2 { margin: 0; font-size: 1.1rem; }
|
|
1366
1486
|
.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; }
|
|
1487
|
+
.mb-actions { display: flex; align-items: center; justify-content: flex-end; gap: .5rem; flex: 1 1 16rem; flex-wrap: wrap; }
|
|
1488
|
+
.mb-actions .btn-link,
|
|
1489
|
+
.mb-actions .pill { white-space: nowrap; }
|
|
1368
1490
|
.mb-rollup { display: grid; gap: .45rem; }
|
|
1369
1491
|
.mb-stats { display: flex; flex-wrap: wrap; gap: .9rem; font-size: .8rem; color: var(--muted); }
|
|
1370
1492
|
.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.5}/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.5}/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
|