diff --git a/docs/specs/alert.md b/docs/specs/alert.md index e0841cd9b..3cf328b7c 100644 --- a/docs/specs/alert.md +++ b/docs/specs/alert.md @@ -433,7 +433,7 @@ The Settings dialog's Notifications topic is this spec's; Network's content is ` - **Must show only application-wide settings**, excluding Workspace overrides. - A baseboard alarm button toggles only its own setting, as an override for that Workspace; components without a Workspace scope edit application defaults. **Must open a separate Workspace alert dialog from either button's right-click or focused `Shift+F10`/`ContextMenu`**, only with a Workspace scope, preserving per-field inheritance and reset-all. - The watched-command list removes rules and cannot add one — creating a rule stays the terminal context of a Pane running the command. It is the only place a rule set on a since-closed Pane can be removed. -- **The push group's device line must name every device a push would reach**, and otherwise say why there is none. +- **The push group's device line must name every device a push would reach**, and otherwise say why there is none; a refusing Relay's reason comes from the Burrow's status (`relayRefuses`), which the device list cannot tell, with the plans where the account has none. - **The Managed voice group is shown wherever the adapter has `managedVoice`**, which a self-host build's omits; there the speech row names managed voice instead of linking the `/hosted` preview ([website-docs.md](./website-docs.md) -> `/hosted`). **Must state what is sent to Hosted, and that ElevenLabs' copy has no guaranteed deletion time, before the enrollment that enables it.** Managed voice has its own approval and Disconnect, independent of remote control; under Nowhere it explains and links Network instead, changing no policy. Enabled, it offers the curated voices and Disconnect, or, where speak says the plan lapsed, the plans. **No control takes a token, and no copy names one.** - Each sink carries a test control, usable while the sink is off: the test sound goes through the shared speech queue and selected voice, reporting admission or an unavailable backend, never Session delivery state; the test push takes the real Burrow→ACL→Relay path and distinguishes no targets, zero, partial, and full delivery, hidden without a Burrow service. diff --git a/lib/src/components/HostedEnrollment.tsx b/lib/src/components/HostedEnrollment.tsx index 3637af7d6..a1ff805d2 100644 --- a/lib/src/components/HostedEnrollment.tsx +++ b/lib/src/components/HostedEnrollment.tsx @@ -13,6 +13,11 @@ export const HOSTED_PRICING_URL = 'https://dormouse.sh/hosted/#pricing'; /** The Hosted page's voice section, the spoken-alarm row's link where the build has no managed voice. */ export const HOSTED_VOICE_URL = 'https://dormouse.sh/hosted/#voice'; +/** The link to {@link HOSTED_PRICING_URL}, the one way every no-plan line names the plans. */ +export function HostedPlansLink() { + return See Hosted plans; +} + /** The one field the offer card and the typed form both ask for. */ export function BurrowNameField({ value, @@ -114,7 +119,7 @@ export function HostedEnrollmentEnded({ {ended.reason === 'failed' && ended.message ?
{ended.message}
: null} {ended.reason === 'not-entitled' ? (
- See Hosted plans +
) : null} {page ? ( diff --git a/lib/src/components/ManagedVoiceSection.tsx b/lib/src/components/ManagedVoiceSection.tsx index 1487edc4b..873da592f 100644 --- a/lib/src/components/ManagedVoiceSection.tsx +++ b/lib/src/components/ManagedVoiceSection.tsx @@ -1,7 +1,6 @@ import { useCallback, useState, useSyncExternalStore } from 'react'; import { INLINE_ACTION_CLASS, SELECT_CLASS, modalActionButton } from './design'; -import { ExternalTextLink } from './ExternalTextLink'; -import { HOSTED_PRICING_URL, HostedApprovalProgress } from './HostedEnrollment'; +import { HostedApprovalProgress, HostedPlansLink } from './HostedEnrollment'; import { FIELD_LABEL, useBusyAction, useNetworkPolicy } from './remote-control-shared'; import { getPlatform } from '../lib/platform'; import { @@ -82,7 +81,7 @@ export function ManagedVoiceSection({ onShowNetwork }: { onShowNetwork?: () => v <>
{voice.notEntitled ? ( - <>{NO_PLAN_COPY} See Hosted plans + <>{NO_PLAN_COPY} ) : 'Natural Voice is on.'}