boards: task/bug/backlog boards, outline shortened, workspace index improved
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7b9f7a1fb9
commit
5bcabe2c67
|
|
@ -0,0 +1,86 @@
|
||||||
|
.board-page
|
||||||
|
{
|
||||||
|
max-width: 75em;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
gap: calc( var( --font-size ) * 1.5 );
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lane
|
||||||
|
{
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lane-header
|
||||||
|
{
|
||||||
|
font-size: calc( var( --font-size ) * 1.1 );
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.12em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var( --accent );
|
||||||
|
margin-bottom: calc( var( --font-size ) * 1 );
|
||||||
|
padding-bottom: calc( var( --font-size ) * 0.5 );
|
||||||
|
border-bottom: 1px solid var( --border );
|
||||||
|
}
|
||||||
|
|
||||||
|
task-item
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
background: var( --surface );
|
||||||
|
border: 1px solid var( --item-color, var( --border ) );
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: calc( var( --font-size ) * 0.75 );
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
task-title
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
padding: calc( var( --font-size ) * 0.5 ) calc( var( --font-size ) * 0.85 );
|
||||||
|
font-size: calc( var( --font-size ) * 0.9 );
|
||||||
|
font-weight: 600;
|
||||||
|
color: var( --item-color, var( --text ) );
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
task-title::before
|
||||||
|
{
|
||||||
|
content: '▾ ';
|
||||||
|
font-size: 0.75em;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
task-item.hide-content task-title::before
|
||||||
|
{
|
||||||
|
content: '▸ ';
|
||||||
|
}
|
||||||
|
|
||||||
|
task-content
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
padding: calc( var( --font-size ) * 0.4 ) calc( var( --font-size ) * 0.85 ) calc( var( --font-size ) * 0.65 );
|
||||||
|
font-size: calc( var( --font-size ) * 0.85 );
|
||||||
|
color: var( --muted );
|
||||||
|
border-top: 1px solid var( --border );
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
task-item.hide-content task-content
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
task-item.red { --item-color: #c96b6b; }
|
||||||
|
task-item.green { --item-color: #57a870; }
|
||||||
|
task-item.blue { --item-color: #7c8cff; }
|
||||||
|
task-item.yellow { --item-color: #c4a040; }
|
||||||
|
task-item.purple { --item-color: #9060d0; }
|
||||||
|
task-item.orange { --item-color: #c47840; }
|
||||||
|
|
@ -2,6 +2,15 @@ var NAV_DATA = {
|
||||||
title: 'Workspace',
|
title: 'Workspace',
|
||||||
path: 'index.html',
|
path: 'index.html',
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
title: 'Boards',
|
||||||
|
path: 'boards/index.html',
|
||||||
|
children: [
|
||||||
|
{ title: 'Tasks', path: 'boards/tasks.html' },
|
||||||
|
{ title: 'Bugs', path: 'boards/bugs.html' },
|
||||||
|
{ title: 'Backlog', path: 'boards/backlog.html' },
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'Outline',
|
title: 'Outline',
|
||||||
path: 'outline/index.html',
|
path: 'outline/index.html',
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@
|
||||||
inkscape:document-units="px"
|
inkscape:document-units="px"
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
inkscape:zoom="0.20890503"
|
inkscape:zoom="0.20890503"
|
||||||
inkscape:cx="1969.7946"
|
inkscape:cx="1605.9929"
|
||||||
inkscape:cy="1179.962"
|
inkscape:cy="1093.7985"
|
||||||
inkscape:window-width="1920"
|
inkscape:window-width="1920"
|
||||||
inkscape:window-height="1017"
|
inkscape:window-height="1017"
|
||||||
inkscape:window-x="-8"
|
inkscape:window-x="-8"
|
||||||
|
|
@ -35,6 +35,34 @@
|
||||||
inkscape:window-maximized="1"
|
inkscape:window-maximized="1"
|
||||||
inkscape:current-layer="g17" /><defs
|
inkscape:current-layer="g17" /><defs
|
||||||
id="defs2"><linearGradient
|
id="defs2"><linearGradient
|
||||||
|
id="linearGradient19"
|
||||||
|
inkscape:collect="never"><stop
|
||||||
|
style="stop-color:#040bff;stop-opacity:0;"
|
||||||
|
offset="0.37807184"
|
||||||
|
id="stop19" /><stop
|
||||||
|
style="stop-color:#28ece5;stop-opacity:0.26666668;"
|
||||||
|
offset="0.71330941"
|
||||||
|
id="stop22" /><stop
|
||||||
|
style="stop-color:#15ff04;stop-opacity:0.5333333;"
|
||||||
|
offset="0.86158413"
|
||||||
|
id="stop21" /><stop
|
||||||
|
style="stop-color:#f8de48;stop-opacity:1;"
|
||||||
|
offset="0.93746954"
|
||||||
|
id="stop23" /><stop
|
||||||
|
style="stop-color:#ff0404;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop20" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3"
|
||||||
|
inkscape:collect="never"><stop
|
||||||
|
style="stop-color:#0495ff;stop-opacity:0;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3" /><stop
|
||||||
|
style="stop-color:#0495ff;stop-opacity:1;"
|
||||||
|
offset="0.46630496"
|
||||||
|
id="stop15" /><stop
|
||||||
|
style="stop-color:#0495ff;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop4" /></linearGradient><linearGradient
|
||||||
id="linearGradient12"
|
id="linearGradient12"
|
||||||
inkscape:collect="never"><stop
|
inkscape:collect="never"><stop
|
||||||
style="stop-color:#00bcf5;stop-opacity:1;"
|
style="stop-color:#00bcf5;stop-opacity:1;"
|
||||||
|
|
@ -157,7 +185,102 @@
|
||||||
height="2.6460531"><feGaussianBlur
|
height="2.6460531"><feGaussianBlur
|
||||||
inkscape:collect="always"
|
inkscape:collect="always"
|
||||||
stdDeviation="329.39922"
|
stdDeviation="329.39922"
|
||||||
id="feGaussianBlur16" /></filter></defs><g
|
id="feGaussianBlur16" /></filter><linearGradient
|
||||||
|
inkscape:collect="never"
|
||||||
|
xlink:href="#linearGradient3"
|
||||||
|
id="linearGradient4"
|
||||||
|
x1="564.68774"
|
||||||
|
y1="994.08865"
|
||||||
|
x2="2793.1636"
|
||||||
|
y2="994.08865"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.7990724,0,0,0.1301594,-1341.5831,915.47103)" /><linearGradient
|
||||||
|
inkscape:collect="never"
|
||||||
|
xlink:href="#linearGradient3"
|
||||||
|
id="linearGradient15"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.7990724,0,0,0.03619034,-109.50713,1057.1184)"
|
||||||
|
x1="564.68774"
|
||||||
|
y1="994.08865"
|
||||||
|
x2="2793.1636"
|
||||||
|
y2="994.08865" /><linearGradient
|
||||||
|
inkscape:collect="never"
|
||||||
|
xlink:href="#linearGradient3"
|
||||||
|
id="linearGradient16"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.7990724,0,0,0.03619034,-1260.748,958.98764)"
|
||||||
|
x1="564.68774"
|
||||||
|
y1="994.08865"
|
||||||
|
x2="2793.1636"
|
||||||
|
y2="994.08865" /><linearGradient
|
||||||
|
inkscape:collect="never"
|
||||||
|
xlink:href="#linearGradient5"
|
||||||
|
id="linearGradient17"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.4051037,0,0,1.4051038,-1136.1343,-483.68581)"
|
||||||
|
x1="2094.8511"
|
||||||
|
y1="824.97723"
|
||||||
|
x2="2175.8127"
|
||||||
|
y2="1376.4214" /><radialGradient
|
||||||
|
inkscape:collect="never"
|
||||||
|
xlink:href="#linearGradient19"
|
||||||
|
id="radialGradient20"
|
||||||
|
cx="1119.998"
|
||||||
|
cy="950.70941"
|
||||||
|
fx="1119.998"
|
||||||
|
fy="950.70941"
|
||||||
|
r="624.87445"
|
||||||
|
gradientTransform="matrix(1,0,0,1.0015514,816.16034,91.868989)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><radialGradient
|
||||||
|
inkscape:collect="never"
|
||||||
|
xlink:href="#linearGradient19"
|
||||||
|
id="radialGradient23"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.50283735,0,0,0.50361745,772.23007,617.91495)"
|
||||||
|
cx="1119.998"
|
||||||
|
cy="950.70941"
|
||||||
|
fx="1119.998"
|
||||||
|
fy="950.70941"
|
||||||
|
r="624.87445" /><radialGradient
|
||||||
|
inkscape:collect="never"
|
||||||
|
xlink:href="#linearGradient19"
|
||||||
|
id="radialGradient24"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.75180429,0,0,0.75297064,658.53439,589.08112)"
|
||||||
|
cx="1119.998"
|
||||||
|
cy="950.70941"
|
||||||
|
fx="1119.998"
|
||||||
|
fy="950.70941"
|
||||||
|
r="624.87445" /><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
style="color-interpolation-filters:sRGB"
|
||||||
|
id="filter24"
|
||||||
|
x="-0.166101"
|
||||||
|
y="-0.16584373"
|
||||||
|
width="1.332202"
|
||||||
|
height="1.3316875"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="65.026231"
|
||||||
|
id="feGaussianBlur24" /></filter><linearGradient
|
||||||
|
inkscape:collect="never"
|
||||||
|
xlink:href="#linearGradient3"
|
||||||
|
id="linearGradient24"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.55162383,0,0,0.03990891,-214.34719,-1814.9918)"
|
||||||
|
x1="564.68774"
|
||||||
|
y1="994.08865"
|
||||||
|
x2="2793.1636"
|
||||||
|
y2="994.08865" /><radialGradient
|
||||||
|
inkscape:collect="never"
|
||||||
|
xlink:href="#linearGradient19"
|
||||||
|
id="radialGradient25"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.75180429,0,0,0.75297064,811.71404,129.54216)"
|
||||||
|
cx="1119.998"
|
||||||
|
cy="950.70941"
|
||||||
|
fx="1119.998"
|
||||||
|
fy="950.70941"
|
||||||
|
r="624.87445" /></defs><g
|
||||||
inkscape:label="Content"
|
inkscape:label="Content"
|
||||||
inkscape:groupmode="layer"
|
inkscape:groupmode="layer"
|
||||||
id="layer1"
|
id="layer1"
|
||||||
|
|
@ -274,4 +397,91 @@
|
||||||
id="tspan14"
|
id="tspan14"
|
||||||
x="1480.9904"
|
x="1480.9904"
|
||||||
y="1344.5404"
|
y="1344.5404"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Kode Mono';-inkscape-font-specification:'Kode Mono Bold';fill:#00cbf5;fill-opacity:1;stroke-width:16.8787">COMPUTER! ZOOM IN!</tspan></text></g></g></g></svg>
|
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Kode Mono';-inkscape-font-specification:'Kode Mono Bold';fill:#00cbf5;fill-opacity:1;stroke-width:16.8787">COMPUTER! ZOOM IN!</tspan></text></g><rect
|
||||||
|
style="opacity:0.208678;mix-blend-mode:screen;fill:url(#linearGradient4);stroke:none;stroke-width:9.67817;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect3"
|
||||||
|
width="4009.1892"
|
||||||
|
height="51.572651"
|
||||||
|
x="-325.66898"
|
||||||
|
y="1019.0746"
|
||||||
|
ry="0.16230841" /><rect
|
||||||
|
style="opacity:0.053719;mix-blend-mode:screen;fill:url(#linearGradient15);stroke:none;stroke-width:5.10331;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect15"
|
||||||
|
width="4009.1892"
|
||||||
|
height="14.339585"
|
||||||
|
x="906.40698"
|
||||||
|
y="1085.9249"
|
||||||
|
ry="0.045129254" /><rect
|
||||||
|
style="opacity:0.163223;mix-blend-mode:screen;fill:url(#linearGradient16);stroke:none;stroke-width:5.10331;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect16"
|
||||||
|
width="4009.1892"
|
||||||
|
height="14.339585"
|
||||||
|
x="-244.83385"
|
||||||
|
y="987.79419"
|
||||||
|
ry="0.045129254" /><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:italic;font-variant:normal;font-weight:900;font-stretch:normal;font-size:642.307px;line-height:1.4;font-family:Barlow;-inkscape-font-specification:'Barlow Heavy Italic';writing-mode:lr-tb;direction:ltr;display:inline;opacity:0.05319149;mix-blend-mode:screen;fill:#04ff1d;fill-opacity:1;stroke-width:16.0577;stroke-linecap:round;stroke-linejoin:round"
|
||||||
|
x="1045.7648"
|
||||||
|
y="1203.9724"
|
||||||
|
id="text17"
|
||||||
|
transform="matrix(0.95321043,0,-0.10226778,1.0490863,0,0)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan17"
|
||||||
|
x="1045.7648"
|
||||||
|
y="1203.9724"
|
||||||
|
style="font-style:italic;font-variant:normal;font-weight:900;font-stretch:normal;font-family:Barlow;-inkscape-font-specification:'Barlow Heavy Italic';fill:#04ff1d;fill-opacity:1;stroke-width:16.0577">ROJECT</tspan></text><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:italic;font-variant:normal;font-weight:900;font-stretch:normal;font-size:650.011px;line-height:1.4;font-family:Barlow;-inkscape-font-specification:'Barlow Heavy Italic';writing-mode:lr-tb;direction:ltr;display:inline;opacity:0.07021277;mix-blend-mode:screen;fill:#ea04ff;fill-opacity:1;stroke-width:16.2503;stroke-linecap:round;stroke-linejoin:round"
|
||||||
|
x="1042.5992"
|
||||||
|
y="1194.2871"
|
||||||
|
id="text18"
|
||||||
|
transform="matrix(0.95321043,0,-0.10226778,1.0490863,0,0)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan18"
|
||||||
|
x="1042.5992"
|
||||||
|
y="1194.2871"
|
||||||
|
style="font-style:italic;font-variant:normal;font-weight:900;font-stretch:normal;font-family:Barlow;-inkscape-font-specification:'Barlow Heavy Italic';fill:#ea04ff;fill-opacity:1;stroke-width:16.2503">ROJECT</tspan></text><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:italic;font-variant:normal;font-weight:900;font-stretch:normal;font-size:650.011px;line-height:1.4;font-family:Barlow;-inkscape-font-specification:'Barlow Heavy Italic';writing-mode:lr-tb;direction:ltr;display:inline;opacity:0.02553191;mix-blend-mode:screen;fill:#f7ff04;fill-opacity:1;stroke-width:16.2503;stroke-linecap:round;stroke-linejoin:round"
|
||||||
|
x="1056.5159"
|
||||||
|
y="1201.1315"
|
||||||
|
id="text19"
|
||||||
|
transform="matrix(0.95321043,0,-0.10226778,1.0490863,0,0)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan19"
|
||||||
|
x="1056.5159"
|
||||||
|
y="1201.1315"
|
||||||
|
style="font-style:italic;font-variant:normal;font-weight:900;font-stretch:normal;font-family:Barlow;-inkscape-font-specification:'Barlow Heavy Italic';fill:#f7ff04;fill-opacity:1;stroke-width:16.2503">ROJECT</tspan></text><ellipse
|
||||||
|
style="opacity:0.01652893;mix-blend-mode:screen;fill:url(#radialGradient20);fill-opacity:1;stroke:none;stroke-width:18.4735;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="path19"
|
||||||
|
cx="1936.1583"
|
||||||
|
cy="1044.0532"
|
||||||
|
rx="624.87445"
|
||||||
|
ry="625.84381" /><ellipse
|
||||||
|
style="opacity:0.0785124;mix-blend-mode:screen;fill:url(#radialGradient23);fill-opacity:1;stroke:none;stroke-width:9.28917;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="ellipse23"
|
||||||
|
cx="1335.4069"
|
||||||
|
cy="1096.7087"
|
||||||
|
rx="314.21021"
|
||||||
|
ry="314.69763" /><ellipse
|
||||||
|
style="opacity:0.10638298;mix-blend-mode:screen;fill:url(#radialGradient24);fill-opacity:1;stroke:none;stroke-width:13.8885;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;filter:url(#filter24)"
|
||||||
|
id="ellipse24"
|
||||||
|
cx="1500.5537"
|
||||||
|
cy="1304.9374"
|
||||||
|
rx="469.78329"
|
||||||
|
ry="470.51205" /><rect
|
||||||
|
style="opacity:0.208678;mix-blend-mode:screen;fill:url(#linearGradient24);stroke:none;stroke-width:2.96748;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||||
|
id="rect24"
|
||||||
|
width="1229.2803"
|
||||||
|
height="15.812984"
|
||||||
|
x="97.148048"
|
||||||
|
y="-1783.2252"
|
||||||
|
ry="0.049766306"
|
||||||
|
transform="rotate(100.78149)" /><ellipse
|
||||||
|
style="opacity:0.10743802;mix-blend-mode:screen;fill:url(#radialGradient25);fill-opacity:1;stroke:none;stroke-width:13.8885;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;filter:url(#filter24)"
|
||||||
|
id="ellipse25"
|
||||||
|
cx="1653.7334"
|
||||||
|
cy="845.39844"
|
||||||
|
rx="469.78329"
|
||||||
|
ry="470.51205"
|
||||||
|
transform="matrix(0.34298317,0,0,0.34298317,1105.6782,569.80159)" /></g></g></svg>
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 24 KiB |
|
|
@ -198,3 +198,90 @@ pre code
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.board-page
|
||||||
|
{
|
||||||
|
max-width: 75em;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board
|
||||||
|
{
|
||||||
|
display: flex;
|
||||||
|
gap: calc( var( --font-size ) * 1.5 );
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lane
|
||||||
|
{
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lane-header
|
||||||
|
{
|
||||||
|
font-size: calc( var( --font-size ) * 1.1 );
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.12em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var( --accent );
|
||||||
|
margin-bottom: calc( var( --font-size ) * 1 );
|
||||||
|
padding-bottom: calc( var( --font-size ) * 0.5 );
|
||||||
|
border-bottom: 1px solid var( --border );
|
||||||
|
}
|
||||||
|
|
||||||
|
task-item
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
background: var( --surface );
|
||||||
|
border: 1px solid var( --item-color, var( --border ) );
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: calc( var( --font-size ) * 0.75 );
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
task-title
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
padding: calc( var( --font-size ) * 0.5 ) calc( var( --font-size ) * 0.85 );
|
||||||
|
font-size: calc( var( --font-size ) * 0.9 );
|
||||||
|
font-weight: 600;
|
||||||
|
color: var( --item-color, var( --text ) );
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
task-title::before
|
||||||
|
{
|
||||||
|
content: '▾ ';
|
||||||
|
font-size: 0.75em;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
task-item.hide-content task-title::before
|
||||||
|
{
|
||||||
|
content: '▸ ';
|
||||||
|
}
|
||||||
|
|
||||||
|
task-content
|
||||||
|
{
|
||||||
|
display: block;
|
||||||
|
padding: calc( var( --font-size ) * 0.4 ) calc( var( --font-size ) * 0.85 ) calc( var( --font-size ) * 0.65 );
|
||||||
|
font-size: calc( var( --font-size ) * 0.85 );
|
||||||
|
color: var( --muted );
|
||||||
|
border-top: 1px solid var( --border );
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
task-item.hide-content task-content
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
task-item.red { --item-color: #c96b6b; }
|
||||||
|
task-item.green { --item-color: #57a870; }
|
||||||
|
task-item.blue { --item-color: #7c8cff; }
|
||||||
|
task-item.yellow { --item-color: #c4a040; }
|
||||||
|
task-item.purple { --item-color: #9060d0; }
|
||||||
|
task-item.orange { --item-color: #c47840; }
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Backlog - Roject</title>
|
||||||
|
<link rel="stylesheet" href="../_assets_/styles.css">
|
||||||
|
<link rel="stylesheet" href="../_assets_/nav.css">
|
||||||
|
<link rel="stylesheet" href="../_assets_/boards.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="board-page">
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<p class="date">Board</p>
|
||||||
|
<h1>Backlog</h1>
|
||||||
|
<p class="subtitle">Big visions and user stories. Too complex or vague for tasks, but important for direction.</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="board">
|
||||||
|
|
||||||
|
<div class="lane">
|
||||||
|
<div class="lane-header">MVP</div>
|
||||||
|
|
||||||
|
<task-item class="blue hide-content">
|
||||||
|
<task-title>Local Git Repository Integration</task-title>
|
||||||
|
<task-content>
|
||||||
|
Git integration inside the editor: file status indicators in the tree, staging,
|
||||||
|
commit, push, pull, diffs, and history. Depends on local filesystem access.
|
||||||
|
Once Roject can open an arbitrary local directory, the git repo is already there.
|
||||||
|
Implementation via simple-git (thin Node.js wrapper around the git CLI).
|
||||||
|
</task-content>
|
||||||
|
</task-item>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="lane">
|
||||||
|
<div class="lane-header">Nice To Have</div>
|
||||||
|
|
||||||
|
<task-item class="purple hide-content">
|
||||||
|
<task-title>Internet Tunnel / Port Relay</task-title>
|
||||||
|
<task-content>
|
||||||
|
Allow local devices — a Stable Diffusion instance, a local LLM, a GDScript language
|
||||||
|
server — to be accessible to authorised Roject users over the internet, routed through
|
||||||
|
the Roject server via a persistent WebSocket. Primary use case: phone as a thin client
|
||||||
|
while the main machine handles all heavy processing.
|
||||||
|
</task-content>
|
||||||
|
</task-item>
|
||||||
|
|
||||||
|
<task-item class="purple hide-content">
|
||||||
|
<task-title>Real-Time Multi-User Collaboration</task-title>
|
||||||
|
<task-content>
|
||||||
|
Multiple users editing the same file simultaneously with presence indicators
|
||||||
|
and conflict resolution. Likely via operational transforms or CRDTs.
|
||||||
|
Significant architecture work; not planned for the near term.
|
||||||
|
</task-content>
|
||||||
|
</task-item>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="lane">
|
||||||
|
<div class="lane-header">To The Moon</div>
|
||||||
|
|
||||||
|
<task-item class="orange hide-content">
|
||||||
|
<task-title>Mobile App (PWA first, native shell later)</task-title>
|
||||||
|
<task-content>
|
||||||
|
Make Roject usable on a phone or tablet. Quickest path: PWA with a manifest
|
||||||
|
and service worker — works in Safari and Chrome on Android and iOS without an
|
||||||
|
app store. If native capabilities are later needed, Capacitor can wrap the same
|
||||||
|
web app. A React Native or Flutter rewrite is not planned.
|
||||||
|
The realistic mobile workflow is browsing files, reading output, and light edits
|
||||||
|
rather than heavy coding.
|
||||||
|
</task-content>
|
||||||
|
</task-item>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
Roject — backlog
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<script>var NAV_ROOT = '../';</script>
|
||||||
|
<script src="../_assets_/nav-data.js"></script>
|
||||||
|
<script src="../_assets_/nav.js"></script>
|
||||||
|
<script>
|
||||||
|
document.querySelectorAll( 'task-title' ).forEach( title =>
|
||||||
|
{
|
||||||
|
title.addEventListener( 'click', () =>
|
||||||
|
{
|
||||||
|
title.closest( 'task-item' ).classList.toggle( 'hide-content' );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Bugs - Roject</title>
|
||||||
|
<link rel="stylesheet" href="../_assets_/styles.css">
|
||||||
|
<link rel="stylesheet" href="../_assets_/nav.css">
|
||||||
|
<link rel="stylesheet" href="../_assets_/boards.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="board-page">
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<p class="date">Board</p>
|
||||||
|
<h1>Bugs</h1>
|
||||||
|
<p class="subtitle">Click a task title to expand or collapse its content.</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="board">
|
||||||
|
|
||||||
|
<div class="lane">
|
||||||
|
<div class="lane-header">Critical</div>
|
||||||
|
|
||||||
|
<task-item class="red hide-content">
|
||||||
|
<task-title>Projects Lookup/Editing Broken</task-title>
|
||||||
|
<task-content>
|
||||||
|
The projects a user can view should only be the ones the user created
|
||||||
|
or is a member of. Currently a second account can open and edit the content
|
||||||
|
of a project belonging to the first account.
|
||||||
|
</task-content>
|
||||||
|
</task-item>
|
||||||
|
|
||||||
|
<task-item class="red hide-content">
|
||||||
|
<task-title>401 Not Handled in Data-Fetching Components</task-title>
|
||||||
|
<task-content>
|
||||||
|
Components that fetch data (groups, projects, etc.) do not handle 401 responses
|
||||||
|
gracefully — they crash when the API returns an error object instead of an array.
|
||||||
|
Should show an appropriate message or redirect to login.
|
||||||
|
</task-content>
|
||||||
|
</task-item>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="lane">
|
||||||
|
<div class="lane-header">Visual / UI / UX</div>
|
||||||
|
|
||||||
|
<task-item class="yellow hide-content">
|
||||||
|
<task-title>Code Editor Needs Click to Show Content</task-title>
|
||||||
|
<task-content>
|
||||||
|
The code editor's content is not rendered immediately — it needs a click to show up.
|
||||||
|
This happens when selecting a fresh new tab or when a file is selected.
|
||||||
|
</task-content>
|
||||||
|
</task-item>
|
||||||
|
|
||||||
|
<task-item class="yellow hide-content">
|
||||||
|
<task-title>Right Panel Has No Default Content</task-title>
|
||||||
|
<task-content>
|
||||||
|
The Right panel is empty by default and relies on manual tab dragging to populate.
|
||||||
|
Should show a placeholder or hint explaining what to do.
|
||||||
|
</task-content>
|
||||||
|
</task-item>
|
||||||
|
|
||||||
|
<task-item class="yellow hide-content">
|
||||||
|
<task-title>Portrait Mode Section Switcher Not Wired</task-title>
|
||||||
|
<task-content>
|
||||||
|
The secondary section switcher in portrait mode (when a panel has multiple
|
||||||
|
side-by-side sections) is not yet functional.
|
||||||
|
</task-content>
|
||||||
|
</task-item>
|
||||||
|
|
||||||
|
<task-item class="yellow hide-content">
|
||||||
|
<task-title>Member List Shows Raw UUIDs</task-title>
|
||||||
|
<task-content>
|
||||||
|
The member list UI displays raw UUIDs instead of usernames.
|
||||||
|
Should resolve user IDs to display names via the auth service.
|
||||||
|
</task-content>
|
||||||
|
</task-item>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
Roject — bugs
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<script>var NAV_ROOT = '../';</script>
|
||||||
|
<script src="../_assets_/nav-data.js"></script>
|
||||||
|
<script src="../_assets_/nav.js"></script>
|
||||||
|
<script>
|
||||||
|
document.querySelectorAll( 'task-title' ).forEach( title =>
|
||||||
|
{
|
||||||
|
title.addEventListener( 'click', () =>
|
||||||
|
{
|
||||||
|
title.closest( 'task-item' ).classList.toggle( 'hide-content' );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Boards — Roject</title>
|
||||||
|
<link rel="stylesheet" href="../_assets_/styles.css">
|
||||||
|
<link rel="stylesheet" href="../_assets_/nav.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="page">
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<p class="date">Boards</p>
|
||||||
|
<h1>Roject</h1>
|
||||||
|
<p class="subtitle">Planning and tracking boards for development work.</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3><a href="tasks.html">Tasks</a></h3>
|
||||||
|
<p>
|
||||||
|
Current development sprint. Active work items with To Do, In Progress, and Done lanes.
|
||||||
|
We try to keep this focused — only things being worked on now or very soon.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3><a href="bugs.html">Bugs</a></h3>
|
||||||
|
<p>
|
||||||
|
Known issues we want to fix later. Organized by severity: Critical for things that
|
||||||
|
break correctness or security, Visual/UI/UX for things that look or feel wrong.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3><a href="backlog.html">Backlog</a></h3>
|
||||||
|
<p>
|
||||||
|
Big user stories and visions. Too complex or vague to be tasks, but important for
|
||||||
|
understanding the direction. Lanes: MVP for the core product vision,
|
||||||
|
Nice To Have for quality-of-life additions, To The Moon for longer-horizon ambitions.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
Roject — boards
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<script>var NAV_ROOT = '../';</script>
|
||||||
|
<script src="../_assets_/nav-data.js"></script>
|
||||||
|
<script src="../_assets_/nav.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Tasks - Roject</title>
|
||||||
|
<link rel="stylesheet" href="../_assets_/styles.css">
|
||||||
|
<link rel="stylesheet" href="../_assets_/nav.css">
|
||||||
|
<link rel="stylesheet" href="../_assets_/boards.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="board-page">
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<p class="date">Board</p>
|
||||||
|
<h1>Tasks</h1>
|
||||||
|
<p class="subtitle">Click a task title to expand or collapse its content.</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="board">
|
||||||
|
|
||||||
|
<div class="lane">
|
||||||
|
<div class="lane-header">To Do</div>
|
||||||
|
|
||||||
|
<task-item class="blue hide-content">
|
||||||
|
<task-title>Remote Projects in Electron</task-title>
|
||||||
|
<task-content>
|
||||||
|
Allow the Electron app to connect to roject.rokojori.com and list remote projects
|
||||||
|
alongside local ones. The JWT is already available;
|
||||||
|
it's a matter of pointing requests at the remote URL with the token.
|
||||||
|
</task-content>
|
||||||
|
</task-item>
|
||||||
|
|
||||||
|
<task-item class="blue hide-content">
|
||||||
|
<task-title>Unauthenticated Landing Screen</task-title>
|
||||||
|
<task-content>
|
||||||
|
Unauthenticated users currently crash on dashboard components.
|
||||||
|
They should land on a screen that explains the app and shows a login link.
|
||||||
|
</task-content>
|
||||||
|
</task-item>
|
||||||
|
|
||||||
|
<task-item class="blue hide-content">
|
||||||
|
<task-title>Replace browser confirm() in group editor and account delete</task-title>
|
||||||
|
<task-content>
|
||||||
|
The group editor and account delete button still use the browser confirm() dialog.
|
||||||
|
Replace with the custom <confirm-dialog> component already used elsewhere.
|
||||||
|
</task-content>
|
||||||
|
</task-item>
|
||||||
|
|
||||||
|
<task-item class="blue hide-content">
|
||||||
|
<task-title>MediaViewerPanel for images and PDFs</task-title>
|
||||||
|
<task-content>
|
||||||
|
Non-text files (images, PDFs) are visible in the file tree but not openable.
|
||||||
|
Add a MediaViewerPanel and register it in FileEditorRegistry for common media types.
|
||||||
|
</task-content>
|
||||||
|
</task-item>
|
||||||
|
|
||||||
|
<task-item class="blue hide-content">
|
||||||
|
<task-title>CI deploy email notification</task-title>
|
||||||
|
<task-content>
|
||||||
|
The /api/deploy endpoint should send an email notification after each successful
|
||||||
|
restart so deploys are visible without checking server logs.
|
||||||
|
</task-content>
|
||||||
|
</task-item>
|
||||||
|
|
||||||
|
<task-item class="blue hide-content">
|
||||||
|
<task-title>Switch Gitea webhook to dev branch</task-title>
|
||||||
|
<task-content>
|
||||||
|
The Gitea webhook currently triggers on pushes to main, redeploying on every commit.
|
||||||
|
Switch to a dev branch so main is the stable release target.
|
||||||
|
</task-content>
|
||||||
|
</task-item>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="lane">
|
||||||
|
<div class="lane-header">In Progress</div>
|
||||||
|
|
||||||
|
<task-item class="yellow hide-content">
|
||||||
|
<task-title>Local Filesystem Access</task-title>
|
||||||
|
<task-content>
|
||||||
|
Extend the file tree to browse arbitrary directories on the host
|
||||||
|
machine using Node.js fs rather than the server's JSON-backed project storage.
|
||||||
|
</task-content>
|
||||||
|
</task-item>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="lane">
|
||||||
|
<div class="lane-header">Done</div>
|
||||||
|
|
||||||
|
<task-item class="green hide-content">
|
||||||
|
<task-title>Electron Desktop App Shell</task-title>
|
||||||
|
<task-content>
|
||||||
|
Login window, JWT auth via API, Authorization header injection, token persistence,
|
||||||
|
ROJECT_ROOT path fix, ELECTRON_RUN_AS_NODE workaround. Full local-only Electron app working.
|
||||||
|
</task-content>
|
||||||
|
</task-item>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
Roject — tasks
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<script>var NAV_ROOT = '../';</script>
|
||||||
|
<script src="../_assets_/nav-data.js"></script>
|
||||||
|
<script src="../_assets_/nav.js"></script>
|
||||||
|
<script>
|
||||||
|
document.querySelectorAll( 'task-title' ).forEach( title =>
|
||||||
|
{
|
||||||
|
title.addEventListener( 'click', () =>
|
||||||
|
{
|
||||||
|
title.closest( 'task-item' ).classList.toggle( 'hide-content' );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -161,20 +161,60 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Workspace boards system</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>Task boards (<code>workspace/boards/</code>)</h3>
|
||||||
|
<p>
|
||||||
|
A new Boards section added to the workspace with three boards:
|
||||||
|
<strong>Tasks</strong> (current sprint, To Do / In Progress / Done),
|
||||||
|
<strong>Bugs</strong> (Critical and Visual/UI/UX lanes),
|
||||||
|
<strong>Backlog</strong> (MVP / Nice To Have / To The Moon).
|
||||||
|
Each board uses custom HTML elements — <code><task-item></code>,
|
||||||
|
<code><task-title></code>, <code><task-content></code> — styled
|
||||||
|
to match the workspace visual language (CSS variables, same spacing rhythm,
|
||||||
|
lane headers matching <code>section h2</code>). Clicking a title toggles
|
||||||
|
the content open/closed via <code>hide-content</code> class.
|
||||||
|
Shared styles extracted to <code>workspace/_assets_/boards.css</code>.
|
||||||
|
</p>
|
||||||
|
<div class="tags">
|
||||||
|
<span class="tag">workspace/boards/</span>
|
||||||
|
<span class="tag">task-item</span>
|
||||||
|
<span class="tag">boards.css</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>Outline shortened</h3>
|
||||||
|
<p>
|
||||||
|
<code>workspace/outline/index.html</code> was rewritten to remove the long
|
||||||
|
numbered to-do list and the "Smaller open improvements" bullet list.
|
||||||
|
Those items were distributed across the three boards.
|
||||||
|
The outline now covers only: what Roject is, what exists now (condensed into
|
||||||
|
named cards), a one-card pointer to the boards, and the technical implementation
|
||||||
|
reference. About half the original length.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>Workspace index improved</h3>
|
||||||
|
<p>
|
||||||
|
<code>workspace/index.html</code> gained a Boards card and a
|
||||||
|
"Quick orientation" section with separate guidance for humans and agents:
|
||||||
|
read Outline → Guides → Reference before making changes; check Boards and
|
||||||
|
the most recent History entry for current context.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2>What's next</h2>
|
<h2>What's next</h2>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<p>
|
<p>
|
||||||
Local filesystem access — extend the file tree to browse arbitrary directories
|
See <a href="../../../../boards/tasks.html">Tasks board</a> for the current sprint.
|
||||||
on the host machine using Node.js <code>fs</code> rather than the server's
|
Local filesystem access and remote projects in Electron are the two active items.
|
||||||
JSON-backed project storage.
|
|
||||||
</p>
|
|
||||||
<p style="margin-top:0.75rem">
|
|
||||||
Remote projects in Electron — allow the Electron app to connect to a remote
|
|
||||||
Roject server (<code>roject.rokojori.com</code>) and list projects hosted
|
|
||||||
there alongside local ones. The JWT is already available; it's a matter of
|
|
||||||
pointing a request (or a BrowserView panel) at the remote URL with the token.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3><a href="2026/07-July/14-Monday/index.html">Monday, 14 July 2026</a></h3>
|
<h3><a href="2026/07-July/14-Monday/index.html">Monday, 14 July 2026</a></h3>
|
||||||
<p>Electron desktop app shell — login window, JWT auth via API, Authorization header injection, token persistence, ROJECT_ROOT path fix, ELECTRON_RUN_AS_NODE workaround.</p>
|
<p>Electron desktop app shell — login window, JWT auth via API, Authorization header injection, token persistence, ROJECT_ROOT path fix, ELECTRON_RUN_AS_NODE workaround. Workspace boards system (Tasks/Bugs/Backlog) with custom task-item elements; outline shortened; workspace index quick orientation added.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
|
|
|
||||||
|
|
@ -17,15 +17,24 @@
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
|
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3>Outline</h3>
|
<h3>Outline</h3>
|
||||||
<p>
|
<p>
|
||||||
Explains Roject's goals, features and future plans for the project.
|
What Roject is, what exists now, and the technical implementation.
|
||||||
|
Start here for the big picture.
|
||||||
<br>
|
<br>
|
||||||
|
<a href="./outline/index.html">Read the outline</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<a href="./outline/index.html">Read more about the outline</a>
|
<div class="card">
|
||||||
|
<h3>Boards</h3>
|
||||||
|
<p>
|
||||||
|
Current state at a glance: active tasks, known bugs, and longer-horizon backlog.
|
||||||
|
Check here to know what is being worked on right now.
|
||||||
|
<br>
|
||||||
|
<a href="./boards/index.html">Open the boards</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -34,8 +43,7 @@
|
||||||
<p>
|
<p>
|
||||||
Context, rules, workflows and examples for how to do things.
|
Context, rules, workflows and examples for how to do things.
|
||||||
<br>
|
<br>
|
||||||
|
<a href="./guides/index.html">Read the guides</a>
|
||||||
<a href="./guides/index.html">Read more about the guides</a>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -44,18 +52,16 @@
|
||||||
<p>
|
<p>
|
||||||
Named tasks and steps for recurring topics.
|
Named tasks and steps for recurring topics.
|
||||||
<br>
|
<br>
|
||||||
|
<a href="./actions/index.html">Read the actions</a>
|
||||||
<a href="./actions/index.html">Read more about the actions</a>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3>History</h3>
|
<h3>History</h3>
|
||||||
<p>
|
<p>
|
||||||
Per-day history that gives insights about the daily work.
|
Per-day session log. Good for understanding what changed and why.
|
||||||
<br>
|
<br>
|
||||||
|
<a href="./history/index.html">Read the history</a>
|
||||||
<a href="./history/index.html">Read more about the history</a>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -64,18 +70,27 @@
|
||||||
<p>
|
<p>
|
||||||
Technical reference for core systems: the Editor singleton, events, and the client/server compilation split.
|
Technical reference for core systems: the Editor singleton, events, and the client/server compilation split.
|
||||||
<br>
|
<br>
|
||||||
|
<a href="./reference/index.html">Read the reference</a>
|
||||||
<a href="./reference/index.html">Read more about the reference</a>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div>
|
|
||||||
To get the full picture, follow each page (as human or agent).
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Quick orientation</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<p>
|
||||||
|
<strong>New here?</strong> Read Outline for the big picture, then Guides for how things are done.
|
||||||
|
Check Boards for what is active right now.
|
||||||
|
</p>
|
||||||
|
<p style="margin-top: 0.75rem">
|
||||||
|
<strong>Agents:</strong> read Outline → Guides → Reference before making changes.
|
||||||
|
Check Boards and the most recent History entry to understand the current context.
|
||||||
|
Follow all coding conventions in the Guides — do not infer style from existing code alone.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
Roject documentation
|
Roject documentation
|
||||||
|
|
|
||||||
|
|
@ -22,317 +22,106 @@
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2>Project Outline</h2>
|
<h2>What it is</h2>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3>What it is</h3>
|
|
||||||
<p>
|
<p>
|
||||||
Roject is a self-hosted, agent-based IDE for working with files and projects.
|
Roject is a self-hosted, agent-based IDE for working with files and projects.
|
||||||
It provides specialised editors for different file types — a WYSIWYG HTML editor,
|
It provides specialised editors for different file types — a WYSIWYG HTML editor,
|
||||||
a CodeMirror-backed code editor, and more editors to come — all within a
|
a CodeMirror-backed code editor, and more to come — all within a multi-panel,
|
||||||
multi-panel, tab-based workspace. Projects can be hosted on the Roject server
|
tab-based workspace. Projects can be hosted on a Roject server (remote) or opened
|
||||||
(remote) or opened directly from the local filesystem (local), making it usable
|
directly from the local filesystem (local), making it usable both as a lightweight
|
||||||
both as a lightweight self-hosted CMS and as a full desktop development environment.
|
self-hosted CMS and as a full desktop development environment.
|
||||||
It is designed for individual developers or small teams and has no external database
|
Designed for individual developers or small teams; no external database dependency.
|
||||||
dependency.
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>What exists now</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>Auth & accounts</h3>
|
||||||
|
<p>
|
||||||
|
User accounts are managed by <strong>rokojori-auth</strong> at
|
||||||
|
<code>account.rokojori.com</code> — registration, login, JWT issuance, refresh
|
||||||
|
token rotation, roles, and account deletion. Roject verifies the shared
|
||||||
|
<code>accessToken</code> JWT cookie and transparently refreshes expired tokens
|
||||||
|
via <code>account.rokojori.com/api/auth/refresh-session</code>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3>What exists now</h3>
|
<h3>Groups, projects & storage</h3>
|
||||||
<p>
|
<p>
|
||||||
User accounts with registration, login, logout, and account deletion.
|
|
||||||
Groups and projects with member management (viewer / editor / admin roles).
|
Groups and projects with member management (viewer / editor / admin roles).
|
||||||
Each project gets a real directory on disk at <code>storage/<uuid>/root/</code>
|
Each project gets a real directory on disk at <code>storage/<uuid>/root/</code>
|
||||||
with a default <code>index.html</code> on creation.
|
with a default <code>index.html</code> on creation. All data lives as JSON files
|
||||||
|
in <code>data/</code> — no external database.
|
||||||
</p>
|
</p>
|
||||||
<p style="margin-top:0.75rem">
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>Editor</h3>
|
||||||
|
<p>
|
||||||
A full editor page (<code>/editor.html</code>) with a 3-panel resizable layout
|
A full editor page (<code>/editor.html</code>) with a 3-panel resizable layout
|
||||||
(Left / Center / Right). Each panel holds one or more sections side by side,
|
(Left / Center / Right). Each panel holds one or more sections side by side;
|
||||||
each section holds a <code><tab-container></code>. Tabs are draggable
|
each section holds a <code><tab-container></code> with drag-and-drop tabs.
|
||||||
between containers. The Left panel shows the file tree with create, rename,
|
The Left panel shows the file tree (create, rename, delete).
|
||||||
and delete for files and folders. The Center panel holds the WYSIWYG HTML
|
A <code>FileEditorRegistry</code> routes files to the correct panel by extension:
|
||||||
editor (iframe, <code>contenteditable</code>, MutationObserver, undo/redo,
|
HTML → <code>html-editor-panel</code> (iframe, contenteditable, MutationObserver,
|
||||||
Ctrl+S save). The Right panel is empty by default and receives dropped tabs.
|
undo/redo, Ctrl+S save); all other text formats → <code>code-panel</code>
|
||||||
</p>
|
(CodeMirror 5, syntax highlighting, dark theme). An optional project-level
|
||||||
<p style="margin-top:0.75rem">
|
<code>workspace/editor/file-editors.json</code> overrides the defaults.
|
||||||
A <code>FileEditorRegistry</code> routes files to the correct panel by suffix.
|
|
||||||
HTML files open in <code>html-editor-panel</code>; all other known text formats
|
|
||||||
open in <code>code-panel</code> (CodeMirror 5, syntax highlighting, dark theme,
|
|
||||||
Pin/Undo/Redo/Save toolbar). The registry checks an optional project-level
|
|
||||||
<code>workspace/editor/file-editors.json</code> first, then falls back to
|
|
||||||
in-memory defaults. Unknown extensions show an error in the file tree instead
|
|
||||||
of attempting to open.
|
|
||||||
</p>
|
|
||||||
<p style="margin-top:0.75rem">
|
|
||||||
A <code>rojo-chat-panel</code> provides a streaming AI chat interface backed
|
|
||||||
by a LangChain + OpenAI-compatible model. Each panel instance holds its own
|
|
||||||
conversation session in memory.
|
|
||||||
A reusable <code><confirm-dialog></code> component replaces browser
|
|
||||||
<code>confirm()</code> for destructive actions (currently project deletion).
|
|
||||||
An <code>EmailService</code> static facade (<code>source/server/email/</code>)
|
|
||||||
provides a single <code>sendEmail()</code> entry point backed by a swappable
|
|
||||||
<code>EmailSender</code> interface; the default implementation is
|
|
||||||
<code>SMTPEmailSender</code> (Nodemailer), configured via environment variables.
|
|
||||||
</p>
|
|
||||||
<p style="margin-top:0.75rem">
|
|
||||||
The app is deployed and publicly accessible at
|
|
||||||
<code>https://roject.rokojori.com</code> on Server A. nginx handles TLS
|
|
||||||
termination and reverse-proxies to the Node.js process managed by a
|
|
||||||
systemd service. Deployment is currently manual
|
|
||||||
(<code>git pull && npm run build && systemctl restart roject</code>);
|
|
||||||
CI automation is the remaining open item.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3>What still needs work</h3>
|
<h3>AI chat & utilities</h3>
|
||||||
<p>Items below are ordered by priority. Major planned features first, then smaller open improvements.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<h3>1 — Local filesystem access</h3>
|
|
||||||
<p>
|
<p>
|
||||||
The Electron shell is done (see below). The remaining work is extending the
|
A <code>rojo-chat-panel</code> provides a streaming AI chat interface backed by
|
||||||
file tree to browse arbitrary directories on the host machine using Node.js
|
LangChain + OpenAI-compatible models. A reusable <code><confirm-dialog></code>
|
||||||
<code>fs</code> directly, rather than being restricted to the
|
component replaces browser <code>confirm()</code> for destructive actions.
|
||||||
<code>storage/<uuid>/root/</code> paths managed by the server. This is
|
An <code>EmailService</code> facade (<code>source/server/email/</code>) provides
|
||||||
what turns Roject from a hosted CMS into something closer to VS Code — the
|
a <code>sendEmail()</code> entry point backed by a swappable <code>EmailSender</code>
|
||||||
user can open any folder on their machine as a project.
|
interface; the default is <code>SMTPEmailSender</code> (Nodemailer).
|
||||||
</p>
|
</p>
|
||||||
<div class="tags">
|
|
||||||
<span class="tag">Node.js fs</span>
|
|
||||||
<span class="tag">file tree extension</span>
|
|
||||||
<span class="tag">local mode</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3>2 — Remote projects in Electron</h3>
|
<h3>Electron desktop app</h3>
|
||||||
<p>
|
<p>
|
||||||
The Electron app currently runs a fully local Express server with its own
|
Roject runs as a standalone Windows desktop application. The Express server starts
|
||||||
data store — it shares the same identity as the web version (via rokojori-auth)
|
in-process inside Electron's main process. A custom login window calls the auth
|
||||||
but not the same projects. The next step is to allow the Electron app to also
|
API directly; tokens are stored in <code>userData/tokens.json</code> and re-used
|
||||||
connect to a remote Roject server (e.g. <code>roject.rokojori.com</code>) and
|
across sessions. All requests to <code>localhost</code> have
|
||||||
list, open, and edit projects hosted there, alongside any local filesystem
|
<code>Authorization: Bearer</code> injected automatically via
|
||||||
projects.
|
<code>session.webRequest.onBeforeSendHeaders</code>. Run with
|
||||||
|
<code>npm run electron:dev</code>.
|
||||||
</p>
|
</p>
|
||||||
<p style="margin-top:0.75rem">
|
|
||||||
The Electron app already holds a valid JWT and can send it as an
|
|
||||||
<code>Authorization: Bearer</code> header. Connecting to a remote server is
|
|
||||||
therefore a matter of pointing a second BrowserWindow (or a panel in the
|
|
||||||
existing window) at the remote URL and injecting the token — no new auth
|
|
||||||
work needed.
|
|
||||||
</p>
|
|
||||||
<div class="tags">
|
|
||||||
<span class="tag">remote Roject server</span>
|
|
||||||
<span class="tag">Authorization header</span>
|
|
||||||
<span class="tag">mixed local + remote</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3>3 — Local git repository integration</h3>
|
<h3>Deployment & CI</h3>
|
||||||
<p>
|
<p>
|
||||||
Git integration inside the editor: file status indicators in the tree, staging,
|
Live at <code>https://roject.rokojori.com</code> on Server A. nginx handles TLS
|
||||||
commit, push and pull, and eventually diffs and history. This depends on local
|
and reverse-proxies to a Node.js process managed by systemd. Auto-deploy on push
|
||||||
filesystem access (feature 2) and follows naturally from it — once Roject can
|
to <code>main</code> via a Gitea webhook calling <code>POST /api/deploy</code>
|
||||||
open an arbitrary local directory, the git repo that directory belongs to is
|
(HMAC-SHA256 verified). The deploy script runs detached so it survives the
|
||||||
already there.
|
<code>systemctl restart</code> that kills the parent process.
|
||||||
</p>
|
</p>
|
||||||
<p style="margin-top:0.75rem">
|
|
||||||
The implementation uses <code>simple-git</code>, a thin Node.js wrapper around
|
|
||||||
the git CLI, which keeps the dependency surface small and relies on the user's
|
|
||||||
existing git installation. An alternative is <code>isomorphic-git</code> (pure
|
|
||||||
JS, works in the browser too), but the CLI wrapper is simpler for a first
|
|
||||||
iteration. A new panel in the editor displays repo status and exposes the
|
|
||||||
common operations.
|
|
||||||
</p>
|
|
||||||
<div class="tags">
|
|
||||||
<span class="tag">simple-git</span>
|
|
||||||
<span class="tag">git CLI</span>
|
|
||||||
<span class="tag">new panel</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>What's next</h2>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3>4 — Internet tunnel / port pass-through relay</h3>
|
|
||||||
<p>
|
<p>
|
||||||
A tunneling feature that allows local devices — a main workstation running
|
Current tasks, known bugs, and longer-horizon features are tracked on the
|
||||||
Stable Diffusion, a local LLM, a GDScript language server, or any other
|
<a href="../boards/index.html">Boards</a>.
|
||||||
service — to be accessible to authorised Roject users over the internet,
|
|
||||||
routed through the Roject server.
|
|
||||||
</p>
|
</p>
|
||||||
<p style="margin-top:0.75rem">
|
|
||||||
The architecture: a small local agent (a Node.js script, or a built-in Roject
|
|
||||||
feature) connects to the Roject server via a persistent WebSocket, identifying
|
|
||||||
itself with a secret key. The server maps that key to a Roject user and
|
|
||||||
permission record. When an authorised Roject user (e.g. on their phone) makes
|
|
||||||
a request to the relay endpoint, the server forwards it through the WebSocket
|
|
||||||
to the local agent, which proxies it to the configured local port, and returns
|
|
||||||
the response the same way.
|
|
||||||
</p>
|
|
||||||
<p style="margin-top:0.75rem">
|
|
||||||
The primary use case is mobile-to-desktop: use a phone as a thin client while
|
|
||||||
the main machine handles all heavy processing (image generation, inference,
|
|
||||||
language server completions). The secret key is the only credential needed —
|
|
||||||
it is registered once in Roject's user system and then shared with the local
|
|
||||||
agent. A single pass-through maps one local port to one authorised user.
|
|
||||||
</p>
|
|
||||||
<p style="margin-top:0.75rem">
|
|
||||||
This feature is architecturally independent of Electron and mobile and lives
|
|
||||||
entirely on the server, so it can be developed in parallel with the desktop
|
|
||||||
work. It is placed here because its primary value is unlocked only once mobile
|
|
||||||
access (feature 5) also exists.
|
|
||||||
|
|
||||||
</p>
|
|
||||||
<div class="tags">
|
|
||||||
<span class="tag">WebSocket relay</span>
|
|
||||||
<span class="tag">HTTP proxy</span>
|
|
||||||
<span class="tag">secret key / auth</span>
|
|
||||||
<span class="tag">local agent</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<h3>5 — Mobile app (PWA first, native shell later)</h3>
|
|
||||||
<p>
|
|
||||||
Make Roject usable on a phone or tablet. The quickest path given the existing
|
|
||||||
web frontend is a Progressive Web App (PWA) — a manifest file and a service
|
|
||||||
worker. This costs almost nothing to add, works in Safari and Chrome on both
|
|
||||||
Android and iOS without an app store, and covers the core use case of reaching
|
|
||||||
the editor and the tunnel relay from a mobile browser.
|
|
||||||
</p>
|
|
||||||
<p style="margin-top:0.75rem">
|
|
||||||
If native capabilities are later needed (background processing, push
|
|
||||||
notifications, deeper OS integration), Capacitor can wrap the same web app
|
|
||||||
in a native shell without requiring a framework rewrite. A full React Native
|
|
||||||
or Flutter rewrite would be a significant departure from the existing Web
|
|
||||||
Components architecture and is not planned.
|
|
||||||
</p>
|
|
||||||
<p style="margin-top:0.75rem">
|
|
||||||
The main challenge of a mobile editing experience is the code editor — CodeMirror
|
|
||||||
on a touchscreen is not great for authoring. The realistic mobile workflow is
|
|
||||||
lighter interaction: browsing files, reading output, triggering generation
|
|
||||||
requests through the tunnel relay, and simple edits rather than heavy coding.
|
|
||||||
</p>
|
|
||||||
<div class="tags">
|
|
||||||
<span class="tag">PWA</span>
|
|
||||||
<span class="tag">manifest + service worker</span>
|
|
||||||
<span class="tag">Capacitor (later)</span>
|
|
||||||
<span class="tag">no React Native</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<h3>Done — Electron desktop app shell</h3>
|
|
||||||
<p>
|
|
||||||
Roject runs as a standalone desktop application on Windows. The Express server
|
|
||||||
starts in-process inside Electron's main process. A custom login window
|
|
||||||
(<code>electron/login.html</code>) collects credentials and calls
|
|
||||||
<code>POST https://account.rokojori.com/api/auth/login</code> directly from
|
|
||||||
the main process — no browser redirect, no cookie. Tokens are stored in
|
|
||||||
<code>userData/tokens.json</code> and re-used across sessions.
|
|
||||||
</p>
|
|
||||||
<p style="margin-top:0.75rem">
|
|
||||||
All HTTP requests from the BrowserWindow to <code>localhost</code> have
|
|
||||||
<code>Authorization: Bearer <accessToken></code> injected automatically
|
|
||||||
via <code>session.webRequest.onBeforeSendHeaders</code> — the frontend requires
|
|
||||||
zero changes. Expired tokens are refreshed via
|
|
||||||
<code>POST /api/auth/refresh</code> and the page is reloaded transparently.
|
|
||||||
Any navigation away from <code>localhost</code> is intercepted and redirected
|
|
||||||
to the Electron login window instead.
|
|
||||||
</p>
|
|
||||||
<p style="margin-top:0.75rem">
|
|
||||||
Known issue: <code>ELECTRON_RUN_AS_NODE=1</code> is set by VS Code / Claude
|
|
||||||
Code, which makes Electron behave as plain Node.js. The launcher script
|
|
||||||
(<code>scripts/launch-electron.js</code>) deletes this variable before
|
|
||||||
spawning the binary. Run with <code>npm run electron:dev</code> or
|
|
||||||
<code>node scripts/launch-electron.js</code> after the build.
|
|
||||||
</p>
|
|
||||||
<div class="tags">
|
|
||||||
<span class="tag">electron/main.ts</span>
|
|
||||||
<span class="tag">webRequest header injection</span>
|
|
||||||
<span class="tag">token persistence</span>
|
|
||||||
<span class="tag">ELECTRON_RUN_AS_NODE workaround</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<h3>Done — CI/CD pipeline</h3>
|
|
||||||
<p>
|
|
||||||
Auto-deploy on push to main via a Gitea webhook calling
|
|
||||||
<code>POST /api/deploy</code> on the Roject server. The endpoint verifies the
|
|
||||||
<code>X-Gitea-Signature</code> HMAC-SHA256 signature, checks the branch is
|
|
||||||
<code>main</code>, responds immediately, then spawns a detached bash process
|
|
||||||
that runs <code>git pull && npm run build && systemctl restart roject</code>.
|
|
||||||
The detached process survives the <code>systemctl restart</code> that kills the parent.
|
|
||||||
</p>
|
|
||||||
<div class="tags">
|
|
||||||
<span class="tag">Gitea webhook</span>
|
|
||||||
<span class="tag">HMAC-SHA256</span>
|
|
||||||
<span class="tag">detached spawn</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<h3>Done — Centralized auth: rokojori-auth</h3>
|
|
||||||
<p>
|
|
||||||
The standalone auth service <strong>rokojori-auth</strong> is built and live at
|
|
||||||
<code>account.rokojori.com</code>. It handles registration, login, JWT issuance,
|
|
||||||
refresh token rotation, password reset, roles (user / admin / superadmin),
|
|
||||||
products, global settings, rate limiting, and account deletion.
|
|
||||||
Roject has not yet been integrated — that is the next step.
|
|
||||||
</p>
|
|
||||||
<p style="margin-top:0.75rem">
|
|
||||||
See <a href="auth-restructure.html">auth-restructure</a> for the full plan and
|
|
||||||
the rokojori-auth workspace for implementation details.
|
|
||||||
</p>
|
|
||||||
<div class="tags">
|
|
||||||
<span class="tag">rokojori-auth</span>
|
|
||||||
<span class="tag">account.rokojori.com</span>
|
|
||||||
<span class="tag">JWT</span>
|
|
||||||
<span class="tag">centralized identity</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<h3>Done — Roject integration with rokojori-auth</h3>
|
|
||||||
<p>
|
|
||||||
Roject is now a JWT-validating client. The local session-based user system has
|
|
||||||
been removed and replaced with a JWT verification middleware that reads the shared
|
|
||||||
<code>accessToken</code> cookie on <code>.rokojori.com</code>. Expired tokens are
|
|
||||||
transparently refreshed via
|
|
||||||
<code>account.rokojori.com/api/auth/refresh-session?redirect=...</code>.
|
|
||||||
Login and logout links in <code>app-nav</code> point to
|
|
||||||
<code>account.rokojori.com</code>. All data references use <code>userId</code>
|
|
||||||
from the JWT payload. A <code>GET /api/auth/logout?redirect=...</code> endpoint
|
|
||||||
was also added to rokojori-auth to support browser-based logout links.
|
|
||||||
</p>
|
|
||||||
<div class="tags">
|
|
||||||
<span class="tag">JWT middleware</span>
|
|
||||||
<span class="tag">cookie-parser</span>
|
|
||||||
<span class="tag">jsonwebtoken</span>
|
|
||||||
<span class="tag">account.rokojori.com</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<h3>Smaller open improvements</h3>
|
|
||||||
<ul style="line-height:1.9">
|
|
||||||
<li>Unauthenticated users should land on a landing screen that explains the app and shows a login link, rather than crashing on dashboard components.</li>
|
|
||||||
<li>Components that fetch data (groups, projects, etc.) do not handle <code>401</code> responses gracefully — they crash when the API returns an error object instead of an array.</li>
|
|
||||||
<li>The Right panel has no default content and relies on manual tab dragging to populate.</li>
|
|
||||||
<li>Portrait mode's secondary section switcher (when a panel has multiple side-by-side sections) is not yet wired up.</li>
|
|
||||||
<li>The member list UI shows raw UUIDs instead of usernames.</li>
|
|
||||||
<li>The group editor and account delete button still use the browser <code>confirm()</code> instead of the custom <code><confirm-dialog></code>.</li>
|
|
||||||
<li>Non-text files (images, PDFs) are visible in the tree but not openable — a <code>MediaViewerPanel</code> is planned.</li>
|
|
||||||
<li>No real-time multi-user collaboration yet.</li>
|
|
||||||
<li>CI deploy endpoint (<code>/api/deploy</code>) should send an email notification after each restart so deploys are visible without checking logs.</li>
|
|
||||||
<li>The Gitea webhook currently triggers on pushes to <code>main</code> — switch to a <code>dev</code> branch so every commit doesn't redeploy.</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
|
|
@ -342,13 +131,10 @@
|
||||||
<h3>Backend</h3>
|
<h3>Backend</h3>
|
||||||
<p>
|
<p>
|
||||||
Node.js + Express, TypeScript compiled on the fly with <code>ts-node</code>.
|
Node.js + Express, TypeScript compiled on the fly with <code>ts-node</code>.
|
||||||
No database — all data lives as JSON files in <code>data/</code>
|
Auth is handled by <code>rokojori-auth</code>; Roject verifies the shared
|
||||||
(auto-created on first run). Auth is handled by <code>rokojori-auth</code>
|
|
||||||
at <code>account.rokojori.com</code>; Roject verifies the shared
|
|
||||||
<code>accessToken</code> JWT cookie using <code>jsonwebtoken</code> +
|
<code>accessToken</code> JWT cookie using <code>jsonwebtoken</code> +
|
||||||
<code>cookie-parser</code>. All entity IDs are UUIDs via
|
<code>cookie-parser</code>. All entity IDs are UUIDs via
|
||||||
<code>crypto.randomUUID()</code> — no central counter, safe for parallel
|
<code>crypto.randomUUID()</code>. Start the server with <code>npm start</code>.
|
||||||
instances. Start the server with <code>npm start</code>.
|
|
||||||
</p>
|
</p>
|
||||||
<div class="tags">
|
<div class="tags">
|
||||||
<span class="tag">Node.js</span>
|
<span class="tag">Node.js</span>
|
||||||
|
|
@ -364,13 +150,12 @@
|
||||||
<h3>Frontend — Editor Singleton & Client/Server Split</h3>
|
<h3>Frontend — Editor Singleton & Client/Server Split</h3>
|
||||||
<p>
|
<p>
|
||||||
The <code>Editor</code> singleton (<code>src/editor/Editor.ts</code>) is the
|
The <code>Editor</code> singleton (<code>src/editor/Editor.ts</code>) is the
|
||||||
central hub of the frontend editor — it owns all open document state, the
|
central hub of the frontend — it owns all open document state, the
|
||||||
<code>FileEditorRegistry</code>, and the five events that panels and the tab
|
<code>FileEditorRegistry</code>, and the five events that panels and tab containers
|
||||||
container subscribe to (<code>onDocumentOpened</code>,
|
subscribe to (<code>onDocumentOpened</code>, <code>onDocumentDirty</code>,
|
||||||
<code>onDocumentDirty</code>, <code>onDocumentSaved</code>,
|
<code>onDocumentSaved</code>, <code>onFilesChanged</code>,
|
||||||
<code>onFilesChanged</code>, <code>onFileTypeUnknown</code>). For the full
|
<code>onFileTypeUnknown</code>). For the full event reference and the TypeScript
|
||||||
event reference and the TypeScript compilation split between client and server,
|
compilation split between client and server, see the
|
||||||
see the
|
|
||||||
<a href="../reference/editor-singleton/index.html">Editor Singleton reference</a>.
|
<a href="../reference/editor-singleton/index.html">Editor Singleton reference</a>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -383,13 +168,9 @@
|
||||||
<code>source/components/<name>/</code>. CSS uses the element tag as root
|
<code>source/components/<name>/</code>. CSS uses the element tag as root
|
||||||
selector with <code>display: block</code>. TypeScript compiles to
|
selector with <code>display: block</code>. TypeScript compiles to
|
||||||
<code>build/app/components/</code> via <code>tsconfig.client.json</code>
|
<code>build/app/components/</code> via <code>tsconfig.client.json</code>
|
||||||
(<code>module: ESNext</code>, <code>moduleResolution: bundler</code>, no
|
(<code>module: ESNext</code>, no bundler). HTML pages live in
|
||||||
bundler). HTML pages live in <code>source/pages/</code> and are copied to
|
<code>source/pages/</code> and are copied to <code>build/app/</code> by
|
||||||
<code>build/app/</code> by <code>scripts/copy-pages.js</code> as part of the
|
<code>scripts/copy-pages.js</code>. Build with <code>npm run build</code>.
|
||||||
build. HTML pages load components with
|
|
||||||
<code><script type="module"></code>. Shared state uses a module-level
|
|
||||||
singleton (<code>editor-state.ts</code>) rather than globals.
|
|
||||||
Build with <code>npm run build</code>.
|
|
||||||
</p>
|
</p>
|
||||||
<div class="tags">
|
<div class="tags">
|
||||||
<span class="tag">Web Components</span>
|
<span class="tag">Web Components</span>
|
||||||
|
|
@ -406,10 +187,9 @@
|
||||||
A personal TypeScript library lives as a git submodule at
|
A personal TypeScript library lives as a git submodule at
|
||||||
<code>source/library-ts/</code>. It has two parts: <code>browser/</code>
|
<code>source/library-ts/</code>. It has two parts: <code>browser/</code>
|
||||||
(DOM-capable) and <code>node/</code> (Node.js only). The browser part is
|
(DOM-capable) and <code>node/</code> (Node.js only). The browser part is
|
||||||
compiled separately via TypeScript project references
|
compiled separately via TypeScript project references into
|
||||||
(<code>source/library-ts/browser/tsconfig.roject.json</code>, <code>strict: false</code>)
|
<code>build/app/library-ts/browser/</code>. The node part is included by
|
||||||
into <code>build/app/library-ts/browser/</code>. The node part is included by
|
<code>tsconfig.ts-node.json</code>.
|
||||||
<code>tsconfig.ts-node.json</code> (extends server config, <code>strictNullChecks: false</code>).
|
|
||||||
</p>
|
</p>
|
||||||
<div class="tags">
|
<div class="tags">
|
||||||
<span class="tag">git submodule</span>
|
<span class="tag">git submodule</span>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue