From 4eaf657374afd609ab3b3ae086d8000f4336f369 Mon Sep 17 00:00:00 2001
From: ainyan03 <205502311+ainyan03@users.noreply.github.com>
Date: Mon, 12 Oct 2026 08:48:47 +0000
Subject: [PATCH] Rework the board spec editor around a page list and readable
connector pinouts
Header: merge the title, toolbar, revision options, mode bar and view tabs into one row. Runtime choices open from a compact condition panel, New/Duplicate/Remove move into an Open menu, the first-run guide becomes a dialog and JSON files can be dropped anywhere. "Edit raw data" is renamed to describe what it does: return to base data by clearing the resolved selection.
Pages: a list on the left (board information, GPIO, each connector or connector group, buses, devices, accessories, revisions, pin owners, validation) selects the page shown at full height. All pages stay rendered and only visibility changes, so links into hidden pages resolve. Every navigation goes through one function with a one-step back history and scroll restoration, and falls back to the category when an object disappears after switching boards or importing. Editing a role does not change the page. Validation errors open the GPIO row when they resolve to one, otherwise the affected object's page. Layout state uses a new storage key.
Connector pages: show the connector note, verification, type view, layout status and clickable official links next to the pinout. Sockets that share a connector group are shown side by side with headers, references and tables aligned in shared grid rows, while names, first-terminal marks, notes and editing stay per socket. Role chips and power terminals use the same owner colors as the GPIO table, and wire colors get their own column only for connector types that define them. Each endpoint sits beside its terminal number; terminal and endpoint columns are sized to their content and functions take the remaining width, mirrored around the center of two-row connectors. The diagram view is removed because tables cover every connector type in the catalog, and the undo button moves into the page heading, shown only when there is something to undo.
A GPIO shared by several connector positions lists all of them, and hovering only highlights; moving between a terminal and its GPIO row needs an explicit click or key.
---
extras/board_spec/dist/board_spec_editor.html | 1116 ++++++++++-------
extras/board_spec/editor/editor.css | 199 ++-
extras/board_spec/editor/editor.js | 644 +++++-----
extras/board_spec/editor/html.js | 77 ++
extras/board_spec/editor/index.html | 183 ++-
extras/board_spec/editor/role_colors.js | 17 +-
extras/board_spec/test/board_spec.test.js | 358 +++++-
7 files changed, 1664 insertions(+), 930 deletions(-)
diff --git a/extras/board_spec/dist/board_spec_editor.html b/extras/board_spec/dist/board_spec_editor.html
index 49111528..63353fbb 100644
--- a/extras/board_spec/dist/board_spec_editor.html
+++ b/extras/board_spec/dist/board_spec_editor.html
@@ -53,9 +53,9 @@
.onboarding-heading .eyebrow { color: var(--accent); }
.onboarding-heading button { flex: none; padding: .32rem .55rem; }
.onboarding-purpose { margin: .35rem 0 .65rem; color: #38525c; font-size: .78rem; }
-.onboarding > .motion-region-inner > ol { display: grid; grid-template-columns: repeat(4, minmax(8rem, 1fr)); gap: .55rem; margin: 0; padding: 0; list-style: none; }
-.onboarding > .motion-region-inner > ol > li { display: flex; align-items: start; gap: .4rem; color: #38525c; font-size: .72rem; }
-.onboarding > .motion-region-inner > ol > li > strong { display: grid; flex: none; place-items: center; inline-size: 1.45rem; block-size: 1.45rem; border-radius: 50%; background: var(--accent); color: #fff; }
+.onboarding > ol { display: grid; grid-template-columns: repeat(4, minmax(8rem, 1fr)); gap: .55rem; margin: 0; padding: 0; list-style: none; }
+.onboarding > ol > li { display: flex; align-items: start; gap: .4rem; color: #38525c; font-size: .72rem; }
+.onboarding > ol > li > strong { display: grid; flex: none; place-items: center; inline-size: 1.45rem; block-size: 1.45rem; border-radius: 50%; background: var(--accent); color: #fff; }
.onboarding-export details { margin-top: .25rem; }
.onboarding-export summary { cursor: pointer; color: var(--accent); font-weight: 750; }
.onboarding-export ol { display: grid; gap: .15rem; margin: .25rem 0; padding-left: 1.2rem; }
@@ -144,7 +144,6 @@
.gpio-row:focus { outline: 2px solid #005fcc; outline-offset: -2px; }
.gpio-row.is-expanded td { background-color: #eaf6f5; border-bottom-color: #b8d9d7; }
.gpio-row.is-readonly td { color: #53656d; }
-.gpio-row.diagram-highlight td { background-color: #fff1b8; }
tr.gpio-reserved td:first-child { background: var(--reserved); }
tr.gpio-input td:first-child { box-shadow: inset 4px 0 #6699cc; }
tr.gpio-strapping td:first-child { border-right: 5px solid #e1b12c; }
@@ -245,56 +244,34 @@
.error-list .warning button { background: #fff8df; color: var(--warning); border-color: #e7c86e; }
.error-list code { display: block; margin-top: .2rem; color: #7d3b34; font-size: .72rem; white-space: normal; }
.validation-action { display: block; margin-top: .3rem; color: inherit; font-size: .7rem; line-height: 1.35; }
-.connector-tabs { display: flex; gap: .3rem; padding: .55rem .65rem; overflow-x: auto; border-bottom: 1px solid var(--line); }
+.connector-tabs { display: flex; gap: .3rem; padding: .55rem .65rem; flex-wrap: wrap; border-bottom: 1px solid var(--line); }
.connector-card { position: relative; }
-.connector-actions, .connector-view-toggle { display: flex; align-items: center; gap: .3rem; }
-.connector-actions { justify-content: flex-end; padding: 0 .65rem .55rem; }
-.connector-view-toggle { padding: .18rem; border: 1px solid #afbec7; border-radius: 7px; background: #edf2f3; }
-.connector-view-toggle button { padding: .2rem .42rem; border: 0; background: transparent; color: var(--muted); font-size: .7rem; }
-.connector-view-toggle button.active { background: #fff; color: var(--accent); box-shadow: 0 1px 3px rgb(19 43 53 / 18%); }
.connector-tabs button, .position-editor-tabs button { padding: .3rem .5rem; background: #fff; color: var(--muted); border-color: #afbec7; font-size: .72rem; white-space: nowrap; }
.connector-tabs button.active, .position-editor-tabs button.active { background: var(--accent); color: #fff; border-color: var(--accent); }
.connector-tabs button.connector-object-tab { border-left-color: var(--role-bg); border-left-width: 5px; }
-.connector-diagram { padding: .5rem; overflow: auto; background: #f8fafb; }
-.connector-diagram svg { display: block; width: 100%; min-width: 14rem; height: auto; margin: auto; }
+.connector-pinout { padding: .5rem; overflow: auto; background: #f8fafb; }
.connector-meta { display: flex; justify-content: space-between; gap: .5rem; margin: 0 .25rem .35rem; color: var(--muted); font-size: .68rem; }
-.connector-position { cursor: pointer; outline: none; }
-.connector-position circle { fill: var(--position-color, #fff); stroke: #405c67; stroke-width: 2; }
-.connector-position.default { opacity: .58; }
-.connector-position.default circle { stroke-dasharray: 3 3; stroke-width: 1.5; }
-.connector-position.fixed circle { fill: var(--position-color, #aab5bb); stroke: #66747b; }
-.connector-position.missing circle { fill: #fff; stroke: #c35347; stroke-dasharray: 4 3; }
-.connector-position.selected circle { stroke: #006d77; stroke-width: 5; }
-.connector-position.gpio-highlight circle { filter: drop-shadow(0 0 5px #e6a700); stroke: #e6a700; stroke-width: 5; }
-.connector-position:focus circle { stroke: #005fcc; stroke-width: 5; }
-.connector-position text { pointer-events: none; text-anchor: middle; fill: var(--role-fg, #17212b); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
-.connector-key { fill: var(--wash); stroke: #607080; stroke-width: 2; }
-.connector-position.fixed text { fill: #fff; }
-.position-id { font-size: 9px; font-weight: 800; }
-.position-label { font-size: 8px; }
-.position-endpoint { font-size: 9px; font-weight: 800; }
-.connector-pin-table { width: 100%; border-collapse: separate; border-spacing: 0; table-layout: fixed; font: .7rem ui-monospace, SFMono-Regular, Menlo, monospace; }
+.connector-pin-table { width: 100%; border-collapse: separate; border-spacing: 0; table-layout: auto; font: .7rem ui-monospace, SFMono-Regular, Menlo, monospace; }
.connector-pin-table tr { height: 1.8rem; }
.connector-pin-table td { height: 1.8rem; padding: .28rem .35rem; overflow: hidden; border-bottom: 1px solid #dfe6e9; background: #fff; cursor: pointer; text-overflow: ellipsis; white-space: nowrap; vertical-align: middle; }
.connector-pin-table td:focus { position: relative; z-index: 1; outline: 2px solid #005fcc; outline-offset: -2px; }
-.connector-pin-table .pin-number { width: 2.8rem; color: var(--muted); font-weight: 800; text-align: center; white-space: nowrap; }
+.connector-pin-table .pin-number { width: 1%; color: var(--muted); font-weight: 800; text-align: center; white-space: nowrap; }
.connector-pin-table .pin-function { color: #506771; font-size: .64rem; }
-.connector-pin-table .pin-endpoint { font-weight: 800; }
+.connector-pin-table .pin-endpoint { width: 1%; white-space: nowrap; font-weight: 800; }
.connector-pin-table .pin-left { text-align: right; }
.connector-pin-table .pin-right { text-align: left; }
.connector-pin-table .pin-divider { border-left: 2px solid #91a5b1; }
.connector-pin-table td.default { background: #f5f7f8; color: #829098; }
.connector-pin-table td.fixed { background: #d9dfe2; color: #465860; }
.connector-pin-table td.missing { border-top: 1px dashed #c35347; border-bottom: 1px dashed #c35347; background: #fffafa; }
-.connector-pin-table td.missing.pin-endpoint.pin-left, .connector-pin-table td.missing.pin-number:not(.pin-left, .pin-right) { border-left: 1px dashed #c35347; }
-.connector-pin-table td.missing.pin-number.pin-left, .connector-pin-table td.missing.pin-endpoint:not(.pin-left, .pin-right) { border-right: 1px dashed #c35347; }
+.connector-pin-table td.missing.pin-function.pin-left, .connector-pin-table td.missing.pin-number:not(.pin-left, .pin-right) { border-left: 1px dashed #c35347; }
+.connector-pin-table td.missing.pin-number.pin-left, .connector-pin-table td.missing.pin-function:not(.pin-left, .pin-right) { border-right: 1px dashed #c35347; }
.connector-pin-table td.missing.pin-number.pin-right { border-left: 1px dashed #c35347; }
-.connector-pin-table td.missing.pin-endpoint.pin-right { border-right: 1px dashed #c35347; }
+.connector-pin-table td.missing.pin-function.pin-right { border-right: 1px dashed #c35347; }
.connector-pin-table td.selected { background: #d7efed; box-shadow: inset 0 2px #006d77, inset 0 -2px #006d77; }
.connector-pin-table td.gpio-highlight { background: #fff1b8; box-shadow: inset 0 2px #e6a700, inset 0 -2px #e6a700; }
.connector-color { display: inline-block; width: .65rem; height: .65rem; margin-right: .3rem; border: 1px solid #73828a; border-radius: 50%; vertical-align: -.08rem; background: var(--position-color, #fff); }
.connector-editor { padding: .65rem; border-top: 1px solid var(--line); }
-.connector-editor[hidden] { display: none; }
.connector-editor.open { background: #fff; }
.connector-pin-table tr.connector-inline-row { height: auto; }
.connector-pin-table td.connector-inline-cell { height: auto; padding: 0; overflow: visible; border-bottom: 1px solid #91a5b1; cursor: default; white-space: normal; }
@@ -396,7 +373,7 @@
.motion-details[open]:not(.motion-opening, .motion-closing) > .motion-collapse { grid-template-rows: 1fr; opacity: 1; visibility: visible; transition-delay: 0s; }
.motion-details.motion-opening > .motion-collapse, .motion-details.motion-closing > .motion-collapse { grid-template-rows: 0fr; opacity: 0; visibility: hidden; }
.motion-details > summary { cursor: pointer; }
-.role-chip, .role-owner-label, .related-object-link, .owner-object-tab, .connector-object-tab, .connector-position circle { transition: color var(--motion-duration) var(--motion-ease), background-color var(--motion-duration) var(--motion-ease), fill var(--motion-duration) var(--motion-ease), stroke var(--motion-duration) var(--motion-ease), border-color var(--motion-duration) var(--motion-ease); }
+.role-chip, .role-owner-label, .related-object-link, .owner-object-tab, .connector-object-tab { transition: color var(--motion-duration) var(--motion-ease), background-color var(--motion-duration) var(--motion-ease), fill var(--motion-duration) var(--motion-ease), stroke var(--motion-duration) var(--motion-ease), border-color var(--motion-duration) var(--motion-ease); }
@keyframes state-change { from { opacity: .35; } to { opacity: 1; } }
::view-transition-old(root), ::view-transition-new(root) { animation-duration: var(--motion-duration); animation-timing-function: var(--motion-ease); }
@@ -415,7 +392,7 @@
.dialog-error.warning { color: var(--warning); }
@media (max-width: 980px) {
- .onboarding > .motion-region-inner > ol { grid-template-columns: repeat(2, minmax(8rem, 1fr)); }
+ .onboarding > ol { grid-template-columns: repeat(2, minmax(8rem, 1fr)); }
.repository-next { margin-inline: 1rem; }
.repository-next .motion-region-inner { align-items: start; flex-direction: column; }
.repository-next ol { display: grid; }
@@ -445,34 +422,187 @@
.save-state { font-size: .75rem; }
.language-picker select { width: 7.25rem; }
.onboarding { margin: .5rem 1rem 0; }
- .onboarding > .motion-region-inner > ol { grid-template-columns: 1fr; }
+ .onboarding > ol { grid-template-columns: 1fr; }
+}
+
+.connector-meta { display: grid; justify-content: initial; gap: .3rem; font-size: .75rem; overflow-wrap: anywhere; }
+.connector-note { margin: 0; white-space: normal; line-height: 1.45; }
+.reference-links { display: flex; flex-wrap: wrap; gap: .3rem; font-size: .7rem; }
+.reference-links a { color: var(--accent); }
+.connector-pin-table th { padding: .35rem; background: var(--wash); color: var(--muted); text-align: center; }
+.connector-pin-table .pin-function { white-space: normal; overflow-wrap: anywhere; text-overflow: clip; }
+
+/* Compact controls leave the existing workspace its full remaining height. */
+.app-header.toolbar { position: relative; display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; min-height: 3.5rem; padding: .4rem .75rem; background: var(--paper); color: var(--ink); z-index: 20; }
+.app-header.toolbar .board-picker { display: block; min-width: 13rem; flex: 1 1 15rem; max-width: 19rem; }
+.app-header.toolbar .board-combobox { width: 100%; }
+.app-header.toolbar .board-combobox input { padding-block: .4rem; font-size: .78rem; }
+.app-header.toolbar button, .header-menu > summary { padding: .4rem .5rem; font-size: .75rem; white-space: nowrap; }
+.app-header.toolbar .header-controls { margin-left: auto; flex-direction: row; gap: .4rem; }
+.app-header.toolbar .help-button { color: var(--accent); background: var(--wash); border-color: var(--line); }
+.app-header.toolbar .language-picker { display: flex; color: var(--muted); font-size: .7rem; }
+.app-header.toolbar .language-picker select { min-width: 0; width: 6.5rem; padding: .3rem; font-size: .7rem; color: var(--ink); background: #fff; border-color: var(--line); }
+.app-header.toolbar .save-state { font-size: .72rem; color: var(--muted); }
+.app-header.toolbar .save-state.dirty { color: var(--warning); }
+.option-panel { align-items: center; gap: .35rem; min-height: 0; padding: 0; overflow: visible; }
+.option-panel .revision-control { display: block; }
+.option-panel select { min-width: 0; max-width: 8rem; padding: .35rem; font-size: .75rem; }
+.runtime-control, .header-menu { position: relative; }
+.runtime-toggle { display: flex; align-items: center; gap: .3rem; }
+.runtime-toggle span { display: inline-block; max-width: 7rem; overflow: hidden; text-overflow: ellipsis; }
+.header-menu > summary { list-style: none; cursor: pointer; border: 1px solid #aebbc5; border-radius: 6px; }
+.header-menu > summary::-webkit-details-marker { display: none; }
+.header-menu-panel, .runtime-panel { position: absolute; top: calc(100% + .35rem); left: 0; z-index: 30; display: grid; gap: .4rem; width: max-content; min-width: 12rem; max-width: min(24rem, calc(100vw - 1.5rem)); max-height: 65dvh; overflow: auto; padding: .65rem; border: 1px solid var(--line); border-radius: 8px; background: #fff; box-shadow: 0 8px 24px rgb(19 43 53 / 18%); }
+.runtime-panel[hidden] { display: none; }
+.header-menu-panel button, .header-menu-panel .file-button { width: 100%; text-align: left; }
+.runtime-panel label { display: grid; gap: .25rem; }
+.runtime-panel select { width: 100%; max-width: none; }
+.view-tabs { gap: .25rem; padding: .2rem .75rem .3rem; flex-wrap: wrap; overflow: visible; }
+.view-tabs button { padding: .2rem .5rem; font-size: .72rem; }
+.board-info > summary { min-height: 1.8rem; padding: .2rem .65rem; font-size: .75rem; }
+.onboarding { width: min(54rem, calc(100vw - 2rem)); max-height: 85dvh; overflow: auto; margin: auto; padding: 1rem; border: 1px solid var(--line); border-radius: 10px; }
+.onboarding::backdrop { background: rgb(3 20 25 / 50%); }
+.onboarding h1 { margin: 0 0 .4rem; font-size: 1.2rem; }
+.repository-next { position: fixed; right: .75rem; bottom: .75rem; z-index: 40; width: min(25rem, calc(100vw - 1.5rem)); max-height: 50dvh; overflow: auto; margin: 0; }
+.repository-next.is-open { margin: 0; padding: .65rem; border-width: 1px; }
+.repository-next .motion-region-inner { display: block; }
+.repository-next ol { display: grid; margin-top: .5rem; }
+.repository-next [data-dismiss-export] { float: right; padding: 0 .35rem; margin-left: .5rem; }
+.global-drop-hint { display: none; position: fixed; inset: 0; z-index: 100; align-items: center; justify-content: center; padding: 1rem; background: rgb(0 109 119 / 18%); border: 4px dashed var(--accent); color: var(--accent); font-size: 1.3rem; font-weight: 800; pointer-events: none; }
+body.dragging .global-drop-hint { display: flex; }
+@media (max-width: 760px) {
+ .app-header.toolbar .board-picker { flex: 1 1 14rem; max-width: none; }
+ .app-header.toolbar .header-controls { margin-left: auto; }
+ .runtime-toggle span { max-width: 5rem; }
+}
+
+/* All page content remains mounted under one delegated editing root. */
+.workspace { display: grid; grid-template-columns: 13rem minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); gap: .6rem; }
+.workspace.nav-collapsed { grid-template-columns: 2.8rem minmax(0, 1fr); }
+.page-nav { display: flex; flex-direction: column; min-width: 0; min-height: 0; overflow: hidden; border: 1px solid var(--line); border-radius: 8px; background: var(--paper); }
+.nav-heading { display: flex; align-items: center; justify-content: space-between; gap: .35rem; padding: .4rem; flex: none; font-size: .75rem; }
+.nav-heading button { padding: .25rem .4rem; }
+#page-nav-items { flex: 1 1 auto; min-height: 0; overflow: auto; padding: .25rem; }
+#page-nav-items[hidden] { display: none; }
+.page-nav .nav-item, .nav-validation { display: flex; align-items: center; justify-content: space-between; gap: .4rem; width: 100%; padding: .5rem .45rem; background: transparent; color: var(--ink); border: 0; border-radius: 5px; text-align: left; font-size: .76rem; }
+.nav-item span { min-width: 0; overflow-wrap: anywhere; }
+.nav-item small { flex: none; color: var(--muted); }
+.page-nav [aria-current="page"] { background: var(--accent-soft); color: var(--accent); font-weight: 800; }
+.nav-connector-heading { display: flex; align-items: center; }
+.nav-connector-heading > .nav-item { flex: 1; }
+.nav-connector-heading > button.secondary { flex: none; padding: .25rem; }
+.nav-children { margin-left: .55rem; padding-left: .25rem; border-left: 2px solid var(--line); }
+.nav-children[hidden] { display: none; }
+.nav-children .nav-item { font-family: ui-monospace, monospace; font-size: .72rem; }
+.nav-validation { flex: none; border-top: 1px solid var(--line); border-radius: 0; }
+.nav-collapsed .nav-heading strong, .nav-collapsed .nav-validation > span { display: none; }
+.nav-collapsed .nav-heading, .nav-collapsed .nav-validation { justify-content: center; }
+.page-area { display: flex; flex-direction: column; min-width: 0; min-height: 0; overflow: hidden; }
+.page-heading { display: flex; flex: none; align-items: center; gap: .5rem; min-height: 2rem; margin-bottom: .35rem; }
+.page-heading h2 { margin: 0; font-size: .9rem; overflow-wrap: anywhere; }
+.page-heading button { padding: .25rem .45rem; font-size: .72rem; }
+.page-host { flex: 1 1 auto; min-width: 0; min-height: 0; overflow: auto; }
+.configuration-pages, .related-pages, #related-list { display: contents; }
+[data-page][hidden] { display: none !important; }
+.page-host > .gpio-card { height: 100%; min-height: 0; }
+.page-host > .board-info > strong { display: block; padding: .6rem; font-size: .8rem; }
+.page-host > .connector-card { display: flex; flex-direction: column; height: 100%; min-height: 0; overflow: hidden; }
+.connector-heading { flex: none; display: flex; align-items: center; flex-wrap: wrap; gap: .45rem; }
+.page-host > .connector-card > .sidebar-section-body { display: flex; flex: 1 1 auto; flex-direction: column; min-height: 0; overflow: hidden; }
+.connector-card .section-description { flex: none; }
+.connector-card .connector-pinout { flex: 1 1 auto; min-height: 0; overflow: auto; }
+.page-host .revision-table-wrap, .page-host .owner-table-wrap { max-height: none; }
+@media (max-width: 980px) {
+ .workspace, .workspace.nav-collapsed { display: flex; flex-direction: column; overflow: hidden; }
+ .page-nav { flex: none; }
+ #page-nav-items { max-height: 30dvh; }
+ .page-area { flex: 1 1 auto; min-height: 0; }
+ .workspace.nav-collapsed .nav-heading strong, .workspace.nav-collapsed .nav-validation > span { display: inline; }
+ .workspace.nav-collapsed .nav-heading, .workspace.nav-collapsed .nav-validation { justify-content: space-between; }
+ .page-host > .gpio-card { flex: none; }
+}
+
+/* The page heading supplies the title; card headings retain only tools and references. */
+.page-host .config-group > .section-heading { display: none; }
+.page-host .gpio-card > .section-heading { padding-block: .35rem; }
+.page-host .connector-heading { padding-block: .35rem; }
+.page-host .connector-heading:not(:has(a)) { display: none; }
+
+.connector-set { display: flex; align-items: flex-start; gap: 1rem; min-width: min-content; }
+.connector-socket { flex: 1 0 18rem; min-width: 0; }
+.connector-set:not(.grouped) .connector-socket { flex-basis: 100%; }
+.connector-socket h3 { margin: .3rem 0; display: flex; flex-wrap: wrap; gap: .6rem; align-items: center; }
+.first-pin-mark { border-left: .3rem solid var(--accent); padding-left: .4rem; font-size: .75rem; }
+
+
+/* Paired sockets share the heading, evidence and pinout rows. */
+.connector-set.grouped {
+ display: grid;
+ grid-template-rows: auto auto auto;
+ grid-auto-flow: column;
+ grid-auto-columns: minmax(18rem, 1fr);
+ align-items: start;
+}
+.connector-set.grouped > .connector-socket {
+ display: grid;
+ grid-row: 1 / 4;
+ grid-template-rows: subgrid;
+ align-self: stretch;
+}
+.connector-set.grouped > .connector-socket > table { align-self: start; }
+@media (max-width: 980px) {
+ .connector-set.grouped { display: flex; flex-direction: column; min-width: 0; }
+ .connector-set.grouped > .connector-socket { display: block; flex: none; width: 100%; }
}
+.connector-function-roles { white-space: normal; }
+#connector-role-color-legend { flex: none; }
+
+/* Compact columns take their unwrapped content width; function columns share the rest. */
+.connector-pin-table col.pin-col-terminal, .connector-pin-table col.pin-col-endpoint { width: 1%; }
+.connector-pin-table col.pin-col-function { width: 50%; }
+.connector-pin-table.single-column col.pin-col-function { width: 100%; }
+.connector-pin-table th { white-space: nowrap; }
+
+.connector-pin-table .pin-left .connector-function-roles { justify-content: flex-end; text-align: right; }
+
+.connector-pin-table .pin-endpoint { text-align: center; }
+
+.connector-pin-table col.pin-col-wire, .connector-pin-table .pin-wire { width: 1%; white-space: nowrap; text-align: center; }
+.connector-pin-table .pin-wire .connector-color { margin-right: 0; }
+
+.page-heading #connector-undo { margin-left: auto; flex: none; }
+
-
-