feat(forge): 3D-printing/engineering hub page (Manyfold + Spoolman/OctoPrint cards), sidebar Apps entry
This commit is contained in:
@@ -31,6 +31,7 @@ const VIEWS = {
|
|||||||
obd2: () => import('./views/obd2.js'),
|
obd2: () => import('./views/obd2.js'),
|
||||||
links: () => import('./views/links.js'),
|
links: () => import('./views/links.js'),
|
||||||
mirror: () => import('./views/mirror.js'),
|
mirror: () => import('./views/mirror.js'),
|
||||||
|
forge: () => import('./views/forge.js'),
|
||||||
settings: () => import('./views/settings.js'),
|
settings: () => import('./views/settings.js'),
|
||||||
jobs: () => import('./views/jobs.js'),
|
jobs: () => import('./views/jobs.js'),
|
||||||
speedtest: () => import('./views/speedtest.js')
|
speedtest: () => import('./views/speedtest.js')
|
||||||
|
|||||||
@@ -135,7 +135,8 @@ export function renderSidebar(root) {
|
|||||||
navItem('AI Usage', '/ai-usage'),
|
navItem('AI Usage', '/ai-usage'),
|
||||||
navItem('OBD2', '/obd2'),
|
navItem('OBD2', '/obd2'),
|
||||||
navItem('Links', '/links'),
|
navItem('Links', '/links'),
|
||||||
navItem('MagicMirror', '/mirror')
|
navItem('MagicMirror', '/mirror'),
|
||||||
|
navItem('Forge', '/forge')
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ const ROUTES = [
|
|||||||
{ name: 'obd2', re: /^\/obd2$/, keys: [] },
|
{ name: 'obd2', re: /^\/obd2$/, keys: [] },
|
||||||
{ name: 'links', re: /^\/links$/, keys: [] },
|
{ name: 'links', re: /^\/links$/, keys: [] },
|
||||||
{ name: 'mirror', re: /^\/mirror$/, keys: [] },
|
{ name: 'mirror', re: /^\/mirror$/, keys: [] },
|
||||||
|
{ name: 'forge', re: /^\/forge$/, keys: [] },
|
||||||
{ name: 'settings', re: /^\/settings$/, keys: [] },
|
{ name: 'settings', re: /^\/settings$/, keys: [] },
|
||||||
{ name: 'jobs', re: /^\/jobs$/, keys: [] },
|
{ name: 'jobs', re: /^\/jobs$/, keys: [] },
|
||||||
{ name: 'speedtest', re: /^\/speedtest$/, keys: [] },
|
{ name: 'speedtest', re: /^\/speedtest$/, keys: [] },
|
||||||
|
|||||||
49
public/views/forge.js
Normal file
49
public/views/forge.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// Forge — 3D printing, modelling & engineering hub. Links out to the self-hosted
|
||||||
|
// tools (Manyfold today; OctoPrint / Spoolman as the workshop grows).
|
||||||
|
import { el, mount, safeHref } from '../dom.js';
|
||||||
|
|
||||||
|
function card({ name, status, blurb, href, accent }) {
|
||||||
|
const live = status === 'Live';
|
||||||
|
return el('a', {
|
||||||
|
href: href ? safeHref(href) : '#',
|
||||||
|
target: href ? '_blank' : null,
|
||||||
|
rel: href ? 'noopener noreferrer' : null,
|
||||||
|
style: {
|
||||||
|
display: 'block', textDecoration: 'none', color: 'inherit',
|
||||||
|
border: '1px solid var(--border)', borderRadius: '8px', padding: '1rem 1.1rem',
|
||||||
|
background: 'var(--panel, var(--bg2, #1a1a22))', opacity: live ? '1' : '0.7',
|
||||||
|
cursor: href ? 'pointer' : 'default'
|
||||||
|
},
|
||||||
|
onclick: href ? null : (e) => e.preventDefault()
|
||||||
|
},
|
||||||
|
el('div', { style: { display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: '0.5rem' } },
|
||||||
|
el('strong', { style: { fontSize: '1.02rem' } }, name),
|
||||||
|
el('span', { style: {
|
||||||
|
fontSize: '0.65rem', letterSpacing: '0.08em', textTransform: 'uppercase',
|
||||||
|
color: live ? (accent || 'var(--accent, #ff7a45)') : 'var(--muted, #8a8a99)'
|
||||||
|
} }, status)),
|
||||||
|
el('p', { style: { margin: '0.4rem 0 0', fontSize: '0.85rem', color: 'var(--muted, #9a9aa8)', lineHeight: '1.4' } }, blurb),
|
||||||
|
href ? el('span', { style: { fontSize: '0.78rem', color: 'var(--accent, #ff7a45)' } }, '→ open') : null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function render(main) {
|
||||||
|
mount(main,
|
||||||
|
el('h1', { class: 'view-h1' }, 'Forge'),
|
||||||
|
el('p', { class: 'view-sub' }, '3D printing, modelling & engineering projects.'),
|
||||||
|
el('div', { style: {
|
||||||
|
display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(260px, 1fr))',
|
||||||
|
gap: '0.9rem', marginTop: '1rem'
|
||||||
|
} },
|
||||||
|
card({ name: 'Manyfold', status: 'Live',
|
||||||
|
blurb: 'Your 3D model & file library — store, tag and organise STL/3MF/Chitubox projects; import straight from Printables, Thingiverse & MyMiniFactory.',
|
||||||
|
href: 'https://forge.hynesy.com' }),
|
||||||
|
card({ name: 'Spoolman', status: 'Recommended',
|
||||||
|
blurb: 'Self-hosted resin/filament inventory — track bottles, usage and cost. The natural next add for the resin workflow.' }),
|
||||||
|
card({ name: 'OctoPrint', status: 'Planned',
|
||||||
|
blurb: 'FDM printer control & monitoring. For a future filament printer — the resin Mars 3 Pro prints standalone from USB via Chitubox, so this is parked until there is an FDM machine.' })
|
||||||
|
),
|
||||||
|
el('p', { style: { marginTop: '1.2rem', fontSize: '0.8rem', color: 'var(--muted, #8a8a99)' } },
|
||||||
|
'Forge grows with the workshop — more 3D-printing, modelling and engineering tools land here as they are stood up.')
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user