refactor: separate notebooks content from server config
Some checks failed
CI / skinny-install (aco) (push) Successful in 52s
CI / skinny-install (api) (push) Successful in 31s
CI / skinny-install (bcda) (push) Successful in 27s
CI / skinny-install (bib) (push) Successful in 27s
CI / skinny-install (bls) (push) Successful in 21s
CI / skinny-install (ccw) (push) Successful in 22s
CI / skinny-install (conf) (push) Successful in 25s
CI / skinny-install (cli) (push) Successful in 48s
CI / skinny-install (cms) (push) Successful in 25s
CI / skinny-install (pfs) (push) Successful in 24s
CI / skinny-install (rex) (push) Successful in 23s
CI / lint-test (push) Successful in 6m1s
Infra CI / notebooks (push) Successful in 6s
Infra CI / zotero (push) Successful in 6s
Infra CI / docs (push) Successful in 13s
Infra CI / api (push) Successful in 9s
Infra CI / mc (push) Successful in 6s
Package Supply Chain / pkg-supply-chain (push) Failing after 27s
Deploy / build-scan-report (push) Successful in 3m59s
Some checks failed
CI / skinny-install (aco) (push) Successful in 52s
CI / skinny-install (api) (push) Successful in 31s
CI / skinny-install (bcda) (push) Successful in 27s
CI / skinny-install (bib) (push) Successful in 27s
CI / skinny-install (bls) (push) Successful in 21s
CI / skinny-install (ccw) (push) Successful in 22s
CI / skinny-install (conf) (push) Successful in 25s
CI / skinny-install (cli) (push) Successful in 48s
CI / skinny-install (cms) (push) Successful in 25s
CI / skinny-install (pfs) (push) Successful in 24s
CI / skinny-install (rex) (push) Successful in 23s
CI / lint-test (push) Successful in 6m1s
Infra CI / notebooks (push) Successful in 6s
Infra CI / zotero (push) Successful in 6s
Infra CI / docs (push) Successful in 13s
Infra CI / api (push) Successful in 9s
Infra CI / mc (push) Successful in 6s
Package Supply Chain / pkg-supply-chain (push) Failing after 27s
Deploy / build-scan-report (push) Successful in 3m59s
Move marimo server config, snippets, and patched assets out of notebooks/ into infra/marimo/, keeping notebooks/ as pure .py content. - infra/marimo/marimo.toml: merged authoritative config (was split across .marimo.toml and .marimo-config/marimo.toml) - infra/marimo/snippets/: 10 code templates (was notebooks/snippets/) - infra/marimo/home-page-patched.js: custom home page (was in notebooks/) - Deleted duplicate loch.css (already served from assets/css/marimo.css) - Updated compose.yml volume mounts, Dockerfile, stack.toml path_filter - 18 layout tests enforce separation going forward Closes #214, closes #215, closes #216, closes #217, closes #218
This commit is contained in:
@@ -7,6 +7,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- 'notebooks/**'
|
- 'notebooks/**'
|
||||||
|
- 'infra/marimo/**'
|
||||||
- 'infra/images/notebooks.Dockerfile'
|
- 'infra/images/notebooks.Dockerfile'
|
||||||
- 'data/zotero/**'
|
- 'data/zotero/**'
|
||||||
- 'infra/images/zotero.Dockerfile'
|
- 'infra/images/zotero.Dockerfile'
|
||||||
@@ -20,6 +21,7 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- 'notebooks/**'
|
- 'notebooks/**'
|
||||||
|
- 'infra/marimo/**'
|
||||||
- 'infra/images/notebooks.Dockerfile'
|
- 'infra/images/notebooks.Dockerfile'
|
||||||
- 'data/zotero/**'
|
- 'data/zotero/**'
|
||||||
- 'infra/images/zotero.Dockerfile'
|
- 'infra/images/zotero.Dockerfile'
|
||||||
|
|||||||
@@ -46,10 +46,10 @@ RUN uv python install ${PYTHON_VERSION} \
|
|||||||
vega_datasets pyzotero obstore s3fs \
|
vega_datasets pyzotero obstore s3fs \
|
||||||
--extra-index-url https://pypi.nvidia.com
|
--extra-index-url https://pypi.nvidia.com
|
||||||
|
|
||||||
# Create marimo config directory and config
|
# Create marimo config directory with minimal defaults.
|
||||||
|
# Full config (marimo.toml, snippets, loch.css) is mounted at runtime
|
||||||
|
# from infra/marimo/ via compose.yml volumes.
|
||||||
RUN mkdir -p /home/${USERNAME}/.config/marimo
|
RUN mkdir -p /home/${USERNAME}/.config/marimo
|
||||||
COPY --chown=${USER_UID}:${USER_GID} .marimo.toml /home/${USERNAME}/.config/marimo/marimo.toml
|
|
||||||
COPY --chown=${USER_UID}:${USER_GID} .marimo-config/loch.css /home/${USERNAME}/.config/marimo/loch.css
|
|
||||||
|
|
||||||
EXPOSE 2718
|
EXPOSE 2718
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ preset = "default"
|
|||||||
[keymap.overrides]
|
[keymap.overrides]
|
||||||
|
|
||||||
[snippets]
|
[snippets]
|
||||||
custom_paths = ["/home/kert/notebooks/snippets"]
|
custom_paths = ["/home/kert/.config/marimo/snippets"]
|
||||||
include_default_snippets = false
|
include_default_snippets = false
|
||||||
|
|
||||||
[mcp]
|
[mcp]
|
||||||
@@ -1,618 +0,0 @@
|
|||||||
/* LOCH — deep-navy design system */
|
|
||||||
/* Uses marimo's native CSS variable system for proper theming */
|
|
||||||
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&display=swap');
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
FONT CONFIGURATION
|
|
||||||
============================================ */
|
|
||||||
:root {
|
|
||||||
--marimo-monospace-font: 'IBM Plex Mono', 'Courier New', monospace;
|
|
||||||
--marimo-text-font: 'IBM Plex Mono', 'Courier New', monospace;
|
|
||||||
--marimo-heading-font: 'IBM Plex Mono', monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
LOCH COLOR PALETTE
|
|
||||||
============================================ */
|
|
||||||
:root {
|
|
||||||
--ds-navy: #0a1628;
|
|
||||||
--ds-navy-light: #162040;
|
|
||||||
--ds-navy-mid: #1e2d50;
|
|
||||||
--ds-slate: #2a3f5f;
|
|
||||||
--ds-steel: #8a9fbb;
|
|
||||||
--ds-white: #f0f0f0;
|
|
||||||
|
|
||||||
--ds-blue: #4477dd;
|
|
||||||
--ds-blue-light: #6699ee;
|
|
||||||
--ds-blue-bright: #99bbff;
|
|
||||||
|
|
||||||
--ds-red: #ee5577;
|
|
||||||
--ds-red-light: #ee6688;
|
|
||||||
|
|
||||||
--ds-green: #44aa44;
|
|
||||||
--ds-green-light: #77dd77;
|
|
||||||
--ds-yellow: #eebb44;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
MARIMO THEME VARIABLES (Dark Mode)
|
|
||||||
============================================ */
|
|
||||||
:root {
|
|
||||||
/* Base colors */
|
|
||||||
--background: var(--ds-navy);
|
|
||||||
--foreground: var(--ds-white);
|
|
||||||
|
|
||||||
/* Muted/secondary text */
|
|
||||||
--muted: var(--ds-navy-light);
|
|
||||||
--muted-foreground: var(--ds-steel);
|
|
||||||
|
|
||||||
/* Popover/dropdown backgrounds */
|
|
||||||
--popover: var(--ds-navy-light);
|
|
||||||
--popover-foreground: var(--ds-white);
|
|
||||||
|
|
||||||
/* Cards (cells) */
|
|
||||||
--card: var(--ds-navy);
|
|
||||||
--card-foreground: var(--ds-white);
|
|
||||||
|
|
||||||
/* Borders and inputs */
|
|
||||||
--border: var(--ds-slate);
|
|
||||||
--input: var(--ds-navy-mid);
|
|
||||||
|
|
||||||
/* Primary action color */
|
|
||||||
--primary: var(--ds-blue);
|
|
||||||
--primary-foreground: var(--ds-white);
|
|
||||||
|
|
||||||
/* Secondary elements */
|
|
||||||
--secondary: var(--ds-slate);
|
|
||||||
--secondary-foreground: var(--ds-white);
|
|
||||||
|
|
||||||
/* Accent/highlight */
|
|
||||||
--accent: var(--ds-blue-light);
|
|
||||||
--accent-foreground: var(--ds-white);
|
|
||||||
|
|
||||||
/* Focus ring */
|
|
||||||
--ring: var(--ds-blue-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
SEMANTIC COLORS
|
|
||||||
============================================ */
|
|
||||||
:root {
|
|
||||||
/* Error states */
|
|
||||||
--destructive: var(--ds-red);
|
|
||||||
--destructive-foreground: var(--ds-white);
|
|
||||||
--error: var(--ds-red);
|
|
||||||
--error-foreground: var(--ds-white);
|
|
||||||
|
|
||||||
/* Success states */
|
|
||||||
--success: var(--ds-green);
|
|
||||||
--success-foreground: var(--ds-white);
|
|
||||||
|
|
||||||
/* Action buttons (run, etc) */
|
|
||||||
--action: var(--ds-green);
|
|
||||||
--action-hover: var(--ds-green-light);
|
|
||||||
--action-foreground: var(--ds-white);
|
|
||||||
|
|
||||||
/* Links */
|
|
||||||
--link: var(--ds-blue-bright);
|
|
||||||
--link-visited: var(--ds-blue-light);
|
|
||||||
|
|
||||||
/* Stale indicator */
|
|
||||||
--stale: rgba(221, 170, 51, 0.25);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
SHADOWS
|
|
||||||
============================================ */
|
|
||||||
:root {
|
|
||||||
--base-shadow: rgba(0, 0, 0, 0.4);
|
|
||||||
--base-shadow-darker: rgba(0, 0, 0, 0.6);
|
|
||||||
--base-shadow-opacity: 20%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
BACKGROUND
|
|
||||||
============================================ */
|
|
||||||
:root {
|
|
||||||
--background-image: none;
|
|
||||||
--background-repeat: no-repeat;
|
|
||||||
--background-attachment: fixed;
|
|
||||||
--background-size: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background: var(--ds-navy) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
CODE EDITOR - CODEMIRROR STYLING
|
|
||||||
============================================ */
|
|
||||||
.cm-editor {
|
|
||||||
font-family: var(--marimo-monospace-font) !important;
|
|
||||||
font-size: 14px !important;
|
|
||||||
background: var(--ds-navy) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-content {
|
|
||||||
font-family: var(--marimo-monospace-font) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-line {
|
|
||||||
font-family: var(--marimo-monospace-font) !important;
|
|
||||||
color: var(--ds-white) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-gutters {
|
|
||||||
background: var(--ds-navy-light) !important;
|
|
||||||
border-right: 2px solid var(--ds-slate) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cm-lineNumbers .cm-gutterElement {
|
|
||||||
color: var(--ds-steel) !important;
|
|
||||||
font-family: var(--marimo-monospace-font) !important;
|
|
||||||
font-size: 12px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Syntax highlighting */
|
|
||||||
.cm-keyword, .tok-keyword { color: var(--ds-blue-bright) !important; }
|
|
||||||
.cm-string, .tok-string { color: var(--ds-green-light) !important; }
|
|
||||||
.cm-number, .tok-number { color: var(--ds-yellow) !important; }
|
|
||||||
.cm-comment, .tok-comment { color: var(--ds-steel) !important; font-style: italic !important; }
|
|
||||||
.cm-function, .tok-function { color: var(--ds-white) !important; }
|
|
||||||
.cm-variableName, .tok-variableName { color: var(--ds-white) !important; }
|
|
||||||
.cm-operator, .tok-operator { color: var(--ds-blue-light) !important; }
|
|
||||||
.cm-propertyName, .tok-propertyName { color: var(--ds-blue-bright) !important; }
|
|
||||||
.tok-standard, .cm-builtin { color: var(--ds-red-light) !important; }
|
|
||||||
.tok-definition, .cm-def { color: var(--ds-blue-bright) !important; }
|
|
||||||
.tok-typeName { color: var(--ds-yellow) !important; }
|
|
||||||
.tok-bool { color: var(--ds-red-light) !important; }
|
|
||||||
|
|
||||||
/* Cursor */
|
|
||||||
.cm-cursor {
|
|
||||||
border-left: 2px solid var(--ds-white) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Selection */
|
|
||||||
.cm-selectionBackground,
|
|
||||||
::selection {
|
|
||||||
background: var(--ds-blue) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Active line */
|
|
||||||
.cm-activeLine {
|
|
||||||
background: rgba(51, 102, 204, 0.15) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
HEADINGS
|
|
||||||
============================================ */
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
|
||||||
font-family: var(--marimo-heading-font) !important;
|
|
||||||
font-weight: 600 !important;
|
|
||||||
letter-spacing: 0.5px !important;
|
|
||||||
line-height: 1.8 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 18px !important;
|
|
||||||
color: var(--ds-white) !important;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
font-size: 14px !important;
|
|
||||||
color: var(--ds-blue-bright) !important;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
font-size: 12px !important;
|
|
||||||
color: var(--ds-white) !important;
|
|
||||||
}
|
|
||||||
h4, h5, h6 {
|
|
||||||
font-size: 11px !important;
|
|
||||||
color: var(--ds-white) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
TABLES (Table styling)
|
|
||||||
============================================ */
|
|
||||||
table {
|
|
||||||
border-collapse: collapse !important;
|
|
||||||
font-family: var(--marimo-monospace-font) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
background: var(--ds-blue) !important;
|
|
||||||
color: var(--ds-white) !important;
|
|
||||||
font-family: var(--marimo-heading-font) !important;
|
|
||||||
font-size: 10px !important;
|
|
||||||
padding: 10px 12px !important;
|
|
||||||
text-transform: uppercase !important;
|
|
||||||
letter-spacing: 1px !important;
|
|
||||||
border: 1px solid var(--ds-blue-light) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
background: var(--ds-navy) !important;
|
|
||||||
color: var(--ds-white) !important;
|
|
||||||
padding: 8px 12px !important;
|
|
||||||
border: 1px solid var(--ds-navy-light) !important;
|
|
||||||
font-size: 13px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr:nth-child(even) td {
|
|
||||||
background: var(--ds-navy-light) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr:hover td {
|
|
||||||
background: rgba(51, 102, 204, 0.2) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
BUTTONS
|
|
||||||
============================================ */
|
|
||||||
button,
|
|
||||||
[role="button"],
|
|
||||||
.btn {
|
|
||||||
font-family: var(--marimo-monospace-font) !important;
|
|
||||||
font-weight: 500 !important;
|
|
||||||
border-radius: 4px !important;
|
|
||||||
transition: all 0.15s ease !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Primary/action buttons should be visible */
|
|
||||||
button[data-variant="primary"],
|
|
||||||
button.primary,
|
|
||||||
[data-action="run"] {
|
|
||||||
background: var(--ds-green) !important;
|
|
||||||
color: var(--ds-white) !important;
|
|
||||||
border: 2px solid var(--ds-green-light) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
button[data-variant="primary"]:hover,
|
|
||||||
button.primary:hover,
|
|
||||||
[data-action="run"]:hover {
|
|
||||||
background: var(--ds-green-light) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
INPUTS AND FORMS
|
|
||||||
============================================ */
|
|
||||||
input,
|
|
||||||
textarea,
|
|
||||||
select {
|
|
||||||
font-family: var(--marimo-monospace-font) !important;
|
|
||||||
background: var(--ds-navy-mid) !important;
|
|
||||||
color: var(--ds-white) !important;
|
|
||||||
border: 2px solid var(--ds-slate) !important;
|
|
||||||
border-radius: 4px !important;
|
|
||||||
padding: 8px 12px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:focus,
|
|
||||||
textarea:focus,
|
|
||||||
select:focus {
|
|
||||||
border-color: var(--ds-blue-light) !important;
|
|
||||||
outline: none !important;
|
|
||||||
box-shadow: 0 0 0 2px rgba(85, 136, 221, 0.3) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
CODE BLOCKS (in markdown)
|
|
||||||
============================================ */
|
|
||||||
pre, code {
|
|
||||||
font-family: var(--marimo-monospace-font) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
background: var(--ds-navy-light) !important;
|
|
||||||
color: var(--ds-blue-bright) !important;
|
|
||||||
padding: 2px 6px !important;
|
|
||||||
border-radius: 3px !important;
|
|
||||||
font-size: 0.9em !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
background: var(--ds-navy) !important;
|
|
||||||
border: 2px solid var(--ds-slate) !important;
|
|
||||||
border-radius: 4px !important;
|
|
||||||
padding: 12px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre code {
|
|
||||||
background: transparent !important;
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
LINKS
|
|
||||||
============================================ */
|
|
||||||
a {
|
|
||||||
color: var(--ds-blue-bright) !important;
|
|
||||||
text-decoration: none !important;
|
|
||||||
transition: color 0.15s ease !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: var(--ds-white) !important;
|
|
||||||
text-decoration: underline !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:visited {
|
|
||||||
color: var(--ds-blue-light) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
SCROLLBARS
|
|
||||||
============================================ */
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
width: 12px;
|
|
||||||
height: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
|
||||||
background: var(--ds-navy-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
|
||||||
background: var(--ds-slate);
|
|
||||||
border-radius: 6px;
|
|
||||||
border: 2px solid var(--ds-navy-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb:hover {
|
|
||||||
background: var(--ds-steel);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
MISC UI REFINEMENTS
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
/* Tooltips */
|
|
||||||
[role="tooltip"],
|
|
||||||
[data-state="open"][data-side] {
|
|
||||||
font-family: var(--marimo-monospace-font) !important;
|
|
||||||
font-size: 12px !important;
|
|
||||||
background: var(--ds-navy-light) !important;
|
|
||||||
color: var(--ds-white) !important;
|
|
||||||
border: 2px solid var(--ds-slate) !important;
|
|
||||||
border-radius: 4px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dropdown menus */
|
|
||||||
[role="menu"],
|
|
||||||
[role="listbox"] {
|
|
||||||
background: var(--ds-navy-light) !important;
|
|
||||||
border: 2px solid var(--ds-slate) !important;
|
|
||||||
border-radius: 4px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
[role="menuitem"]:hover,
|
|
||||||
[role="option"]:hover,
|
|
||||||
[role="menuitem"][data-highlighted],
|
|
||||||
[role="option"][data-highlighted] {
|
|
||||||
background: var(--ds-blue) !important;
|
|
||||||
color: var(--ds-white) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Badges and pills */
|
|
||||||
[class*="badge"],
|
|
||||||
[class*="pill"],
|
|
||||||
[class*="tag"] {
|
|
||||||
font-family: var(--marimo-monospace-font) !important;
|
|
||||||
font-size: 11px !important;
|
|
||||||
font-weight: 500 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Focus visible for accessibility */
|
|
||||||
:focus-visible {
|
|
||||||
outline: 2px solid var(--ds-blue-light) !important;
|
|
||||||
outline-offset: 2px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
CELL OUTPUT AREA
|
|
||||||
============================================ */
|
|
||||||
[data-testid="outputs"],
|
|
||||||
.output-area {
|
|
||||||
background: rgba(22, 32, 64, 0.5) !important;
|
|
||||||
border-top: 2px solid var(--ds-slate) !important;
|
|
||||||
padding: 12px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* DataFrame outputs */
|
|
||||||
[class*="dataframe"] {
|
|
||||||
font-family: var(--marimo-monospace-font) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Plot/chart containers */
|
|
||||||
[class*="plot"],
|
|
||||||
[class*="chart"],
|
|
||||||
svg {
|
|
||||||
background: transparent !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
HOME PAGE - "CREATE NEW NOTEBOOK" CARD
|
|
||||||
============================================ */
|
|
||||||
a[rel="noreferrer"][target="_blank"] {
|
|
||||||
background: var(--ds-navy-light) !important;
|
|
||||||
border: 3px solid var(--ds-slate) !important;
|
|
||||||
border-radius: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
a[rel="noreferrer"][target="_blank"]:hover {
|
|
||||||
border-color: var(--ds-blue) !important;
|
|
||||||
background: var(--ds-navy-mid) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
a[rel="noreferrer"][target="_blank"] h2 {
|
|
||||||
font-family: var(--marimo-heading-font) !important;
|
|
||||||
font-size: 12px !important;
|
|
||||||
color: var(--ds-white) !important;
|
|
||||||
text-transform: uppercase !important;
|
|
||||||
letter-spacing: 2px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
HOME PAGE - SECTION HEADERS
|
|
||||||
============================================ */
|
|
||||||
h2.text-xl {
|
|
||||||
font-family: var(--marimo-heading-font) !important;
|
|
||||||
font-size: 11px !important;
|
|
||||||
text-transform: uppercase !important;
|
|
||||||
letter-spacing: 2px !important;
|
|
||||||
color: var(--ds-white) !important;
|
|
||||||
padding-bottom: 6px;
|
|
||||||
border-bottom: 2px solid var(--ds-slate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
HOME PAGE - FILE LISTS (notebook lists)
|
|
||||||
============================================ */
|
|
||||||
.divide-y.border.rounded {
|
|
||||||
border: 3px solid var(--ds-slate) !important;
|
|
||||||
border-radius: 0 !important;
|
|
||||||
background: var(--ds-navy) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divide-y.border > a,
|
|
||||||
.divide-y.border > div > a {
|
|
||||||
border-color: var(--ds-navy-light) !important;
|
|
||||||
transition: all 0.1s ease !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divide-y.border > a:hover,
|
|
||||||
.divide-y.border > div > a:hover {
|
|
||||||
background: rgba(51, 102, 204, 0.2) !important;
|
|
||||||
border-left: 3px solid var(--ds-blue) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divide-y.border a .flex-1 span {
|
|
||||||
font-family: var(--marimo-monospace-font) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
HOME PAGE - WORKSPACE FILE TREE
|
|
||||||
============================================ */
|
|
||||||
[class*="cursor-pointer"][class*="whitespace-nowrap"]:hover {
|
|
||||||
background: rgba(51, 102, 204, 0.2) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
[class*="cursor-pointer"][class*="whitespace-nowrap"] span {
|
|
||||||
font-family: var(--marimo-monospace-font) !important;
|
|
||||||
font-size: 13px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
BORDERS
|
|
||||||
============================================ */
|
|
||||||
.rounded-lg,
|
|
||||||
.rounded-md {
|
|
||||||
border-radius: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rounded-sm,
|
|
||||||
.rounded {
|
|
||||||
border-radius: 2px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
TOP BAR / ACTION BUTTONS
|
|
||||||
============================================ */
|
|
||||||
.absolute.top-3.right-5 button {
|
|
||||||
border: 2px solid var(--ds-slate) !important;
|
|
||||||
border-radius: 0 !important;
|
|
||||||
background: var(--ds-navy-light) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.absolute.top-3.right-5 button:hover {
|
|
||||||
background: var(--ds-blue) !important;
|
|
||||||
border-color: var(--ds-blue-light) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
SEARCH INPUT
|
|
||||||
============================================ */
|
|
||||||
input#search {
|
|
||||||
border: 3px solid var(--ds-slate) !important;
|
|
||||||
border-radius: 0 !important;
|
|
||||||
background: var(--ds-navy) !important;
|
|
||||||
font-family: var(--marimo-monospace-font) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
input#search:focus {
|
|
||||||
border-color: var(--ds-blue-light) !important;
|
|
||||||
box-shadow: 0 0 8px rgba(85, 136, 221, 0.3) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
BUTTON STATES
|
|
||||||
============================================ */
|
|
||||||
button:active,
|
|
||||||
[role="button"]:active {
|
|
||||||
transform: translate(1px, 1px) !important;
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Dashboard tiles — file browser as card grid ─────────── */
|
|
||||||
|
|
||||||
/* Hide marimo resources / external links section */
|
|
||||||
.home-page a[href*="docs.marimo.io"],
|
|
||||||
.home-page a[href*="discord"],
|
|
||||||
.home-page a[href*="github.com/marimo-team"],
|
|
||||||
.home-page [class*="resource"],
|
|
||||||
.home-page [class*="Resource"],
|
|
||||||
section:has(a[href*="docs.marimo.io"]) {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* File browser → tile grid */
|
|
||||||
.home-page [class*="file-list"],
|
|
||||||
.home-page [class*="FileList"],
|
|
||||||
.home-page .divide-y {
|
|
||||||
display: grid !important;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)) !important;
|
|
||||||
gap: 12px !important;
|
|
||||||
border: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Individual notebook tiles */
|
|
||||||
.home-page [class*="file-list"] > *,
|
|
||||||
.home-page [class*="FileList"] > *,
|
|
||||||
.home-page .divide-y > * {
|
|
||||||
border: 1px solid #2a3f5f !important;
|
|
||||||
border-radius: 6px !important;
|
|
||||||
padding: 16px !important;
|
|
||||||
background: #162040 !important;
|
|
||||||
transition: border-color 0.15s, background 0.15s !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-page [class*="file-list"] > *:hover,
|
|
||||||
.home-page [class*="FileList"] > *:hover,
|
|
||||||
.home-page .divide-y > *:hover {
|
|
||||||
border-color: #4488dd !important;
|
|
||||||
background: #1e2d50 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tile headings */
|
|
||||||
.home-page [class*="file-list"] a,
|
|
||||||
.home-page [class*="FileList"] a,
|
|
||||||
.home-page .divide-y a {
|
|
||||||
color: #f0f0f0 !important;
|
|
||||||
font-weight: 600 !important;
|
|
||||||
text-decoration: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Section headers */
|
|
||||||
.home-page h2,
|
|
||||||
.home-page [class*="section-header"],
|
|
||||||
.home-page .text-xl {
|
|
||||||
font-family: 'IBM Plex Mono', monospace !important;
|
|
||||||
font-weight: 600 !important;
|
|
||||||
font-size: 14px !important;
|
|
||||||
letter-spacing: 1px !important;
|
|
||||||
text-transform: uppercase !important;
|
|
||||||
color: #7a90b0 !important;
|
|
||||||
border-bottom: 1px solid #2a3f5f !important;
|
|
||||||
padding-bottom: 8px !important;
|
|
||||||
margin-bottom: 16px !important;
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
[display]
|
|
||||||
theme = "dark"
|
|
||||||
custom_css = ["/home/kert/.config/marimo/loch.css"]
|
|
||||||
|
|
||||||
[snippets]
|
|
||||||
custom_paths = ["/home/kert/notebooks/snippets"]
|
|
||||||
include_default_snippets = false
|
|
||||||
125
tests/test_notebook_layout.py
Normal file
125
tests/test_notebook_layout.py
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
"""Tests for notebook separation of concerns — config in infra/, content in notebooks/."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import tomllib
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
NOTEBOOKS = ROOT / "notebooks"
|
||||||
|
INFRA_MARIMO = ROOT / "infra" / "marimo"
|
||||||
|
|
||||||
|
|
||||||
|
class TestNotebooksClean:
|
||||||
|
"""notebooks/ should contain only .py notebook files."""
|
||||||
|
|
||||||
|
def test_no_config_files(self):
|
||||||
|
"""No TOML, CSS, JS, or hidden config dirs in notebooks/."""
|
||||||
|
bad = []
|
||||||
|
for p in NOTEBOOKS.iterdir():
|
||||||
|
if p.name.startswith("__"):
|
||||||
|
continue # __marimo__, __pycache__ are fine
|
||||||
|
if p.name == "aco.duckdb":
|
||||||
|
continue # symlink, gitignored
|
||||||
|
if p.suffix not in (".py",):
|
||||||
|
bad.append(p.name)
|
||||||
|
if p.name.startswith("."):
|
||||||
|
bad.append(p.name)
|
||||||
|
assert bad == [], f"Non-notebook files found: {bad}"
|
||||||
|
|
||||||
|
def test_only_py_files(self):
|
||||||
|
"""All visible files are .py notebooks."""
|
||||||
|
py_files = [
|
||||||
|
p
|
||||||
|
for p in NOTEBOOKS.iterdir()
|
||||||
|
if p.is_file() and not p.name.startswith("__") and p.name != "aco.duckdb"
|
||||||
|
]
|
||||||
|
assert all(p.suffix == ".py" for p in py_files)
|
||||||
|
assert len(py_files) >= 12
|
||||||
|
|
||||||
|
def test_no_marimo_config_dir(self):
|
||||||
|
assert not (NOTEBOOKS / ".marimo-config").exists()
|
||||||
|
|
||||||
|
def test_no_marimo_toml(self):
|
||||||
|
assert not (NOTEBOOKS / ".marimo.toml").exists()
|
||||||
|
|
||||||
|
def test_no_snippets_dir(self):
|
||||||
|
assert not (NOTEBOOKS / "snippets").exists()
|
||||||
|
|
||||||
|
def test_no_home_page_js(self):
|
||||||
|
assert not (NOTEBOOKS / "home-page-patched.js").exists()
|
||||||
|
|
||||||
|
|
||||||
|
class TestInfraMarimo:
|
||||||
|
"""infra/marimo/ should contain server config, snippets, and assets."""
|
||||||
|
|
||||||
|
def test_directory_exists(self):
|
||||||
|
assert INFRA_MARIMO.is_dir()
|
||||||
|
|
||||||
|
def test_marimo_toml_exists(self):
|
||||||
|
assert (INFRA_MARIMO / "marimo.toml").is_file()
|
||||||
|
|
||||||
|
def test_marimo_toml_valid(self):
|
||||||
|
text = (INFRA_MARIMO / "marimo.toml").read_text()
|
||||||
|
data = tomllib.loads(text)
|
||||||
|
assert "display" in data
|
||||||
|
assert "snippets" in data
|
||||||
|
assert "runtime" in data
|
||||||
|
|
||||||
|
def test_snippet_path_updated(self):
|
||||||
|
text = (INFRA_MARIMO / "marimo.toml").read_text()
|
||||||
|
data = tomllib.loads(text)
|
||||||
|
paths = data["snippets"]["custom_paths"]
|
||||||
|
assert any(".config/marimo/snippets" in p for p in paths)
|
||||||
|
assert not any("notebooks/snippets" in p for p in paths)
|
||||||
|
|
||||||
|
def test_snippets_directory(self):
|
||||||
|
snippets_dir = INFRA_MARIMO / "snippets"
|
||||||
|
assert snippets_dir.is_dir()
|
||||||
|
snippet_files = list(snippets_dir.glob("*.py"))
|
||||||
|
assert len(snippet_files) == 10
|
||||||
|
|
||||||
|
def test_all_snippets_present(self):
|
||||||
|
expected = {
|
||||||
|
"bib-connect.py",
|
||||||
|
"chart-bar.py",
|
||||||
|
"chart-line.py",
|
||||||
|
"duckdb-connect.py",
|
||||||
|
"nessie-connect.py",
|
||||||
|
"query-demographics.py",
|
||||||
|
"query-pmpm.py",
|
||||||
|
"query-schema.py",
|
||||||
|
"s3-obstore.py",
|
||||||
|
"trino-connect.py",
|
||||||
|
}
|
||||||
|
actual = {p.name for p in (INFRA_MARIMO / "snippets").iterdir()}
|
||||||
|
assert actual == expected
|
||||||
|
|
||||||
|
def test_home_page_js_exists(self):
|
||||||
|
assert (INFRA_MARIMO / "home-page-patched.js").is_file()
|
||||||
|
|
||||||
|
def test_no_loch_css_duplicate(self):
|
||||||
|
"""loch.css should NOT be in infra/marimo/ — served from assets/css/."""
|
||||||
|
assert not (INFRA_MARIMO / "loch.css").exists()
|
||||||
|
|
||||||
|
|
||||||
|
class TestNoStaleReferences:
|
||||||
|
"""No stale paths to old locations in config files."""
|
||||||
|
|
||||||
|
def test_compose_no_old_config_mount(self):
|
||||||
|
text = (ROOT / "compose.yml").read_text()
|
||||||
|
assert ".marimo-config" not in text
|
||||||
|
|
||||||
|
def test_compose_no_old_homepage_mount(self):
|
||||||
|
text = (ROOT / "compose.yml").read_text()
|
||||||
|
assert "notebooks/home-page" not in text
|
||||||
|
|
||||||
|
def test_compose_uses_infra_marimo(self):
|
||||||
|
text = (ROOT / "compose.yml").read_text()
|
||||||
|
assert "infra/marimo" in text
|
||||||
|
|
||||||
|
def test_stack_toml_path_filter(self):
|
||||||
|
text = (ROOT / "stack.toml").read_text()
|
||||||
|
data = tomllib.loads(text)
|
||||||
|
filters = data["images"]["notebooks"]["path_filter"]
|
||||||
|
assert "infra/marimo/**" in filters
|
||||||
Reference in New Issue
Block a user