Skip to content

Commit ee75b5b

Browse files
nav: one registry drives the sidebar; the battery names its lane (items 16+17) — STAGED, unpushed
Item 16, ported as contract. The sidebar is generated from frontmatter `category:` + `order:` in CATEGORY_ORDER instead of three hand-typed link clusters in components/navbar.py. Every page already declared a category; the navbar ignored them, which is exactly how /audiences/web-crawlers came to be listed twice — as "Web Crawlers" under This package and as "A · What the crawler sees" under Showcase. It is listed once now. The three cluster names survive as category names, per the drop. Gained: /changelog as a Timeline, a footer, an Other Apps menu built from network_directory.PRIMARY, lib/aside.py collapsing the aside where no `.. toc::` fills it, and an Admin section that appears FOR THE OWNER — previously a blanket /admin/* filter hid both admin pages from everyone including them. The anonymous tree still carries no /admin/ href. Lost, deliberately: the sidebar's Pip Components and Other Apps I've built sections (the network is listed once, in the menu) and everything in Resources except dmc. /api is NOT registered, and that is a measurement rather than an omission: dash_improve_my_llms ships no component metadata — no metadata.json, no bundled JS, no generated component classes — so there are no props to tabulate. API_PACKAGES = []. Also in 16: home renders through markdown2dash rather than dcc.Markdown, /admin/traffic swaps dcc.Dropdown for dmc.DatePickerInput and gains a People section above the crawler tables, and the mobile-fit CSS keeps a code block inside a List item from widening the whole document. FIXED, found by item 16's own rewritten test: five docs pages hyperlinked /admin/control-board in their prose, putting an admin path into /llms.txt. The prose keeps the words and drops the link. Item 17: the battery's default UA now names the browser lane — a Chrome token first, the internal token after it (substring match, so the internal-traffic exclusion still holds), CRAWLER_UA untouched. At dimll >= 2.8 the bare internal token was crawler-lane, so every default-UA check was reading the prerendered crawler document. Item 17(b) is not-applicable here: the only og:image in run.py is a comment describing the 2.5.0 history, and this fork declares identity through the package's configure_seo rather than injecting tags. Cargo-eligibility evidence the item asks for: nine of the eleven files came out BYTE-IDENTICAL to the template's at 1.6.38. The two that differ are recorded as DIVERGENCES.md 8, and both seams are the template's, not this fork's — header.py hardcodes its own 'ddb.png' logo filename, and test_nav_contract.py names /backend-comparison, a tutorial page this fork deleted at fork time. DIVERGENCES.md 9 records SAME_AS keeping the PyPI distribution beside the repo. Suite: 688 passed / 2 skipped (flask), 685 / 5 (fastapi), 685 / 5 (quart). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EjfiNPKsgLwAxtN3BexPSJ
1 parent e4031a1 commit ee75b5b

36 files changed

Lines changed: 1691 additions & 246 deletions

CHANGELOG.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,68 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [llms-2plot-dev 1.4.0] - 2026-08-30
9+
10+
**Sync items 16 and 17 — the navigation contract and the battery's lane.**
11+
12+
### Added
13+
14+
- **`/changelog`** — this file as a Timeline, linked under Home in the
15+
sidebar and from the footer, and served as its own `llms.txt`.
16+
- **A footer** — © Pip Install Python LLC, the owner's GitHub profile,
17+
Discord and YouTube, every icon labelled.
18+
- **An Admin section in the sidebar, visible to the OWNER.** Both admin
19+
pages (`/admin/control-board`, `/admin/traffic`) were previously
20+
hidden from everyone including the owner by a blanket `/admin/*` path
21+
filter; they are now listed for `is_admin_user()` and for nobody else —
22+
the anonymous tree carries no `/admin/` href at all.
23+
- **An `Other Apps` menu in the top bar**, built from
24+
`network_directory.PRIMARY` rather than a hand-typed list.
25+
- **`lib/aside.py`** — the aside column collapses on pages with no
26+
`.. toc::`, so `/changelog` and `/` render full width.
27+
28+
### Changed
29+
30+
- **The sidebar is generated from frontmatter**, not from three
31+
hand-typed link clusters in `components/navbar.py`. Every page already
32+
declared `category:`; the navbar simply ignored it. Each page now also
33+
declares `order:`. **`/audiences/web-crawlers` is listed once** — it
34+
appeared twice, as "Web Crawlers" under This package and as
35+
"A · What the crawler sees" under Showcase.
36+
- **The sidebar's `Pip Components` and `Other Apps I've built` sections
37+
are gone** — the network is listed once, in the top bar's menu.
38+
`Resources` is third-party only: `dmc`. No `community.plotly.com`, no
39+
`2plot.dev`.
40+
- **`pages/home.py` renders through markdown2dash**, not `dcc.Markdown`,
41+
so home's headings, tables and code fences match every docs page.
42+
- **`/admin/traffic` uses `dmc.DatePickerInput`** instead of
43+
`dcc.Dropdown`, and gains a **People** section — the day's human hits,
44+
visitors, sessions and median session — above the crawler tables, with
45+
the line that humans never enter the read ledger.
46+
- **Mobile fit**: code blocks inside a List item, Blockquote or Timeline
47+
scroll in their own box instead of widening the document; the rules are
48+
in `main.css` against public Mantine class names, never per page.
49+
- **`/api` is NOT registered here, and that is a measurement.**
50+
`dash_improve_my_llms` ships no component metadata — no
51+
`metadata.json`, no bundled JS, no generated component classes
52+
(checked on 2.8.0). It is a library of routes, middleware and config
53+
objects, so there are no props to tabulate. `API_PACKAGES` is `[]`.
54+
- **The live battery's default User-Agent names the browser lane**
55+
(item 17). At dimll ≥2.8 a UA with no browser engine token is
56+
crawler-lane, so `scripts/network_smoke.py`'s bare internal token made
57+
every default-UA check read the prerendered crawler document. A Chrome
58+
token now leads, the internal token follows it (substring match, so
59+
internal-traffic exclusion still holds), and `CRAWLER_UA` is untouched.
60+
61+
### Fixed
62+
63+
- **Admin URLs no longer leak into the machine corpus.** Five pages
64+
hyperlinked `/admin/control-board` in their prose, which put an admin
65+
path in `/llms.txt`. Found by item 16's rewritten
66+
`tests/test_excluded_links_hidden.py`. The prose keeps the words and
67+
drops the link — the URL is useless to anyone who cannot authenticate,
68+
and publishing it invited crawlers to an admin path.
69+
870
## [llms-2plot-dev 1.3.0] - 2026-08-30
971

1072
**Round 3.4, the posture flip — consumed as sync item 15, with

DIVERGENCES.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,36 @@ this host is a worked example. Expect the FIRST promoted run after
103103
adoption to go red on the wait until the dashboard is switched, and read
104104
that red as the owner step outstanding, not as a defect in cd.yml.
105105

106+
**8. Two item-16 files are not byte-identical to the template's, and
107+
both seams are the TEMPLATE's, not this fork's.** 1.6.38 asks each fork
108+
to report which of the eight navigation files came out byte-identical,
109+
as the evidence for reclassing them as cargo next round. Nine of the
110+
eleven files this fork took are identical (`components/navbar.py`,
111+
`footer.py`, `appshell.py`, `pages/changelog.py`, `pages/api.py`,
112+
`lib/api_reference.py`, `lib/aside.py`, `lib/network_directory.py`,
113+
`tests/test_excluded_links_hidden.py`). The two that differ:
114+
115+
- `components/header.py` — ONE line: `get_asset_url('ddb.png')`. The
116+
template hardcodes its own logo filename in the component, so the
117+
file still carries fork identity and cannot be cargo until that
118+
moves to a constant beside `WORDMARK`. This fork serves
119+
`llms.png`.
120+
- `tests/test_nav_contract.py` — the aside pin names
121+
`/backend-comparison`, a template tutorial page this fork DELETED at
122+
fork time (divergence 1). Two of this site's own `.. toc::` pages
123+
stand in. Cargo-eligible once the pin picks its paths from the
124+
registry rather than naming them.
125+
126+
Recorded so the reclass has the evidence rather than a claim, and so a
127+
later sync does not read either line as drift.
128+
129+
**9. `SAME_AS` carries the PyPI distribution as well as the repo.** The
130+
template's 1.6.38 constant is `SAME_AS = [GITHUB_URL]`. This site
131+
documents a published package, so its JSON-LD identity claims both the
132+
repo and `https://pypi.org/project/dash-improve-my-llms/` — the
133+
docs-home ↔ package loop this site exists to close. `GITHUB_URL` itself
134+
points at the PACKAGE's repo, not this site's, for the same reason.
135+
106136
## Byte-owned paths
107137

108138
Paths this fork owns byte-for-byte. The F3b fan-out never overwrites

assets/main.css

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,12 +228,69 @@ input:invalid {
228228
Element+class selector so it outranks Mantine's own width rule
229229
regardless of stylesheet load order. markdown2dash stamps m2d-table on
230230
every table it renders, including kwargs prop tables. */
231-
table.m2d-table {
231+
table.m2d-table,
232+
table.m2d-block-kwargs {
232233
display: block;
233234
width: max-content;
234235
max-width: 100%;
235236
overflow-x: auto;
236237
}
238+
/* (1.6.39) `.. kwargs::` prop tables carry m2d-block-kwargs, not m2d-table
239+
— the one table class the rule above never matched; measured on
240+
/examples/directives at 391px: a 457px table, a 488px document. */
241+
242+
/* Code blocks scroll inside their own box, wherever they sit (1.6.39;
243+
owner-observed on /backend-comparison at phone width). CodeHighlight's
244+
<pre> already scrolls, but a flex parent with the default min-width:auto
245+
— Mantine's List item wrapper, a Blockquote, a Stack — refuses to shrink
246+
below the code's intrinsic width and drags the whole document wide
247+
(measured: a 959px document in a 200px viewport, widest element the
248+
List itemWrapper around the "Replace @server.before_request" block).
249+
Public Mantine class names only — never the hashed .m_* internals. */
250+
.mantine-List-itemWrapper,
251+
.mantine-List-itemLabel,
252+
.mantine-List-item,
253+
.mantine-Blockquote-root,
254+
.mantine-Timeline-itemBody,
255+
.mantine-CodeHighlight-root,
256+
.mantine-CodeHighlightTabs-root {
257+
min-width: 0;
258+
max-width: 100%;
259+
}
260+
/* The wrapper is inline-flex and the label a block child: without
261+
min-width:0 on the LABEL too, it grows to the code's width (measured
262+
887px inside a 287px wrapper on /backend-comparison at 391px). */
263+
.mantine-List-itemWrapper {
264+
width: 100%;
265+
}
266+
.mantine-List-itemLabel {
267+
flex: 1 1 auto;
268+
}
269+
/* Timeline bullets (/changelog): a Text in a no-wrap Group grows to its
270+
longest unbreakable token — a 60-character test name in a code span —
271+
and leaves its card (a 549px document at 391px). Same rule as the
272+
component sets inline; here so the stylesheet alone holds the line. */
273+
.mantine-Timeline-root .mantine-Text-root,
274+
.mantine-Timeline-root .mantine-Code-root {
275+
min-width: 0;
276+
overflow-wrap: anywhere;
277+
}
278+
/* Inline code in prose: a long unbreakable token (`app.server.before_request`
279+
chains, dotted module paths) is the last thing that can push a page past
280+
the viewport once blocks and tables scroll in their own boxes — measured:
281+
a 353px codespan, a 425px document at 391px on /backend-comparison.
282+
Block code (CodeHighlight) keeps pre formatting; this is inline only. */
283+
code.m2d-codespan,
284+
.mantine-Code-root:not(.mantine-CodeHighlight-code) {
285+
overflow-wrap: anywhere;
286+
white-space: normal;
287+
}
288+
.mantine-CodeHighlight-root pre,
289+
.mantine-CodeHighlightTabs-root pre,
290+
.mantine-AppShell-main pre {
291+
overflow-x: auto;
292+
max-width: 100%;
293+
}
237294

238295
/* NEVER style Mantine's hashed .m_* classes — they are private internals,
239296
version-coupled by construction. A rule that stops matching after a bump

components/appshell.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import dash_mantine_components as dmc
2-
from dash import Output, Input, clientside_callback, dcc, page_container, State
2+
from dash import Output, Input, callback, clientside_callback, dcc, page_container, State
33

4+
from components.footer import FOOTER_HEIGHT, create_footer
5+
from lib.aside import aside_config
46
from components.header import create_header
57
from components.navbar import create_navbar, create_navbar_drawer
68
from lib.constants import PRIMARY_COLOR, HEADER_HEIGHT
@@ -181,22 +183,22 @@ def create_appshell(data):
181183
create_navbar_drawer(data),
182184
dmc.AppShellMain(
183185
children=page_container,
184-
style={"minHeight": f"calc(100dvh - {HEADER_HEIGHT}px)"} # Full height minus header
186+
style={"minHeight": f"calc(100dvh - {HEADER_HEIGHT + FOOTER_HEIGHT}px)"}
185187
),
188+
create_footer(),
186189
],
187190
id="m2d-appshell",
188191
header={"height": HEADER_HEIGHT},
192+
footer={"height": FOOTER_HEIGHT},
189193
padding="xl",
190194
navbar={
191195
"width": 280,
192196
"breakpoint": "md", # Collapse on medium screens and below
193197
"collapsed": {"mobile": True},
194198
},
195-
aside={
196-
"width": 280,
197-
"breakpoint": "xl",
198-
"collapsed": {"desktop": False, "mobile": True},
199-
},
199+
# Collapsed wherever the page renders no TOC — the callback
200+
# below re-evaluates it per navigation (lib/aside.py).
201+
aside=aside_config("/"),
200202
withBorder=True,
201203
),
202204
],
@@ -307,3 +309,13 @@ def create_appshell(data):
307309
Input("url", "pathname"),
308310
State("desktop-navbar-collapsed", "data"),
309311
)
312+
313+
314+
@callback(
315+
Output("m2d-appshell", "aside"),
316+
Input("url", "pathname"),
317+
)
318+
def _collapse_aside_without_toc(pathname):
319+
"""Full width for pages that render no aside (1.6.39): /changelog, /api,
320+
home, the admin pages. The docs pages keep their TOC column."""
321+
return aside_config(pathname)

components/footer.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""The footer — identical on every host (1.6.38).
2+
3+
© {this year} Pip Install Python LLC · GitHub PROFILE · Discord · YouTube.
4+
The repository link is the top bar's icon, the single Changelog link is
5+
the sidebar's (owner, 2026-08-30). Every icon carries an accessible name;
6+
the year is computed; no Terms/Privacy links until those pages exist.
7+
"""
8+
from datetime import datetime
9+
10+
import dash_mantine_components as dmc
11+
from dash_iconify import DashIconify
12+
13+
from lib.constants import DISCORD_URL, GITHUB_PROFILE_URL, PUBLISHER, YOUTUBE_SUBSCRIBE_URL
14+
15+
FOOTER_HEIGHT = 56
16+
17+
18+
def _icon_link(icon, href, label):
19+
return dmc.Anchor(
20+
dmc.ActionIcon(
21+
DashIconify(icon=icon, width=20),
22+
size="lg",
23+
variant="subtle",
24+
color="gray",
25+
**{"aria-label": label},
26+
),
27+
href=href,
28+
target="_blank",
29+
**{"aria-label": label},
30+
)
31+
32+
33+
def create_footer():
34+
return dmc.AppShellFooter(
35+
dmc.Container(
36+
dmc.Group(
37+
[
38+
dmc.Text(f"© {datetime.now().year} {PUBLISHER}", size="sm", c="dimmed"),
39+
dmc.Group(
40+
[
41+
_icon_link("radix-icons:github-logo", GITHUB_PROFILE_URL, "Pip Install Python on GitHub"),
42+
_icon_link("ic:baseline-discord", DISCORD_URL, "Join the 2plot Discord"),
43+
_icon_link("mdi:youtube", YOUTUBE_SUBSCRIBE_URL, "Subscribe on YouTube"),
44+
],
45+
gap="sm",
46+
),
47+
],
48+
justify="space-between",
49+
wrap="nowrap",
50+
),
51+
fluid=True,
52+
px="md",
53+
h="100%",
54+
style={"display": "flex", "alignItems": "center"},
55+
),
56+
h=FOOTER_HEIGHT,
57+
withBorder=True,
58+
)

0 commit comments

Comments
 (0)