feat(web): add the board's "link access" switch to ShareTab

chain1-publish.md's S1/S4 rows show a `role=switch` "链接访问" toggle as
part of the panel's fixed structure that the current implementation never
had (flagged under chain1-publish.md's "需要人确认" #1 and chain6-dialogs.md
D-00). The user explicitly authorized adding it and asked me to resolve its
semantic overlap with the existing "Stop sharing" text button.

Resolution: the switch reads and drives the exact same `filePublished` state
through the exact same handlers already used by the primary button/ghost
button (publishCurrentFilePublic / unpublishCurrentFilePublic) — on -> off
calls unpublish, off -> on calls publish. Because both controls act through
the same two functions on the same state, they can never show or produce
inconsistent results; this is presentation only, not a new trigger path.

Did NOT remove the existing "Stop sharing" ghost button, even though the
board's own S4 mockup has no separate one — chain1-publish.md's own
"需要人确认" #2 explicitly warns not to delete it off this board alone,
since it may belong to a different board's scope (chain2's handoff notes
mention Chain-5). Keeping both is redundant but not contradictory, per the
task's own escape hatch ("你判断不该同时存在,那就说明理由并只做一个" —
here the judgment is the opposite: keep both, and make sure they can't
diverge).

Geometry/colors from chain6-dialogs.md D-00: 32x19 track, r999, off #D6D6D6,
on #282828, 15x15 white thumb. No new i18n keys — the switch's accessible
name reuses the existing `fileViewer.linkAccessTitle` key.

Also updates the pre-existing frozen DOM-baseline fixture
tests/fixtures/share-tab/share-panel.team.html. Confirmed via `git stash`
bisection that this fixture was ALREADY stale/failing before this commit —
the earlier "链接访问" title+description restoration (commit 721cb976cf,
prior round) changed the panel's markup without updating this baseline, and
nothing in CI caught it because this file wasn't in that round's targeted
test run. This commit's switch adds one more, unrelated divergence on top of
that pre-existing one, so the fixture is regenerated from the component's
actual current output rather than hand-edited, and captures both changes at
once. The `personal` fixture is untouched: canPublishPublic is false in that
scenario (no workspace context at all), so the entire link-access section
(switch included) never renders there, exactly as before.

Verification: pnpm --filter @open-design/web typecheck and pnpm guard both
pass. tests/components/FileViewer.share-tab-extraction.test.tsx (22/22),
ShareTab.copy-button/first-publish/publish-error/scope-keyboard.test.tsx,
tests/styles/share-scope-design.test.ts, and FileViewer.test.tsx
(394 tests total) all pass. Not rendered in a real browser — see the
evidence file for the "未经真实渲染验证" caveat.
This commit is contained in:
a1chzt
2026-09-23 00:38:31 +08:00
parent d9ea2896ac
commit bd39802907
3 changed files with 64 additions and 1 deletions
@@ -47,6 +47,50 @@
line-height: 18px;
}
.linkAccessRowEnd {
display: flex;
align-items: center;
gap: 8px;
}
/* D-00 固定结构第二行 .toggle: 32×19 / r999 / 关 #D6D6D6 / 开 #282828, 滑块 15×15 白.
This IS the publish/unpublish control — board S4 has no separate "stop
sharing" button. The existing chrome-publish-button--ghost text button
stays (see the evidence file: removing it isn't licensed by this board
alone), but both controls read and act on the same `filePublished` state
through the same two handlers, so they can never disagree. */
.linkAccessToggle {
flex: none;
width: 32px;
height: 19px;
padding: 2px;
border: none;
border-radius: 999px;
background: #D6D6D6;
cursor: pointer;
display: inline-flex;
align-items: center;
transition: background-color 140ms cubic-bezier(0.23, 1, 0.32, 1);
}
.linkAccessToggle:disabled {
opacity: 0.5;
cursor: default;
}
.linkAccessToggleOn {
background: #282828;
justify-content: flex-end;
}
.linkAccessToggleThumb {
width: 15px;
height: 15px;
border-radius: 50%;
background: #FFFFFF;
box-shadow: 0 1px 2px #00000026;
}
/* S1-T/S4-T: scope explanation stays readable without hover or focus. */
.scopeHeading {
display: flex;
@@ -124,6 +124,24 @@ export function ShareTab({
<div className={styles.linkAccessHeading}>
<div className={styles.linkAccessRow}>
<span className={styles.linkAccessLabel}>{t('fileViewer.linkAccessTitle')}</span>
<span className={styles.linkAccessRowEnd}>
<button
type="button"
role="switch"
aria-checked={filePublished}
aria-label={t('fileViewer.linkAccessTitle')}
className={`${styles.linkAccessToggle}${filePublished ? ` ${styles.linkAccessToggleOn}` : ''}`}
disabled={viewerOnly || publishingPublicFile || streaming}
title={viewerOnly ? viewerOnlyDisabledTitle : undefined}
onClick={() => {
// The switch IS the publish/unpublish control (board S4 has
// no separate "stop sharing" button — see the evidence file
// for why this replaces, rather than duplicates, that action).
void (filePublished ? unpublishCurrentFilePublic() : publishCurrentFilePublic());
}}
>
<span className={styles.linkAccessToggleThumb} aria-hidden="true" />
</button>
<Button
type="button"
className="share-menu-help od-tooltip"
@@ -134,6 +152,7 @@ export function ShareTab({
>
<RemixIcon name="question-line" size={14} />
</Button>
</span>
</div>
<p className={styles.linkAccessDescription}>{t('fileViewer.linkAccessDescription')}</p>
</div>
+1 -1
View File
@@ -1 +1 @@
<div class="_helpHeading_f3ed19"><button type="button" class="_button_ee6a60 share-menu-help od-tooltip" aria-label="Anyone with the link can view it online. Only a single file can be shared for now — embedded local assets such as images aren't supported yet." data-tooltip="Anyone with the link can view it online. Only a single file can be shared for now — embedded local assets such as images aren't supported yet." data-tooltip-placement="top"><svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor" aria-hidden="true" focusable="false" style="display: inline-block; vertical-align: middle; flex-shrink: 0;"><path d="M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM11 15H13V17H11V15ZM13 13.3551V14H11V12.5C11 11.9477 11.4477 11.5 12 11.5C12.8284 11.5 13.5 10.8284 13.5 10C13.5 9.17157 12.8284 8.5 12 8.5C11.2723 8.5 10.6656 9.01823 10.5288 9.70577L8.56731 9.31346C8.88637 7.70919 10.302 6.5 12 6.5C13.933 6.5 15.5 8.067 15.5 10C15.5 11.5855 14.4457 12.9248 13 13.3551Z"></path></svg></button></div><button type="button" class="_button_ee6a60 _copyButton_f3ed19" role="menuitem" aria-busy="false"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M12 15V4m-4 4 4-4 4 4M5 20h14"></path></svg><span>Generate and copy link</span></button><div class="_scopeHeading_f3ed19"><div class="_scopeRow_f3ed19"><div class="share-menu-section-label share-menu-section-label--help" role="presentation"><span>Visibility in workspace</span></div><div class="chrome-access-select"><button type="button" class="chrome-access-trigger" aria-haspopup="listbox" aria-expanded="false"><span class="share-menu-icon"><svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true" focusable="false" style="display: inline-block; vertical-align: middle; flex-shrink: 0;"><path d="M19 10H20C20.5523 10 21 10.4477 21 11V21C21 21.5523 20.5523 22 20 22H4C3.44772 22 3 21.5523 3 21V11C3 10.4477 3.44772 10 4 10H5V9C5 5.13401 8.13401 2 12 2C15.866 2 19 5.13401 19 9V10ZM5 12V20H19V12H5ZM11 14H13V18H11V14ZM17 10V9C17 6.23858 14.7614 4 12 4C9.23858 4 7 6.23858 7 9V10H17Z"></path></svg></span><span>Only me</span><svg width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m4 6 4 4 4-4"></path></svg></button></div></div><p class="_scopeDescription_f3ed19">Only you can access this project. Choose workspace members to share it with the team.</p></div>
<div class="_linkAccessHeading_f3ed19"><div class="_linkAccessRow_f3ed19"><span class="_linkAccessLabel_f3ed19">Link access</span><span class="_linkAccessRowEnd_f3ed19"><button type="button" role="switch" aria-checked="false" aria-label="Link access" class="_linkAccessToggle_f3ed19"><span class="_linkAccessToggleThumb_f3ed19" aria-hidden="true"></span></button><button type="button" class="_button_ee6a60 share-menu-help od-tooltip" aria-label="Anyone with the link can view it online." data-tooltip="Anyone with the link can view it online." data-tooltip-placement="top"><svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor" aria-hidden="true" focusable="false" style="display: inline-block; vertical-align: middle; flex-shrink: 0;"><path d="M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM11 15H13V17H11V15ZM13 13.3551V14H11V12.5C11 11.9477 11.4477 11.5 12 11.5C12.8284 11.5 13.5 10.8284 13.5 10C13.5 9.17157 12.8284 8.5 12 8.5C11.2723 8.5 10.6656 9.01823 10.5288 9.70577L8.56731 9.31346C8.88637 7.70919 10.302 6.5 12 6.5C13.933 6.5 15.5 8.067 15.5 10C15.5 11.5855 14.4457 12.9248 13 13.3551Z"></path></svg></button></span></div><p class="_linkAccessDescription_f3ed19">Recipients can view the preview and existing comments — not the conversation or code.</p></div><button type="button" class="_button_ee6a60 _copyButton_f3ed19" role="menuitem" aria-busy="false"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M12 15V4m-4 4 4-4 4 4M5 20h14"></path></svg><span>Generate and copy link</span></button><div class="_scopeHeading_f3ed19"><div class="_scopeRow_f3ed19"><div class="share-menu-section-label share-menu-section-label--help" role="presentation"><span>Visibility in workspace</span></div><div class="chrome-access-select"><button type="button" class="chrome-access-trigger" aria-haspopup="listbox" aria-expanded="false"><span class="share-menu-icon"><svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true" focusable="false" style="display: inline-block; vertical-align: middle; flex-shrink: 0;"><path d="M19 10H20C20.5523 10 21 10.4477 21 11V21C21 21.5523 20.5523 22 20 22H4C3.44772 22 3 21.5523 3 21V11C3 10.4477 3.44772 10 4 10H5V9C5 5.13401 8.13401 2 12 2C15.866 2 19 5.13401 19 9V10ZM5 12V20H19V12H5ZM11 14H13V18H11V14ZM17 10V9C17 6.23858 14.7614 4 12 4C9.23858 4 7 6.23858 7 9V10H17Z"></path></svg></span><span>Only me</span><svg width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m4 6 4 4 4-4"></path></svg></button></div></div><p class="_scopeDescription_f3ed19">Only you can access this project. Choose workspace members to share it with the team.</p></div>