dsh-offpeak 3.1.6 → 3.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["useState","useRef","POLL_MS","useState","useCallback","useState","useState"],"sources":["../src/client/badge-text.ts","../src/client/free-run-button-text.ts","../src/client/date-range.ts","../src/client/date-range-picker.tsx","../src/client/styles.ts","../src/client/free-run-button.tsx","../src/client/model-list.ts","../src/client/model-panel.tsx","../src/client/status-badge.tsx","../src/client/index.ts"],"sourcesContent":["/**\r\n * dsh-offpeak — 状态徽标文案(纯函数,零依赖,可单测)。\r\n *\r\n * 分离出来的原因:徽标语义(高峰的「无任务 / 拦截中 / 畅跑中」三态、谷时/周末)需要单测,\r\n * 而组件文件是 TSX(含 React 运行时)。这里只有字符串逻辑。\r\n */\r\n\r\n/** /session-guard/status 返回的全局阶段(组件与测试共用)。 */\r\nexport interface Status {\r\n phase: 'peak' | 'off-peak' | 'weekend'\r\n enabled: boolean\r\n weekendMode: boolean\r\n /** 官方源二维判定是否开启(开启时高峰期只拦官方源)。 */\r\n providerGuard?: boolean\r\n /** 拦截列表里的模型数(> 0 = 有拦截任务)。 */\r\n modelPatterns?: number\r\n /** 当前被挂起的请求数(hold 模式)。 */\r\n held?: number\r\n /** error 模式记下的延后会话数。 */\r\n deferred?: number\r\n /** 当前被 step 门挂起的会话数(v0.2.0)。 */\r\n stepHeld?: number\r\n timezone: string\r\n}\r\n\r\n/** 阶段文案。 */\r\nexport const PHASE_LABELS = {\r\n peak: '高峰',\r\n 'off-peak': '谷时',\r\n weekend: '周末',\r\n} as const\r\n\r\n/** 徽标要表达的任务态(高峰期的三种文案)。 */\r\nexport interface BadgeTasks {\r\n /** 拦截列表里有模型(= 有拦截任务)。 */\r\n intercepting?: boolean\r\n /** 本会话有畅跑任务。 */\r\n freeRun?: boolean\r\n}\r\n\r\n/**\r\n * 徽标文案(v3.1.6):\r\n * - 非高峰 → 阶段文案(谷时 / 周末);\r\n * - 高峰 + 有畅跑任务 → `高峰 · 畅跑中`;\r\n * - 高峰 + 有拦截任务 → `高峰 · 拦截中`;\r\n * - 高峰 + 都没任务 → `高峰`。\r\n *\r\n * 畅跑优先于拦截:畅跑是「本会话无视峰谷照常跑」,比「有拦截规则」更直接地描述此刻状态。\r\n */\r\nexport function peakLabel(status: Pick<Status, 'phase'>, tasks: BadgeTasks = {}): string {\r\n if (status.phase !== 'peak') return PHASE_LABELS[status.phase]\r\n if (tasks.freeRun === true) return '高峰 · 畅跑中'\r\n if (tasks.intercepting === true) return '高峰 · 拦截中'\r\n return '高峰'\r\n}\r\n\r\n/**\r\n * 徽标的 CSS 类:**每个阶段都是同一个按钮**(`sg-status sg-status-btn` + 配色变体)。\r\n *\r\n * 这条不变量要钉住:高峰(拦截)与谷时/周末(放行)**只有文案与颜色变**,交互形态一致 ——\r\n * 都是可点击按钮,点开同一个「高峰拦截列表」面板,绝不因为不是高峰就退化成不可点的展示元素。\r\n */\r\nexport function badgeClassNames(status: Pick<Status, 'phase'>): string {\r\n const variant = status.phase === 'peak' ? 'sg-peak' : status.phase === 'weekend' ? 'sg-weekend' : 'sg-off'\r\n return `sg-status sg-status-btn ${variant}`\r\n}\r\n\r\n/** 悬浮说明:把判定口径与当前挂起/延后数量写清楚。 */\r\nexport function badgeTitle(status: Status): string {\r\n const base = `${PHASE_LABELS[status.phase]} · ${status.timezone}${status.weekendMode ? ' · 周末模式' : ''}`\r\n if (status.phase !== 'peak') return base\r\n const mode = status.providerGuard === true ? '仅拦截 DeepSeek 官方源' : '全部会话暂停(未启用二维判定)'\r\n const held = status.held ?? 0\r\n const deferred = status.deferred ?? 0\r\n const stepHeld = status.stepHeld ?? 0\r\n const extra = held > 0 || deferred > 0 || stepHeld > 0\r\n ? ` · 挂起 ${held} · 延后 ${deferred} · step 挂起 ${stepHeld}`\r\n : ''\r\n return `${base} · ${mode}${extra}`\r\n}\r\n","/**\r\n * dsh-offpeak — 「畅跑」按钮文案与状态投影(纯函数,零依赖,可单测)。\r\n *\r\n * 交互(用户定案):**点击按钮一律打开任务管理面板**,面板里可以新建 / 暂停 /\r\n * 删除畅跑任务。因此按钮文案是**固定**的「畅跑」(任务数 >1 时带 `×N`),\r\n * 不再用文案表示生效状态——生效状态由按钮高亮色 + 悬浮提示表达,\r\n * 每段的「暂停 / 恢复」在面板里逐行操作。\r\n *\r\n * 这样也解决了上一版的死角:旧版「畅跑中」点击是直接暂停,导致面板在生效期间\r\n * 不可达;现在点击永远是打开面板。\r\n *\r\n * 本文件只有字符串与状态投影逻辑,不含 React;组件在 free-run-button.tsx。\r\n */\r\n\r\n/** 与主机 `FREE_RUN_STATES` 对齐。 */\r\nexport type FreeRunStateName = 'none' | 'scheduled' | 'active' | 'suspended' | 'expired'\r\n\r\n/** 单段状态(含每段暂停)。 */\r\nexport type FreeRunWindowStatus = 'scheduled' | 'active' | 'paused' | 'ended'\r\n\r\n/** 单个畅跑时段(`/session-guard/state` 的 `freeRun.windows[]` 形状)。 */\r\nexport interface FreeRunWindow {\r\n id: string\r\n from: string\r\n to: string\r\n fromInput: string\r\n toInput: string\r\n fromDisplay: string\r\n toDisplay: string\r\n paused: boolean\r\n status: FreeRunWindowStatus\r\n}\r\n\r\n/** `/session-guard/state` 的 `freeRun` 字段。 */\r\nexport interface FreeRunView {\r\n state: FreeRunStateName\r\n /** 当前是否有未暂停的时段正在生效(按钮高亮用)。 */\r\n active: boolean\r\n available: boolean\r\n timezone: string\r\n windows: FreeRunWindow[]\r\n activeId: string | null\r\n msRemaining: number | null\r\n nextStartMs: number | null\r\n nextStartDisplay: string | null\r\n}\r\n\r\n/** 空视图(请求失败 / 未加载时的安全默认)。 */\r\nexport const EMPTY_FREE_RUN: FreeRunView = {\r\n state: 'none',\r\n active: false,\r\n available: false,\r\n timezone: '',\r\n windows: [],\r\n activeId: null,\r\n msRemaining: null,\r\n nextStartMs: null,\r\n nextStartDisplay: null,\r\n}\r\n\r\n/** 按钮文案:固定「畅跑」,多段时带计数。 */\r\nexport function freeRunLabel(view: FreeRunView | null | undefined): string {\r\n const count = view?.windows?.length ?? 0\r\n return count > 1 ? `畅跑 ×${count}` : '畅跑'\r\n}\r\n\r\n/** 悬浮提示:当前是否生效 + 逐条排期(多任务的重要信息出口)。 */\r\nexport function freeRunTitle(view: FreeRunView | null | undefined): string {\r\n const tz = view?.timezone !== undefined && view.timezone !== '' ? view.timezone : '本地时区'\r\n const lines: string[] = ['点击打开畅跑任务管理']\r\n if (view?.active === true) {\r\n const left = remainingText(view?.msRemaining ?? null)\r\n lines.unshift(left === null ? '畅跑生效中' : `畅跑生效中(剩 ${left})`)\r\n } else if ((view?.windows?.length ?? 0) > 0) {\r\n const next = view?.nextStartDisplay ?? null\r\n lines.unshift(next === null ? '畅跑未生效' : `畅跑未生效,下一段 ${next}`)\r\n }\r\n const windows = view?.windows ?? []\r\n if (windows.length > 0) {\r\n lines.push('—— 畅跑任务 ——')\r\n for (const w of windows) {\r\n lines.push(`${w.fromDisplay} – ${w.toDisplay} ${statusLabel(w.status)}`)\r\n }\r\n } else {\r\n lines.push(`还没有畅跑任务(时间按 ${tz})`)\r\n }\r\n return lines.join('\\n')\r\n}\r\n\r\n/** 单段状态标签。 */\r\nexport function statusLabel(status: FreeRunWindowStatus): string {\r\n if (status === 'active') return '进行中'\r\n if (status === 'paused') return '已暂停'\r\n if (status === 'scheduled') return '待开始'\r\n return '已结束'\r\n}\r\n\r\n/** 该行是否显示「暂停」图标(已结束的段无从暂停)。 */\r\nexport function canPause(w: FreeRunWindow): boolean {\r\n return w.status !== 'ended' && w.paused !== true\r\n}\r\n\r\n/** 该行是否显示「恢复」图标。 */\r\nexport function canResume(w: FreeRunWindow): boolean {\r\n return w.status !== 'ended' && w.paused === true\r\n}\r\n\r\n/** 未结束(可操作)的任务。 */\r\nexport function liveWindows(view: FreeRunView | null | undefined): FreeRunWindow[] {\r\n return (view?.windows ?? []).filter((w) => w.status !== 'ended')\r\n}\r\n\r\n/** 是否所有未结束的任务都已被暂停(决定顶部按钮显示「暂停全部任务」还是「恢复全部任务」)。 */\r\nexport function allTasksPaused(view: FreeRunView | null | undefined): boolean {\r\n const live = liveWindows(view)\r\n return live.length > 0 && live.every((w) => w.paused === true)\r\n}\r\n\r\n/** 顶部第二个文本按钮的文案。 */\r\nexport function pauseAllLabel(view: FreeRunView | null | undefined): string {\r\n return allTasksPaused(view) ? '恢复全部任务' : '暂停全部任务'\r\n}\r\n\r\n/** 顶部三个文本按钮是否可用(没有任务时后两个禁用)。 */\r\nexport function toolbarDisabled(view: FreeRunView | null | undefined): boolean {\r\n return liveWindows(view).length === 0\r\n}\r\n\r\n/**\r\n * 图标字形。带 `\\uFE0E`(text presentation)强制按文字渲染,\r\n * 避免 `⏸` 在部分平台被渲染成彩色 emoji。零图标库依赖。\r\n */\r\nexport const ICON_PAUSE = '⏸\\uFE0E'\r\nexport const ICON_RESUME = '▶\\uFE0E'\r\nexport const ICON_DELETE = '×'\r\n\r\n/** 每行暂停 / 恢复图标按钮的悬浮提示。 */\r\nexport function rowPauseTitle(w: FreeRunWindow): string {\r\n return w.paused === true ? '恢复这一段畅跑' : '暂停这一段畅跑(其他段不受影响)'\r\n}\r\n\r\n/** 每行删除图标按钮的悬浮提示。 */\r\nexport function rowDeleteTitle(): string {\r\n return '删除这一段畅跑'\r\n}\r\n\r\n/** 剩余时长文案(中文,粗粒度)。 */\r\nexport function remainingText(ms: number | null): string | null {\r\n if (ms === null || !Number.isFinite(ms) || ms <= 0) return null\r\n const totalMinutes = Math.ceil(ms / 60_000)\r\n if (totalMinutes < 60) return `${totalMinutes} 分钟`\r\n const hours = Math.floor(totalMinutes / 60)\r\n const minutes = totalMinutes % 60\r\n return minutes === 0 ? `${hours} 小时` : `${hours} 小时 ${minutes} 分`\r\n}\r\n\r\n/** 从 `/session-guard/state` 响应体里取出 `freeRun`(形状异常 → 安全空视图)。 */\r\nexport function readFreeRun(body: unknown): FreeRunView {\r\n const b = (body ?? {}) as { ok?: unknown; freeRun?: unknown }\r\n if (b.ok !== true) return EMPTY_FREE_RUN\r\n const f = b.freeRun\r\n if (f === null || typeof f !== 'object') return EMPTY_FREE_RUN\r\n const raw = f as Partial<FreeRunView>\r\n const windows = Array.isArray(raw.windows)\r\n ? (raw.windows.filter((w) => w !== null && typeof w === 'object') as FreeRunWindow[])\r\n : []\r\n return {\r\n state: typeof raw.state === 'string' ? (raw.state as FreeRunStateName) : 'none',\r\n active: raw.active === true,\r\n available: raw.available === true,\r\n timezone: typeof raw.timezone === 'string' ? raw.timezone : '',\r\n windows,\r\n activeId: typeof raw.activeId === 'string' ? raw.activeId : null,\r\n msRemaining: typeof raw.msRemaining === 'number' ? raw.msRemaining : null,\r\n nextStartMs: typeof raw.nextStartMs === 'number' ? raw.nextStartMs : null,\r\n nextStartDisplay: typeof raw.nextStartDisplay === 'string' ? raw.nextStartDisplay : null,\r\n }\r\n}\r\n","/**\r\n * dsh-offpeak — 日期范围选择的纯逻辑(零依赖,可单测)。\r\n *\r\n * 供 `date-range-picker.tsx` 使用:双月日历的网格、范围高亮、默认值播种。\r\n * 全部按**本地日期**(年/月/日)运算,不做时区换算 —— 选出来的字符串是要交给\r\n * 主机按配置时区解释的墙钟日期。\r\n *\r\n * 约定:一周从**周一**开始(与 DSH 排期面板一致:一 二 三 四 五 六 日)。\r\n */\r\n\r\n/** `YYYY-MM-DD` 形态。 */\r\nexport interface Ymd {\r\n y: number\r\n m: number // 1-12\r\n d: number // 1-31\r\n}\r\n\r\nconst WEEKDAY_LABELS = ['一', '二', '三', '四', '五', '六', '日']\r\n\r\n/** 周一到周日的表头。 */\r\nexport function weekdayLabels(): string[] {\r\n return [...WEEKDAY_LABELS]\r\n}\r\n\r\n/** 两位补零。 */\r\nfunction pad2(n: number): string {\r\n return String(n).padStart(2, '0')\r\n}\r\n\r\n/** `Ymd` → `YYYY-MM-DD`。 */\r\nexport function toIso({ y, m, d }: Ymd): string {\r\n return `${y}-${pad2(m)}-${pad2(d)}`\r\n}\r\n\r\n/** `YYYY-MM-DD` → `Ymd`;非法返回 null。 */\r\nexport function fromIso(value: string | null | undefined): Ymd | null {\r\n const m = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(typeof value === 'string' ? value : '')\r\n if (m === null) return null\r\n const y = Number(m[1])\r\n const mo = Number(m[2])\r\n const d = Number(m[3])\r\n if (mo < 1 || mo > 12 || d < 1 || d > 31) return null\r\n return { y, m: mo, d }\r\n}\r\n\r\n/** 该月天数(用「下个月 0 号」拿到,天然处理闰年)。 */\r\nexport function daysInMonth(y: number, m: number): number {\r\n return new Date(y, m, 0).getDate()\r\n}\r\n\r\n/** 该月 1 号是周几(0=周日…6=周六)。 */\r\nfunction firstWeekday(y: number, m: number): number {\r\n return new Date(y, m - 1, 1).getDay()\r\n}\r\n\r\n/** 月份偏移(可跨年)。 */\r\nexport function addMonths({ y, m }: Pick<Ymd, 'y' | 'm'>, delta: number): { y: number; m: number } {\r\n const total = y * 12 + (m - 1) + delta\r\n return { y: Math.floor(total / 12), m: (total % 12) + 1 }\r\n}\r\n\r\n/**\r\n * 该月的日历网格:周一开头,长度固定为 7 的倍数(可能 5 或 6 周)。\r\n * 非本月的格子用 `inMonth: false` 标出(界面上淡显)。\r\n */\r\nexport function monthGrid(y: number, m: number): Array<{ day: number; iso: string; inMonth: boolean }> {\r\n const lead = (firstWeekday(y, m) + 6) % 7 // 周一=0\r\n const total = daysInMonth(y, m)\r\n const cells: Array<{ day: number; iso: string; inMonth: boolean }> = []\r\n // 上月补位\r\n const prev = addMonths({ y, m }, -1)\r\n const prevTotal = daysInMonth(prev.y, prev.m)\r\n for (let i = lead - 1; i >= 0; i -= 1) {\r\n const d = prevTotal - i\r\n cells.push({ day: d, iso: toIso({ y: prev.y, m: prev.m, d }), inMonth: false })\r\n }\r\n for (let d = 1; d <= total; d += 1) cells.push({ day: d, iso: toIso({ y, m, d }), inMonth: true })\r\n // 下月补位到整周\r\n const next = addMonths({ y, m }, 1)\r\n let d = 1\r\n while (cells.length % 7 !== 0) {\r\n cells.push({ day: d, iso: toIso({ y: next.y, m: next.m, d }), inMonth: false })\r\n d += 1\r\n }\r\n return cells\r\n}\r\n\r\n/** 月份标题,如 `2026年 9月`。 */\r\nexport function monthTitle(y: number, m: number): string {\r\n return `${y}年 ${m}月`\r\n}\r\n\r\n/** 日期先后比较:a<b → -1,相等 → 0,a>b → 1(字符串直接比即可,形态定长)。 */\r\nexport function compareIso(a: string, b: string): number {\r\n if (a === b) return 0\r\n return a < b ? -1 : 1\r\n}\r\n\r\n/** 该日是否落在 `[from, to]` 闭区间内(任一端为空则不算命中)。 */\r\nexport function inRange(iso: string, from: string, to: string): boolean {\r\n if (from === '' || to === '') return false\r\n const [lo, hi] = compareIso(from, to) <= 0 ? [from, to] : [to, from]\r\n return compareIso(iso, lo) >= 0 && compareIso(iso, hi) <= 0\r\n}\r\n\r\n/** 是否区间的端点。 */\r\nexport function isEdge(iso: string, from: string, to: string): boolean {\r\n return iso === from || iso === to\r\n}\r\n\r\n/**\r\n * 小时选择的语义:**按小时包含**。\r\n *\r\n * - **开始**小时 H → `H:00`(从这一小时的开头开始);\r\n * - **结束**小时 H → `H:59`(把这一小时**整个包含**进去)。\r\n *\r\n * 为什么结束取 `:59` 而不是 `:00`:主机侧的窗口是半开的 `[from, to)`。若结束也取 `:00`,\r\n * 选「23 时」就会得到 `23:00`,当天最后一小时(23:00–24:00)根本选不进来;取 `:59`\r\n * 后「开始 12 时 → 结束 14 时」= `12:00 → 14:59`,正好覆盖 12、13、14 三个小时,\r\n * 而「结束 23 时」= `23:59`,即当天最后一刻。\r\n */\r\nexport const END_MINUTE = 59\r\n\r\n/** 把「日期 + 小时」拼成主机要的墙钟串(按配置时区解释):开始 = `H:00`。 */\r\nexport function combineStart(date: string, hour: string): string {\r\n return `${date}T${pad2(Number(hour))}:00`\r\n}\r\n\r\n/** 把「日期 + 小时」拼成主机要的墙钟串:结束 = `H:59`(含该小时)。 */\r\nexport function combineEnd(date: string, hour: string): string {\r\n return `${date}T${pad2(Number(hour))}:${pad2(END_MINUTE)}`\r\n}\r\n\r\n/**\r\n * 默认区间:**开始 = 当前时刻**(本小时整点),结束 = 下一个小时(按上面的「含该小时」\r\n * 语义即覆盖 2 个自然小时;跨天/跨月/跨年会顺延到下一天)。\r\n *\r\n * 与用户要求一致:「默认开始时间是当前时刻」。返回 `YYYY-MM-DD` 与 `HH` 四段,\r\n * 直接喂给日历 + 小时下拉。\r\n */\r\nexport function seedRange(now: Date): { fromDate: string; fromHour: string; toDate: string; toHour: string } {\r\n const start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), 0, 0, 0)\r\n // 结束小时 = 开始小时 + 1(显示用的小时;配合 `:59` 语义 = 覆盖开始那一小时与下一小时)。\r\n const end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours() + 1, 0, 0, 0)\r\n return {\r\n fromDate: toIso({ y: start.getFullYear(), m: start.getMonth() + 1, d: start.getDate() }),\r\n fromHour: pad2(start.getHours()),\r\n toDate: toIso({ y: end.getFullYear(), m: end.getMonth() + 1, d: end.getDate() }),\r\n toHour: pad2(end.getHours()),\r\n }\r\n}\r\n\r\n/**\r\n * 一次点击后的范围状态机:\r\n * - 还没选起点,或已有完整区间 → 该点成为**新起点**,终点清空(等待第二次点击);\r\n * - 已有起点、没有终点 → 该点成为终点;若它在起点之前,则两者对调(允许反着选)。\r\n */\r\nexport function nextRange(\r\n from: string,\r\n to: string,\r\n picked: string,\r\n): { from: string; to: string; done: boolean } {\r\n if (from === '' || to !== '') return { from: picked, to: '', done: false }\r\n return compareIso(picked, from) >= 0\r\n ? { from, to: picked, done: true }\r\n : { from: picked, to: from, done: true }\r\n}\r\n\r\n/** 触发器上显示的区间文案(未选满时给占位)。 */\r\nexport function rangeLabel(from: string, to: string): { text: string; placeholder: boolean } {\r\n if (from !== '' && to !== '') return { text: `${from} → ${to}`, placeholder: false }\r\n if (from !== '') return { text: `${from} → 结束日期`, placeholder: true }\r\n return { text: '开始日期 → 结束日期', placeholder: true }\r\n}\r\n","/**\r\n * dsh-offpeak — 日期范围选择器(双月日历,风格对齐 DSH 排期面板)。\r\n *\r\n * 交互:\r\n * - 触发器是一行 `开始日期 → 结束日期` + 日历图标;\r\n * - 点开后是两个并排月份,`‹‹ ‹ 2026年 9月 2026年 10月 › ››`;\r\n * - 第一次点选起点、第二次点选终点(反着点会自动对调);选满即收起;\r\n * - 已有完整区间时再点一次 = 重新开始选。\r\n *\r\n * 只有日期;**小时**由调用方在日历旁另配下拉(保持「到小时」的精度)。\r\n * 纯展示 + 受控回调,不做校验(非法组合由主机侧拒绝并回报错误)。\r\n */\r\nimport { useEffect, useRef, useState } from 'react'\r\nimport {\r\n addMonths,\r\n compareIso,\r\n fromIso,\r\n inRange,\r\n isEdge,\r\n monthGrid,\r\n monthTitle,\r\n nextRange,\r\n rangeLabel,\r\n seedRange,\r\n weekdayLabels,\r\n} from './date-range'\r\n\r\nexport interface DateRangePickerProps {\r\n from: string\r\n to: string\r\n /** 任一端变化都会回调(`YYYY-MM-DD`;未选满时另一侧为 '')。 */\r\n onChange: (_from: string, _to: string) => void\r\n disabled?: boolean\r\n}\r\n\r\n/** 双月范围日历。 */\r\nexport function DateRangePicker({ from, to, onChange, disabled = false }: DateRangePickerProps) {\r\n const [open, setOpen] = useState(false)\r\n // 「今天」的 ISO 走既有的 seedRange(`Date` → `YYYY-MM-DD` 的转换只在那一处实现)。\r\n const todayIso = seedRange(new Date()).fromDate\r\n const [anchor, setAnchor] = useState(() => {\r\n const seed = fromIso(from) ?? fromIso(to) ?? fromIso(todayIso)!\r\n return { y: seed.y, m: seed.m }\r\n })\r\n const rootRef = useRef<HTMLDivElement | null>(null)\r\n\r\n // 外部值变化时把视图跟到起点所在月(只在「打开」这一下跟随,\r\n // 避免用户翻月时被立刻拽回去)。\r\n useEffect(() => {\r\n const seed = fromIso(from) ?? fromIso(to)\r\n if (seed !== null && open) setAnchor({ y: seed.y, m: seed.m })\r\n }, [open])\r\n\r\n // 点外面 / Esc 收起。\r\n useEffect(() => {\r\n if (!open) return\r\n const onDown = (ev: MouseEvent): void => {\r\n const el = rootRef.current\r\n if (el !== null && ev.target instanceof Node && !el.contains(ev.target)) setOpen(false)\r\n }\r\n const onKey = (ev: KeyboardEvent): void => {\r\n if (ev.key === 'Escape') setOpen(false)\r\n }\r\n document.addEventListener('mousedown', onDown)\r\n document.addEventListener('keydown', onKey)\r\n return () => {\r\n document.removeEventListener('mousedown', onDown)\r\n document.removeEventListener('keydown', onKey)\r\n }\r\n }, [open])\r\n\r\n const label = rangeLabel(from, to)\r\n const right = addMonths(anchor, 1)\r\n\r\n function pick(iso: string): void {\r\n const r = nextRange(from, to, iso)\r\n onChange(r.from, r.to)\r\n if (r.done) setOpen(false)\r\n }\r\n\r\n function cellClass(iso: string, inMonth: boolean): string {\r\n const parts = ['sg-dr-day']\r\n if (!inMonth) parts.push('sg-dr-out')\r\n if (inRange(iso, from, to)) parts.push('sg-dr-in')\r\n if (isEdge(iso, from, to) && compareIso(from, to) !== 0) parts.push('sg-dr-edge')\r\n else if (isEdge(iso, from, to)) parts.push('sg-dr-edge', 'sg-dr-single')\r\n if (iso === todayIso) parts.push('sg-dr-today')\r\n return parts.join(' ')\r\n }\r\n\r\n function renderMonth(y: number, m: number) {\r\n const cells = monthGrid(y, m)\r\n return (\r\n <div className=\"sg-dr-month\">\r\n <div className=\"sg-dr-mtitle\">{monthTitle(y, m)}</div>\r\n <div className=\"sg-dr-grid\">\r\n {weekdayLabels().map((w) => (\r\n <span key={w} className=\"sg-dr-wd\">\r\n {w}\r\n </span>\r\n ))}\r\n {cells.map((c) => (\r\n <button\r\n key={c.iso}\r\n type=\"button\"\r\n className={cellClass(c.iso, c.inMonth)}\r\n disabled={disabled}\r\n onClick={() => pick(c.iso)}\r\n >\r\n {c.day}\r\n </button>\r\n ))}\r\n </div>\r\n </div>\r\n )\r\n }\r\n\r\n return (\r\n <div className=\"sg-dr-root\" ref={rootRef}>\r\n <button\r\n type=\"button\"\r\n className={`sg-dr-trigger ${label.placeholder ? 'sg-dr-ph' : ''}`}\r\n disabled={disabled}\r\n aria-expanded={open}\r\n onClick={() => setOpen((p) => !p)}\r\n >\r\n <span className=\"sg-dr-text\">{label.text}</span>\r\n <span className=\"sg-dr-icon\" aria-hidden=\"true\">\r\n {/* 极简日历字形,零图标依赖 */}\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\">\r\n <rect x=\"2\" y=\"3\" width=\"12\" height=\"11\" rx=\"2\" stroke=\"currentColor\" strokeWidth=\"1.3\" />\r\n <path d=\"M2 6.5h12M5.5 2v2.5M10.5 2v2.5\" stroke=\"currentColor\" strokeWidth=\"1.3\" strokeLinecap=\"round\" />\r\n </svg>\r\n </span>\r\n </button>\r\n\r\n {open && (\r\n <div className=\"sg-dr-panel\" role=\"dialog\" aria-label=\"选择日期范围\">\r\n <div className=\"sg-dr-head\">\r\n <button\r\n type=\"button\"\r\n className=\"sg-dr-nav\"\r\n title=\"上一年\"\r\n onClick={() => setAnchor((a) => addMonths(a, -12))}\r\n >\r\n ‹‹\r\n </button>\r\n <button type=\"button\" className=\"sg-dr-nav\" title=\"上个月\" onClick={() => setAnchor((a) => addMonths(a, -1))}>\r\n ‹\r\n </button>\r\n <span className=\"sg-dr-spacer\" />\r\n <button type=\"button\" className=\"sg-dr-nav\" title=\"下个月\" onClick={() => setAnchor((a) => addMonths(a, 1))}>\r\n ›\r\n </button>\r\n <button\r\n type=\"button\"\r\n className=\"sg-dr-nav\"\r\n title=\"下一年\"\r\n onClick={() => setAnchor((a) => addMonths(a, 12))}\r\n >\r\n ››\r\n </button>\r\n </div>\r\n <div className=\"sg-dr-months\">\r\n {renderMonth(anchor.y, anchor.m)}\r\n {renderMonth(right.y, right.m)}\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n )\r\n}\r\n","/**\r\n * dsh-offpeak — 客户端样式(与 composer 右侧 input-traffic 冻结按钮同一视觉语言)。\r\n *\r\n * 为什么注入 `<style>` 而不是 CSS Modules:本插件的 tsdown 配置没有 CSS Modules 管线\r\n * (input-traffic 有),而 settings-card 已经用 `<style data-plugin-css>` 的既有约定。\r\n *\r\n * 覆盖两块:\r\n * - 「畅跑」按钮(四态:默认 / 畅跑中 / 畅跑暂停)与状态徽标,对齐到同一行的其它控件\r\n * (高度 24px、圆角 6px、12px 字号、同样的 border / hover 令牌);\r\n * - 畅跑任务的**管理弹层**(列表 + 日期/小时选择器 + 操作按钮),同样只用原生控件。\r\n */\r\n\r\n/** 与 `dsh-input-traffic/src/client/freeze-button.module.css` 对齐的控件外观。 */\r\nexport const CLIENT_CSS = `\r\n.sg-fr-root{position:relative;display:inline-flex;flex:none}\r\n.sg-fr{display:inline-flex;align-items:center;gap:4px;height:24px;padding:0 8px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:transparent;color:var(--dsw-alias-label-secondary,#6b7280);font-size:12px;line-height:1;cursor:pointer;white-space:nowrap;flex:none}\r\n.sg-fr:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.04))}\r\n.sg-fr:disabled{opacity:.55;cursor:default}\r\n.sg-fr[aria-pressed='true'],.sg-fr-active{border-color:var(--dsw-alias-state-success-primary,#30a46c);color:var(--dsw-alias-state-success-primary,#30a46c);background:color-mix(in srgb,var(--dsw-alias-state-success-primary,#30a46c) 8%,transparent)}\r\n.sg-fr-suspended{border-color:var(--dsw-alias-state-warning-primary,#d97706);color:var(--dsw-alias-state-warning-primary,#d97706);background:color-mix(in srgb,var(--dsw-alias-state-warning-primary,#d97706) 8%,transparent)}\r\n.sg-status{display:inline-flex;align-items:center;height:24px;padding:0 8px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:transparent;font-size:12px;line-height:1;color:var(--dsw-alias-label-secondary,#6b7280);white-space:nowrap;flex:none}\r\n.sg-status.sg-peak{border-color:var(--dsw-alias-state-error-primary,#dc2626);color:var(--dsw-alias-state-error-primary,#dc2626)}\r\n.sg-status.sg-off{border-color:var(--dsw-alias-state-success-primary,#30a46c);color:var(--dsw-alias-state-success-primary,#30a46c)}\r\n.sg-status.sg-weekend{border-color:var(--dsw-alias-state-success-primary,#30a46c);color:var(--dsw-alias-state-success-primary,#30a46c)}\r\n/* 徽标即按钮(v3.1.4):所有阶段同一个按钮;v3.1.6 去掉了灰色填充与按钮内箭头 */\r\n.sg-status-root{position:relative;display:inline-flex;flex:none}\r\n.sg-status.sg-status-btn{cursor:pointer}\r\n.sg-status.sg-status-btn:hover{border-color:var(--dsw-alias-border-l4,rgba(0,0,0,.28))}\r\n.sg-status-caret{flex:none;line-height:1;transition:transform .16s}\r\n.sg-status-caret-open{transform:rotate(180deg)}\r\n/* 「高峰拦截列表」面板(.sgMl* 由状态徽标与设置表单共用同一份样式) */\r\n.sgMlBody{margin-top:8px;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));border-radius:8px;background:var(--dsw-alias-bg-layer-2,transparent)}\r\n.sgMlBody *{box-sizing:border-box}\r\n.sgMlFilterRow{display:flex;align-items:center;gap:6px;padding-bottom:6px}\r\n.sgMlInput{flex:1;min-width:0;height:24px;padding:0 8px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:var(--dsw-alias-bg-layer-2,transparent);color:inherit;font:inherit;font-size:12px}\r\n.sgMlInput:disabled{opacity:.6;cursor:not-allowed}\r\n.sgMlError{color:var(--dsw-alias-state-error-primary,#dc2626);font-size:12px;line-height:18px;margin:6px 0 0;word-break:break-word}\r\n.sgMlPatterns{display:flex;align-items:center;flex-wrap:wrap;gap:6px;padding-bottom:6px;border-bottom:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.08))}\r\n.sgMlSentinel{color:var(--dsw-alias-label-tertiary,#6b7280);font-size:12px}\r\n.sgMlChip{font-size:12px;padding:1px 6px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:4px;font-variant-numeric:tabular-nums}\r\n.sgMlRefresh{margin-left:auto;flex:none;height:22px;padding:0 8px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:transparent;color:inherit;font:inherit;font-size:12px;cursor:pointer}\r\n.sgMlRefresh:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06))}\r\n.sgMlRefresh:disabled{opacity:.55;cursor:default}\r\n.sgMlBtn{flex:none;height:24px;padding:0 8px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:transparent;color:inherit;font:inherit;font-size:12px;cursor:pointer}\r\n.sgMlBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06))}\r\n.sgMlBtn:disabled{opacity:.55;cursor:default}\r\n.sgMlChipUndo{margin-left:4px;padding:0 2px;border:0;background:transparent;color:inherit;font:inherit;font-size:12px;line-height:1;cursor:pointer;opacity:.7}\r\n.sgMlChipUndo:hover:not(:disabled){opacity:1;color:var(--dsw-alias-state-error-primary,#dc2626)}\r\n.sgMlChipUndo:disabled{opacity:.4;cursor:default}\r\n.sgMlHead{display:flex;align-items:baseline;gap:8px;margin:10px 0 4px}\r\n/* 畅跑任务区内嵌在徽标面板最上方(v3.1.6) */\r\n.sg-fr-section{padding-bottom:8px;border-bottom:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.08))}\r\n.sg-fr-section .sg-fr-head{margin-bottom:6px}\r\n.sgMlList{list-style:none;margin:0;padding:0;max-height:240px;overflow:auto}\r\n.sgMlProvider{margin:8px 0 2px;font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary,#6b7280)}\r\n.sgMlItem{display:flex;align-items:center;padding:2px 0}\r\n.sgMlCheck{display:flex;align-items:center;gap:6px;min-width:0;font-size:12px;cursor:pointer}\r\n.sgMlCheck>input{cursor:pointer;flex:none;margin-left:auto}\r\n.sgMlCheck>input:disabled{cursor:not-allowed}\r\n.sgMlCovered{color:var(--dsw-alias-state-error-primary,#dc2626)}\r\n.sgMlDot{flex:none;line-height:1}\r\n.sgMlModel{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\r\n.sgMlTag{flex:none;font-size:11px;color:var(--dsw-alias-label-tertiary,#6b7280);border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.08));border-radius:4px;padding:0 4px}\r\n.sgMlHint{color:var(--dsw-alias-label-tertiary,#6b7280);font-size:12px;line-height:18px;margin:6px 0 0}\r\n.sg-fr-panel,.sg-status-panel{position:absolute;bottom:calc(100% + 8px);right:0;z-index:40;width:448px;max-width:calc(100vw - 24px);box-sizing:border-box;padding:10px 12px 12px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));border-radius:10px;background:var(--dsw-specific-input-major,#fff);box-shadow:var(--dsw-elevation-panel,0 6px 24px rgba(0,0,0,.16));color:var(--dsw-alias-label-primary,#111);font-size:12px;line-height:1.5}\r\n.sg-status-panel{width:380px;z-index:45}\r\n.sg-status-panel *{box-sizing:border-box}\r\n.sg-fr-panel *{box-sizing:border-box}\r\n.sg-fr-head{display:flex;align-items:baseline;gap:8px;font-size:13px;font-weight:600;margin-bottom:8px}\r\n.sg-fr-title{flex:1;min-width:0}\r\n.sg-fr-tz{font-weight:400;font-size:11px;color:var(--dsw-alias-label-tertiary,#6b7280)}\r\n.sg-fr-closeicon{flex:none;width:20px;height:20px;line-height:1;padding:0;border:0;border-radius:4px;background:transparent;color:var(--dsw-alias-label-tertiary,#6b7280);cursor:pointer;font-size:14px;font-weight:400}\r\n.sg-fr-closeicon:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06));color:var(--dsw-alias-label-primary,#111)}\r\n.sg-fr-bar{display:flex;gap:6px;margin-bottom:8px}\r\n.sg-fr-bar-btn{flex:1;min-width:0;height:26px;padding:0 6px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:transparent;color:var(--dsw-alias-label-secondary,#6b7280);font:inherit;font-size:12px;line-height:1;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\r\n.sg-fr-bar-btn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06));color:var(--dsw-alias-label-primary,#111)}\r\n.sg-fr-bar-btn:disabled{opacity:.5;cursor:default}\r\n.sg-fr-bar-on{border-color:var(--dsw-alias-button-primary-fill,#4d6bfe);color:var(--dsw-alias-button-primary-fill,#4d6bfe)}\r\n.sg-fr-bar-danger:hover:not(:disabled){border-color:var(--dsw-alias-state-error-primary,#dc2626);color:var(--dsw-alias-state-error-primary,#dc2626)}\r\n.sg-fr-empty{color:var(--dsw-alias-label-tertiary,#6b7280);padding:2px 0 8px}\r\n.sg-fr-list{list-style:none;margin:0 0 8px;padding:0;max-height:168px;overflow:auto}\r\n.sg-fr-item{display:flex;align-items:center;gap:6px;padding:4px 0;border-bottom:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.06))}\r\n.sg-fr-range{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-variant-numeric:tabular-nums}\r\n.sg-fr-tag{flex:none;font-size:11px;padding:0 4px;border-radius:4px;border:1px solid transparent}\r\n.sg-fr-tag-active{border-color:var(--dsw-alias-state-success-primary,#30a46c);color:var(--dsw-alias-state-success-primary,#30a46c)}\r\n.sg-fr-tag-scheduled{color:var(--dsw-alias-label-tertiary,#6b7280)}\r\n.sg-fr-tag-paused{border-color:var(--dsw-alias-state-warning-primary,#d97706);color:var(--dsw-alias-state-warning-primary,#d97706)}\r\n.sg-fr-tag-ended{color:var(--dsw-alias-label-dimmed,#9ca3af)}\r\n.sg-fr-icon{flex:none;width:22px;height:22px;line-height:1;padding:0;border:1px solid transparent;border-radius:4px;background:transparent;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;font-size:12px;display:inline-flex;align-items:center;justify-content:center}\r\n.sg-fr-icon:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06));color:var(--dsw-alias-label-primary,#111)}\r\n.sg-fr-icon:disabled{opacity:.4;cursor:default}\r\n.sg-fr-icon-danger:hover:not(:disabled){color:var(--dsw-alias-state-error-primary,#dc2626)}\r\n.sg-fr-form{display:flex;flex-direction:column;gap:6px;margin:2px 0 8px;padding:8px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.08));border-radius:8px}\r\n.sg-fr-form-actions{display:flex;justify-content:flex-end;gap:6px}\r\n.sg-fr-add,.sg-fr-cancel{height:24px;padding:0 10px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:transparent;color:inherit;font:inherit;font-size:12px;cursor:pointer}\r\n.sg-fr-add{border-color:var(--dsw-alias-button-primary-fill,#4d6bfe);color:var(--dsw-alias-button-primary-fill,#4d6bfe)}\r\n.sg-fr-add:hover:not(:disabled),.sg-fr-cancel:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06))}\r\n.sg-fr-add:disabled,.sg-fr-cancel:disabled{opacity:.55;cursor:default}\r\n.sg-fr-error{color:var(--dsw-alias-state-error-primary,#dc2626);margin-top:6px}\r\n/* ── 日期范围选择器(双月日历,风格对齐 DSH 排期面板)── */\r\n.sg-dr-root{position:relative}\r\n.sg-dr-trigger{display:flex;align-items:center;gap:6px;width:100%;height:28px;padding:0 8px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:var(--dsw-alias-bg-layer-2,transparent);color:inherit;font:inherit;font-size:12px;cursor:pointer;text-align:left}\r\n.sg-dr-trigger:hover:not(:disabled){border-color:var(--dsw-alias-border-l4,rgba(0,0,0,.2))}\r\n.sg-dr-trigger:disabled{opacity:.55;cursor:default}\r\n.sg-dr-text{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-variant-numeric:tabular-nums}\r\n.sg-dr-ph .sg-dr-text{color:var(--dsw-alias-label-tertiary,#6b7280)}\r\n.sg-dr-icon{flex:none;display:inline-flex;color:var(--dsw-alias-label-tertiary,#6b7280)}\r\n.sg-dr-panel{position:absolute;top:calc(100% + 6px);left:0;z-index:60;padding:8px 10px 10px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));border-radius:10px;background:var(--dsw-specific-input-major,#fff);box-shadow:var(--dsw-elevation-panel,0 6px 24px rgba(0,0,0,.16))}\r\n.sg-dr-head{display:flex;align-items:center;gap:2px;margin-bottom:4px}\r\n.sg-dr-spacer{flex:1}\r\n.sg-dr-nav{width:22px;height:22px;padding:0;border:0;border-radius:4px;background:transparent;color:var(--dsw-alias-label-secondary,#6b7280);font-size:12px;line-height:1;cursor:pointer}\r\n.sg-dr-nav:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06));color:var(--dsw-alias-label-primary,#111)}\r\n.sg-dr-months{display:flex;gap:14px}\r\n.sg-dr-month{width:196px}\r\n.sg-dr-mtitle{text-align:center;font-size:12px;font-weight:600;margin-bottom:4px}\r\n.sg-dr-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:1px}\r\n.sg-dr-wd{text-align:center;font-size:11px;color:var(--dsw-alias-label-tertiary,#6b7280);padding-bottom:2px}\r\n.sg-dr-day{height:24px;padding:0;border:0;border-radius:4px;background:transparent;color:inherit;font:inherit;font-size:12px;line-height:1;cursor:pointer;font-variant-numeric:tabular-nums}\r\n.sg-dr-day:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06))}\r\n.sg-dr-out{color:var(--dsw-alias-label-dimmed,#9ca3af)}\r\n.sg-dr-in{background:var(--dsw-alias-interactive-bg-selected,rgba(77,107,254,.12))}\r\n.sg-dr-edge{background:var(--dsw-alias-button-primary-fill,#4d6bfe);color:#fff;font-weight:600}\r\n.sg-dr-edge:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,#3f5ce8)}\r\n.sg-dr-today{outline:1px solid var(--dsw-alias-border-l4,rgba(0,0,0,.2));outline-offset:-1px}\r\n.sg-fr-hours{display:flex;align-items:center;gap:6px}\r\n.sg-fr-hour{display:flex;align-items:center;gap:4px;flex:1;min-width:0}\r\n.sg-fr-hour>span:first-child{flex:none;color:var(--dsw-alias-label-tertiary,#6b7280)}\r\n.sg-fr-hour>select{flex:1;min-width:0;font:inherit;font-size:12px;padding:3px 6px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:var(--dsw-alias-bg-layer-2,transparent);color:inherit}\r\n.sg-fr-unit{flex:none;color:var(--dsw-alias-label-tertiary,#6b7280)}\r\n.sg-fr-now{flex:none;height:24px;padding:0 8px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:transparent;color:inherit;font:inherit;font-size:12px;cursor:pointer}\r\n.sg-fr-now:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06))}\r\n.sg-fr-form{display:flex;flex-direction:column;gap:6px;margin:8px 0}\r\n.sg-fr-add{align-self:flex-end;height:24px;padding:0 10px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:transparent;color:inherit;font:inherit;font-size:12px;cursor:pointer}\r\n.sg-fr-add:hover:not(:disabled),.sg-fr-resume:hover:not(:disabled),.sg-fr-clear:hover:not(:disabled),.sg-fr-close:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06))}\r\n.sg-fr-add:disabled,.sg-fr-resume:disabled,.sg-fr-clear:disabled{opacity:.55;cursor:default}\r\n.sg-fr-error{color:var(--dsw-alias-state-error-primary,#dc2626);margin-bottom:6px}\r\n.sg-fr-actions{display:flex;justify-content:flex-end;gap:6px;margin-top:4px}\r\n.sg-fr-actions>button{height:24px;padding:0 10px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:transparent;color:inherit;font:inherit;font-size:12px;cursor:pointer}\r\n.sg-fr-resume{border-color:var(--dsw-alias-state-success-primary,#30a46c)!important;color:var(--dsw-alias-state-success-primary,#30a46c)!important}\r\n`\r\n\r\n/** 注入一次(幂等;无 document 时静默跳过)。 */\r\nexport function injectClientCss(): void {\r\n if (typeof document === 'undefined') return\r\n if (document.querySelector('style[data-plugin-css=\"offpeak-client\"]') !== null) return\r\n const tag = document.createElement('style')\r\n tag.dataset.plugin = 'dsh-offpeak'\r\n tag.dataset.pluginCss = 'offpeak-client'\r\n tag.textContent = CLIENT_CSS\r\n document.head.appendChild(tag)\r\n}\r\n","/**\r\n * dsh-offpeak — 畅跑任务区(**内嵌在状态徽标面板最上方**,v3.1.6 起没有独立按钮)。\r\n *\r\n * 布局(用户定案,对齐原「畅跑任务管理」弹层):\r\n * - **标题行**:`畅跑任务` + `按 <时区>`;\r\n * - **三个文本按钮**:`新建畅跑任务` / `暂停全部任务`(全暂停时变 `恢复全部任务`)/ `删除全部任务`;\r\n * 「新建」内联展开起止时间选择器(开始 / 结束,到小时);\r\n * - **每行两个图标按钮**:`⏸ / ▶`(暂停 / 恢复该段)与 `×`(删除该段)。\r\n *\r\n * 打开/关闭由父层(状态徽标面板)控制:这里没有触发按钮、没有 open 状态、也不点外关闭。\r\n * 时间选择器的输入值按**配置时区**解释(主机侧解析),标题行标注该时区。\r\n *\r\n * 状态更新:5s 轮询 `/session-guard/state`(畅跑是低频交互,轮询足够且最简单)\r\n * + 本地定时器保证到点后状态及时刷新。全部 fail-open,绝不抛错。\r\n */\r\nimport { useCallback, useEffect, useState } from 'react'\r\nimport {\r\n EMPTY_FREE_RUN,\r\n ICON_DELETE,\r\n ICON_PAUSE,\r\n ICON_RESUME,\r\n allTasksPaused,\r\n canPause,\r\n canResume,\r\n pauseAllLabel,\r\n readFreeRun,\r\n rowDeleteTitle,\r\n rowPauseTitle,\r\n statusLabel,\r\n toolbarDisabled,\r\n type FreeRunView,\r\n} from './free-run-button-text'\r\nimport { DateRangePicker } from './date-range-picker'\r\nimport { combineEnd, combineStart, seedRange } from './date-range'\r\nimport { injectClientCss } from './styles'\r\n\r\n/** slot 运行时注入的会话级 props。 */\r\nexport interface FreeRunPanelProps {\r\n sessionId?: string\r\n}\r\n\r\n/** 兜底轮询间隔(畅跑是低频交互,5s 足够;到点切换另有本地定时器)。 */\r\nconst POLL_MS = 5_000\r\n\r\n// 日期 + 小时的拼接与「按小时包含」语义见 date-range.ts 的 combineStart / combineEnd:\r\n// 开始 = H:00,结束 = H:59(否则选「23 时」会变成 23:00,当天最后一小时选不进来)。\r\n\r\n/** 拆出 `YYYY-MM-DD` 与 `HH`(供选择器回填)。 */\r\nexport function splitDateTime(value: string | null | undefined): { date: string; hour: string } {\r\n const s = typeof value === 'string' ? value : ''\r\n const m = /^(\\d{4}-\\d{2}-\\d{2})T(\\d{2})/.exec(s)\r\n if (m === null) return { date: '', hour: '' }\r\n return { date: m[1], hour: m[2] }\r\n}\r\n\r\nconst HOURS = Array.from({ length: 24 }, (_, i) => String(i).padStart(2, '0'))\r\n\r\n/**\r\n * 畅跑任务区(**内嵌在状态徽标面板顶部**,不再有独立按钮)。\r\n *\r\n * 布局对齐原「畅跑任务管理」弹层:标题 + 时区 → 新建 / 暂停全部 / 删除全部 → 任务列表。\r\n * 打开由父层(状态徽标面板)控制,所以这里没有「打开/关闭」状态、没有触发按钮、也不点外关闭。\r\n */\r\nexport function FreeRunPanel({ sessionId }: FreeRunPanelProps) {\r\n const [view, setView] = useState<FreeRunView>(EMPTY_FREE_RUN)\r\n const [busy, setBusy] = useState(false)\r\n const [error, setError] = useState('')\r\n const [creating, setCreating] = useState(false)\r\n const [fromDate, setFromDate] = useState('')\r\n const [fromHour, setFromHour] = useState('')\r\n const [toDate, setToDate] = useState('')\r\n const [toHour, setToHour] = useState('')\r\n\r\n /** 用「当前时刻」重新播种起止(开始 = 本小时整点,结束 = +2h)。 */\r\n const seedFromNow = useCallback((): void => {\r\n const s = seedRange(new Date())\r\n setFromDate(s.fromDate)\r\n setFromHour(s.fromHour)\r\n setToDate(s.toDate)\r\n setToHour(s.toHour)\r\n }, [])\r\n\r\n injectClientCss()\r\n\r\n const poll = useCallback(async (): Promise<void> => {\r\n if (sessionId === undefined || sessionId === '') return\r\n try {\r\n const res = await fetch(`/session-guard/state?session=${encodeURIComponent(sessionId)}`)\r\n const body = await res.json().catch(() => null)\r\n setView(readFreeRun(body))\r\n } catch {\r\n // fail-open:路由不存在 / 网络失败 → 保持上一次状态。\r\n }\r\n }, [sessionId])\r\n\r\n useEffect(() => {\r\n if (sessionId === undefined || sessionId === '') return\r\n let cancelled = false\r\n void poll()\r\n const timer = setInterval(() => {\r\n if (!cancelled) void poll()\r\n }, POLL_MS)\r\n return () => {\r\n cancelled = true\r\n clearInterval(timer)\r\n }\r\n }, [sessionId, poll])\r\n\r\n // 到点后要立刻刷新(按钮高亮 / 列表状态):按剩余毫秒设一个本地定时器。\r\n useEffect(() => {\r\n const ms = view.msRemaining\r\n if (ms === null || !Number.isFinite(ms) || ms <= 0) return\r\n const timer = setTimeout(() => {\r\n void poll()\r\n }, ms + 500)\r\n return () => {\r\n clearTimeout(timer)\r\n }\r\n }, [view.msRemaining, poll])\r\n\r\n // 内嵌形态没有「打开面板」这一刻:挂载即按当前时刻播种新建表单。\r\n useEffect(() => {\r\n seedFromNow()\r\n }, [seedFromNow])\r\n\r\n const rpc = useCallback(\r\n async (action: string, payload: Record<string, unknown> = {}): Promise<boolean> => {\r\n if (sessionId === undefined || sessionId === '') return false\r\n setBusy(true)\r\n setError('')\r\n try {\r\n const res = await fetch('/session-guard/rpc', {\r\n method: 'POST',\r\n headers: { 'content-type': 'application/json' },\r\n body: JSON.stringify({ sessionId, action, ...payload }),\r\n })\r\n const body = await res.json().catch(() => null)\r\n if (body?.ok === true) {\r\n setView(readFreeRun({ ok: true, freeRun: body.result }))\r\n return true\r\n }\r\n setError(typeof body?.error === 'string' ? body.error : '操作失败')\r\n return false\r\n } catch {\r\n setError('操作失败(网络或路由不可用)')\r\n return false\r\n } finally {\r\n setBusy(false)\r\n }\r\n },\r\n [sessionId],\r\n )\r\n\r\n if (sessionId === undefined || sessionId === '') return null\r\n // 插件整体关掉且没有任何任务时不给按钮;已有任务则保留,便于清理。\r\n if (!view.available && view.windows.length === 0) return null\r\n\r\n const onAdd = async (): Promise<void> => {\r\n if (fromDate === '' || fromHour === '' || toDate === '' || toHour === '') {\r\n setError('请选择开始与结束时间')\r\n return\r\n }\r\n const ok = await rpc('freeRunAdd', {\r\n from: combineStart(fromDate, fromHour),\r\n to: combineEnd(toDate, toHour),\r\n })\r\n if (ok) {\r\n setError('')\r\n setCreating(false)\r\n }\r\n }\r\n\r\n const nothingToAct = toolbarDisabled(view)\r\n const allPaused = allTasksPaused(view)\r\n\r\n return (\r\n <div className=\"sg-fr-section\" role=\"group\" aria-label=\"畅跑任务\">\r\n <div className=\"sg-fr-head\">\r\n <span className=\"sg-fr-title\">畅跑任务</span>\r\n <span className=\"sg-fr-tz\">按 {view.timezone === '' ? '本地时区' : view.timezone}</span>\r\n </div>\r\n\r\n {/* 顶部三个文本按钮:新建 / 暂停全部 / 删除全部 */}\r\n <div className=\"sg-fr-bar\">\r\n <button\r\n type=\"button\"\r\n className={`sg-fr-bar-btn ${creating ? 'sg-fr-bar-on' : ''}`}\r\n disabled={busy}\r\n aria-pressed={creating}\r\n onClick={() => {\r\n setCreating((prev) => !prev)\r\n setError('')\r\n }}\r\n >\r\n 新建畅跑任务\r\n </button>\r\n <button\r\n type=\"button\"\r\n className=\"sg-fr-bar-btn\"\r\n disabled={busy || nothingToAct}\r\n onClick={() => {\r\n void rpc(allPaused ? 'freeRunResumeAll' : 'freeRunPauseAll')\r\n }}\r\n >\r\n {pauseAllLabel(view)}\r\n </button>\r\n <button\r\n type=\"button\"\r\n className=\"sg-fr-bar-btn sg-fr-bar-danger\"\r\n disabled={busy || view.windows.length === 0}\r\n onClick={() => {\r\n void rpc('freeRunClear')\r\n }}\r\n >\r\n 删除全部任务\r\n </button>\r\n </div>\r\n\r\n {creating && (\r\n <div className=\"sg-fr-form\">\r\n {/* 日期范围:双月日历(对齐 DSH 排期面板的样式)。\r\n 小时仍用下拉,保持「到小时」的精度。 */}\r\n <DateRangePicker\r\n from={fromDate}\r\n to={toDate}\r\n disabled={busy}\r\n onChange={(f, t) => {\r\n setFromDate(f)\r\n setToDate(t)\r\n setError('')\r\n }}\r\n />\r\n <div className=\"sg-fr-hours\">\r\n <label className=\"sg-fr-hour\">\r\n <span>开始</span>\r\n <select\r\n value={fromHour}\r\n disabled={busy}\r\n onChange={(e) => {\r\n setFromHour(e.target.value)\r\n }}\r\n >\r\n <option value=\"\">时</option>\r\n {HOURS.map((h) => (\r\n <option key={h} value={h}>\r\n {h}\r\n </option>\r\n ))}\r\n </select>\r\n <span className=\"sg-fr-unit\">时</span>\r\n </label>\r\n <label className=\"sg-fr-hour\">\r\n <span>结束</span>\r\n <select\r\n value={toHour}\r\n disabled={busy}\r\n onChange={(e) => {\r\n setToHour(e.target.value)\r\n }}\r\n >\r\n <option value=\"\">时</option>\r\n {HOURS.map((h) => (\r\n <option key={h} value={h}>\r\n {h}\r\n </option>\r\n ))}\r\n </select>\r\n <span className=\"sg-fr-unit\">时</span>\r\n </label>\r\n <button type=\"button\" className=\"sg-fr-now\" disabled={busy} title=\"重置为当前时刻\" onClick={seedFromNow}>\r\n 现在\r\n </button>\r\n </div>\r\n <div className=\"sg-fr-form-actions\">\r\n <button type=\"button\" className=\"sg-fr-add\" disabled={busy} onClick={() => void onAdd()}>\r\n 确定\r\n </button>\r\n <button\r\n type=\"button\"\r\n className=\"sg-fr-cancel\"\r\n disabled={busy}\r\n onClick={() => {\r\n setCreating(false)\r\n setError('')\r\n }}\r\n >\r\n 取消\r\n </button>\r\n </div>\r\n </div>\r\n )}\r\n\r\n {view.windows.length === 0 ? (\r\n <div className=\"sg-fr-empty\">还没有畅跑任务,点「新建畅跑任务」添加</div>\r\n ) : (\r\n <ul className=\"sg-fr-list\">\r\n {view.windows.map((w) => (\r\n <li key={w.id} className=\"sg-fr-item\">\r\n <span className=\"sg-fr-range\">\r\n {w.fromDisplay} – {w.toDisplay}\r\n </span>\r\n <span className={`sg-fr-tag sg-fr-tag-${w.status}`}>{statusLabel(w.status)}</span>\r\n <button\r\n type=\"button\"\r\n className=\"sg-fr-icon\"\r\n title={rowPauseTitle(w)}\r\n aria-label={w.paused === true ? '恢复' : '暂停'}\r\n disabled={busy || (!canPause(w) && !canResume(w))}\r\n onClick={() => {\r\n void rpc(w.paused === true ? 'freeRunResume' : 'freeRunPause', { id: w.id })\r\n }}\r\n >\r\n {w.paused === true ? ICON_RESUME : ICON_PAUSE}\r\n </button>\r\n <button\r\n type=\"button\"\r\n className=\"sg-fr-icon sg-fr-icon-danger\"\r\n title={rowDeleteTitle()}\r\n aria-label=\"删除\"\r\n disabled={busy}\r\n onClick={() => {\r\n void rpc('freeRunRemove', { id: w.id })\r\n }}\r\n >\r\n {ICON_DELETE}\r\n </button>\r\n </li>\r\n ))}\r\n </ul>\r\n )}\r\n\r\n {error !== '' && <div className=\"sg-fr-error\">{error}</div>}\r\n </div>\r\n )\r\n}\r\n","/**\r\n * dsh-offpeak — 「高峰拦截列表」的纯逻辑(零 React、零依赖,可单测)。\r\n *\r\n * 与 host 判定**同源**:列表项(`officialModelPatterns`)是**模型名**,匹配语义为**精确匹配**\r\n * (大小写不敏感、去首尾空白;v3.1.6 起不再有关键字/正则的包含匹配)。这里只做四件事:\r\n * 1. 某模型名是否在列表里(= 是否被拦);\r\n * 2. 单行勾选 / 取消勾选 → 新的列表;\r\n * 3. 关键词过滤(只影响展示与「全选」范围);\r\n * 4. 批量勾选 → 新的列表。\r\n *\r\n * **判定与存储逻辑都不在这里**:勾选只是往同一份列表里增删**同名条目**。\r\n */\r\n\r\n/** 一行的判定状态。 */\r\nexport interface ModelRowState {\r\n /** 命中的列表项(精确匹配下最多一项)。 */\r\n matched: string[]\r\n /** 列表里存在与模型名**完全相同**的条目(= 勾选状态)。 */\r\n explicit: boolean\r\n /** 是否已被拦截(精确匹配下等价于 `explicit`)。 */\r\n covered: boolean\r\n}\r\n\r\n/** 单个列表项是否命中模型名:**精确匹配**(大小写不敏感、去首尾空白)。 */\r\nexport function matchesPattern(model: string, pattern: string): boolean {\r\n if (typeof model !== 'string' || typeof pattern !== 'string') return false\r\n const a = model.trim().toLowerCase()\r\n const b = pattern.trim().toLowerCase()\r\n return a !== '' && b !== '' && a === b\r\n}\r\n\r\n/** 算出某模型名在给定列表下的行状态。 */\r\nexport function rowState(model: string, patterns: readonly string[]): ModelRowState {\r\n const list = Array.isArray(patterns) ? patterns : []\r\n const matched = list.filter((p) => matchesPattern(model, p))\r\n return { matched, explicit: matched.length > 0, covered: matched.length > 0 }\r\n}\r\n\r\n/** 给一行生成提示文案。 */\r\nexport function rowHint(state: ModelRowState): string {\r\n if (state.explicit) return '已拦截'\r\n return ''\r\n}\r\n\r\n/**\r\n * 勾选/取消勾选**单个**模型 → 新列表。\r\n *\r\n * - 已存在**完全同名**条目 → 删掉它(取消勾选;若仍有更宽的关键字覆盖,行会变成\r\n * 只读并提示是哪个关键字盖着 —— 那种情况需要改关键字,而不是逐个模型取消);\r\n * - 否则 → 把模型名作为一个新条目追加。\r\n *\r\n * 纯函数:不修改入参,返回新数组(去重、保持原有顺序)。\r\n */\r\nexport function toggleModel(patterns: readonly string[], model: string): string[] {\r\n const list = Array.isArray(patterns) ? [...patterns] : []\r\n if (typeof model !== 'string' || model === '') return list\r\n const at = list.indexOf(model)\r\n if (at !== -1) {\r\n list.splice(at, 1)\r\n return list\r\n }\r\n if (!list.includes(model)) list.push(model)\r\n return list\r\n}\r\n\r\n/** 模型行(面板与纯函数共用的最小形状)。 */\r\nexport interface ModelLike {\r\n provider: string\r\n model: string\r\n}\r\n\r\n/**\r\n * 关键词过滤:大小写不敏感的**子串**匹配,命中范围是 `provider/model`(只按 model 也能命中)。\r\n *\r\n * 只作用于**展示**与「全选」范围 —— 模型集合、拦截列表本身都不变(空关键词 = 不过滤)。\r\n */\r\nexport function filterRows<T extends ModelLike>(rows: readonly T[], keyword: string): T[] {\r\n const list = Array.isArray(rows) ? rows : []\r\n const q = typeof keyword === 'string' ? keyword.trim().toLowerCase() : ''\r\n if (q === '') return [...list]\r\n return list.filter((r) => `${r.provider}/${r.model}`.toLowerCase().includes(q))\r\n}\r\n\r\n/** 当前过滤结果是否已**全部**被拦截(决定「全选」按钮的文案与动作)。 */\r\nexport function allCovered(patterns: readonly string[], rows: readonly ModelLike[]): boolean {\r\n const list = Array.isArray(rows) ? rows : []\r\n if (list.length === 0) return false\r\n return list.every((r) => rowState(r.model, patterns).covered)\r\n}\r\n\r\n/**\r\n * 批量勾选:对**传入的这些行**(= 当前过滤结果)执行全选 / 取消全选。\r\n *\r\n * - `checked = true`:把还没被覆盖的模型名作为显式条目补进列表(已有条目不动);\r\n * - `checked = false`:把与这些模型名**完全同名**的条目删掉(被更宽关键字盖住的行仍会被拦,\r\n * 与单行勾选的语义一致 —— 那种情况要改关键字)。\r\n *\r\n * 纯函数:不修改入参,返回新数组。\r\n */\r\nexport function bulkToggle(patterns: readonly string[], rows: readonly ModelLike[], checked: boolean): string[] {\r\n const before = Array.isArray(patterns) ? patterns : []\r\n const list = [...before]\r\n const models = (Array.isArray(rows) ? rows : []).map((r) => r.model)\r\n if (checked) {\r\n for (const m of models) {\r\n if (typeof m !== 'string' || m === '') continue\r\n // 已被(关键字或显式条目)拦住的模型不重复追加 —— 避免把列表写成一堆冗余条目。\r\n // 覆盖判定用**进入本函数时**的列表:语义稳定(「给当前未覆盖的过滤结果补显式条目」)。\r\n if (rowState(m, before).covered) continue\r\n if (!list.includes(m)) list.push(m)\r\n }\r\n return list\r\n }\r\n const drop = new Set(models)\r\n return list.filter((p) => !drop.has(p))\r\n}\r\n","/**\r\n * dsh-offpeak — 「高峰拦截列表」面板(状态徽标展开区)。\r\n *\r\n * 形态(v3.1.6):\r\n * - 列表 = DSH 当前全部模型(`GET /session-guard/models`:各路由自报 + 本插件观察到的真实目标),\r\n * 每行一个模型、**复选框在行右侧**、按 provider 分组;\r\n * - 顶部**关键词过滤框** + **全选 / 取消全选**(只作用于当前过滤结果)+ **刷新**(重拉模型列表)\r\n * + **热重载**(先让 host 重读配置文件,再拉一次);\r\n * - **已拦截**:勾选即该模型名进列表(`POST /session-guard/rpc {action:'setModelPatterns'}` →\r\n * 写 `config/offpeak.json` 并立即 reload;0.1.7 客户端没有 settingsScope,这是本线唯一\r\n * 写入口)。列表项**精确匹配模型名**(大小写不敏感),没有关键字/正则的批量覆盖,\r\n * 因此每一项都能用它的 `×` **撤销**。\r\n *\r\n * 行状态与过滤/批量逻辑都在 `model-list.ts`(纯函数、可单测);host 侧判定逻辑不变。\r\n */\r\nimport { useEffect, useState } from 'react'\r\nimport type { JSX } from 'react'\r\nimport { allCovered, bulkToggle, filterRows, rowState, toggleModel } from './model-list'\r\n\r\n/** `GET /session-guard/models` 的一行。 */\r\nexport interface CatalogModel {\r\n provider: string\r\n model: string\r\n source?: string\r\n}\r\n\r\nexport function ModelListPanel(): JSX.Element {\r\n const [loading, setLoading] = useState(true)\r\n const [models, setModels] = useState<readonly CatalogModel[]>([])\r\n const [patterns, setPatterns] = useState<readonly string[]>([])\r\n const [error, setError] = useState<string | null>(null)\r\n const [writeError, setWriteError] = useState<string | null>(null)\r\n const [keyword, setKeyword] = useState('')\r\n const [pending, setPending] = useState(false)\r\n\r\n const load = async () => {\r\n setLoading(true)\r\n setError(null)\r\n const [modelsRes, settingsRes] = await Promise.allSettled([\r\n fetch('/session-guard/models').then((r) => r.json()),\r\n fetch('/session-guard/settings').then((r) => r.json()),\r\n ])\r\n const modelsBody = modelsRes.status === 'fulfilled' ? modelsRes.value : null\r\n if (modelsBody?.ok && Array.isArray(modelsBody.models)) setModels(modelsBody.models)\r\n else setError('模型列表不可用(host 需为 v3.1.5+:重启 DSH 后 /session-guard/models 才存在)')\r\n\r\n const settingsBody = settingsRes.status === 'fulfilled' ? settingsRes.value : null\r\n const list = settingsBody?.settings?.officialModelPatterns\r\n if (Array.isArray(list)) setPatterns(list)\r\n setLoading(false)\r\n }\r\n\r\n /** 热重载:让 host 重读配置文件,然后重拉列表与模型目录。 */\r\n const hotReload = async () => {\r\n try {\r\n await fetch('/session-guard/rpc', {\r\n method: 'POST',\r\n headers: { 'content-type': 'application/json' },\r\n body: JSON.stringify({ action: 'reloadConfig' }),\r\n })\r\n } catch {\r\n // fail-open:重读失败也照常重拉一次\r\n }\r\n await load()\r\n }\r\n\r\n useEffect(() => {\r\n // 只在挂载(= 展开)时拉一次;按钮负责后续更新。\r\n void load()\r\n }, [])\r\n\r\n /** 写回拦截列表(本线:写配置文件 + reload;失败只提示,不改本地展示)。 */\r\n const commit = async (next: string[]) => {\r\n setPending(true)\r\n setWriteError(null)\r\n const before = patterns\r\n setPatterns(next) // 乐观更新,失败时回滚\r\n try {\r\n const res = await fetch('/session-guard/rpc', {\r\n method: 'POST',\r\n headers: { 'content-type': 'application/json' },\r\n body: JSON.stringify({ action: 'setModelPatterns', patterns: next }),\r\n })\r\n const body = await res.json().catch(() => null)\r\n if (!res.ok || body?.ok !== true) {\r\n setPatterns(before)\r\n setWriteError(String(body?.error ?? `写入失败(HTTP ${res.status})`))\r\n } else if (Array.isArray(body.result?.patterns)) {\r\n setPatterns(body.result.patterns)\r\n }\r\n } catch (e) {\r\n setPatterns(before)\r\n setWriteError(`写入失败:${e instanceof Error ? e.message : String(e)}`)\r\n } finally {\r\n setPending(false)\r\n }\r\n }\r\n\r\n const filtered = filterRows(models, keyword)\r\n const providers = [...new Set(filtered.map((r) => r.provider))].sort()\r\n const everyCovered = allCovered(patterns, filtered)\r\n\r\n return (\r\n <div className=\"sgMlBody\">\r\n <div className=\"sgMlFilterRow\">\r\n <input\r\n className=\"sgMlInput\"\r\n type=\"text\"\r\n value={keyword}\r\n placeholder=\"过滤模型(provider/model 子串)\"\r\n onChange={(e) => setKeyword(e.currentTarget.value)}\r\n />\r\n <button\r\n type=\"button\"\r\n className=\"sgMlBtn\"\r\n disabled={pending || filtered.length === 0}\r\n onClick={() => void commit(bulkToggle(patterns, filtered, !everyCovered))}\r\n title=\"只作用于当前过滤结果\"\r\n >\r\n {everyCovered ? '取消全选' : '全选'}\r\n </button>\r\n <button type=\"button\" className=\"sgMlBtn\" disabled={loading} onClick={() => void load()} title=\"重新获取模型列表\">\r\n {loading ? '获取中…' : '刷新'}\r\n </button>\r\n <button type=\"button\" className=\"sgMlBtn\" disabled={loading} onClick={() => void hotReload()} title=\"重读配置文件后再拉一次\">\r\n 热重载\r\n </button>\r\n </div>\r\n {patterns.length > 0 && (\r\n <div className=\"sgMlPatterns\">\r\n <span className=\"sgMlSentinel\">已拦截</span>\r\n {patterns.map((p) => (\r\n <span key={p} className=\"sgMlChip\">\r\n {p}\r\n <button\r\n type=\"button\"\r\n className=\"sgMlChipUndo\"\r\n disabled={pending}\r\n title=\"撤销这一项\"\r\n aria-label={`撤销 ${p}`}\r\n onClick={() => void commit(patterns.filter((x) => x !== p))}\r\n >\r\n ×\r\n </button>\r\n </span>\r\n ))}\r\n </div>\r\n )}\r\n {writeError !== null && <p className=\"sgMlError\">✗ {writeError}</p>}\r\n {error !== null && <p className=\"sgMlHint\">{error}</p>}\r\n {error === null && !loading && filtered.length === 0 && (\r\n <p className=\"sgMlHint\">{models.length === 0 ? '没有可枚举的模型。' : '没有匹配的模型。'}</p>\r\n )}\r\n {filtered.length > 0 && (\r\n <ul className=\"sgMlList\">\r\n {providers.map((provider) => (\r\n <li key={provider}>\r\n <div className=\"sgMlProvider\">{provider}</div>\r\n <ul className=\"sgMlList\">\r\n {filtered\r\n .filter((r) => r.provider === provider)\r\n .map((r) => {\r\n const state = rowState(r.model, patterns)\r\n return (\r\n <li key={`${provider}/${r.model}`} className=\"sgMlItem\">\r\n <label className=\"sgMlCheck\" title=\"勾选 = 加入高峰拦截列表\">\r\n <span className=\"sgMlModel\">{r.model}</span>\r\n <input\r\n type=\"checkbox\"\r\n checked={state.covered}\r\n disabled={pending}\r\n onChange={() => void commit(toggleModel(patterns, r.model))}\r\n />\r\n </label>\r\n </li>\r\n )\r\n })}\r\n </ul>\r\n </li>\r\n ))}\r\n </ul>\r\n )}\r\n </div>\r\n )\r\n}\r\n","/**\r\n * dsh-offpeak — 状态徽标(**徽标即按钮**,fail-open)。\r\n *\r\n * 轮询 host 的 `/session-guard/status`(全局阶段 + 拦截列表规模)与\r\n * `/session-guard/state`(本会话畅跑任务),显示四态文案(v3.1.6):\r\n * - 高峰 + 有畅跑任务 → `高峰 · 畅跑中`;\r\n * - 高峰 + 有拦截任务 → `高峰 · 拦截中`;\r\n * - 高峰 + 都没任务 → `高峰`;\r\n * - 非高峰 → `谷时` / `周末`。\r\n *\r\n * 点开面板 = **畅跑任务区(最上方)+ 高峰拦截列表**(布局按用户定案);**每个阶段都是同一个\r\n * 按钮**(类名见 `badgeClassNames`),只有文案与配色变,不因不是高峰就退化成展示元素。\r\n * 本线(0.1.7)客户端没有 settingsScope,拦截列表的写入口是 `setModelPatterns`(写配置文件)。\r\n * 不做任何队列/会话动作;冻结按钮由 input-traffic 经桥接管(D6/D8)。\r\n */\r\nimport { useEffect, useState } from 'react'\r\nimport { badgeClassNames, badgeTitle, peakLabel, type Status } from './badge-text'\r\nimport { FreeRunPanel } from './free-run-button'\r\nimport { EMPTY_FREE_RUN, readFreeRun, type FreeRunView } from './free-run-button-text'\r\nimport { ModelListPanel } from './model-panel'\r\nimport { injectClientCss } from './styles'\r\n\r\n/** slot 运行时注入的会话级 props(sessionId 由 dsh 的 SessionStandardProps 提供)。 */\r\nexport interface StatusBadgeProps {\r\n sessionId?: string\r\n}\r\n\r\nconst POLL_MS = 15_000\r\n\r\n/** 状态徽标:轮询全局阶段,显示 高峰/谷时/周末(enabled 关闭或请求失败时静默隐藏)。 */\r\nexport function StatusBadge({ sessionId }: StatusBadgeProps) {\r\n const [status, setStatus] = useState<Status | null>(null)\r\n const [freeRun, setFreeRun] = useState<FreeRunView>(EMPTY_FREE_RUN)\r\n const [open, setOpen] = useState(false)\r\n\r\n injectClientCss()\r\n\r\n useEffect(() => {\r\n if (!sessionId) return\r\n let cancelled = false\r\n const poll = async () => {\r\n try {\r\n const res = await fetch('/session-guard/status')\r\n const body = await res.json().catch(() => null)\r\n if (!cancelled && body?.ok && body.status) setStatus(body.status)\r\n } catch {\r\n // fail-open:路由/网络不可达 → 静默,徽标隐藏。\r\n }\r\n try {\r\n const res = await fetch(`/session-guard/state?session=${encodeURIComponent(sessionId)}`)\r\n const body = await res.json().catch(() => null)\r\n if (!cancelled && body !== null) setFreeRun(readFreeRun(body))\r\n } catch {\r\n // fail-open:畅跑状态取不到 → 保持上一次。\r\n }\r\n }\r\n void poll()\r\n const timer = setInterval(poll, POLL_MS)\r\n return () => {\r\n cancelled = true\r\n clearInterval(timer)\r\n }\r\n }, [sessionId])\r\n\r\n if (!status || !status.enabled) return null\r\n\r\n const tasks = {\r\n intercepting: (status.modelPatterns ?? 0) > 0,\r\n freeRun: freeRun.windows.length > 0,\r\n }\r\n return (\r\n <div className=\"sg-status-root\">\r\n <button\r\n type=\"button\"\r\n className={badgeClassNames(status)}\r\n title={`${badgeTitle(status)} — 点击展开/收起高峰面板`}\r\n aria-expanded={open}\r\n data-sg-phase={status.phase}\r\n data-sg-provider-guard={status.providerGuard === true ? 'on' : 'off'}\r\n data-sg-step-held={String(status.stepHeld ?? 0)}\r\n onClick={() => setOpen(!open)}\r\n >\r\n {peakLabel(status, tasks)}\r\n </button>\r\n {open && (\r\n <div className=\"sg-status-panel\" role=\"dialog\" aria-label=\"高峰面板\">\r\n {/* 畅跑任务区在最上方(v3.1.6:独立的「畅跑」按钮已去掉,UI 内嵌到这里) */}\r\n <FreeRunPanel sessionId={sessionId} />\r\n <div className=\"sgMlHead\">\r\n <span className=\"sg-fr-title\">高峰拦截列表(按模型名)</span>\r\n <button type=\"button\" className=\"sg-fr-closeicon\" onClick={() => setOpen(false)} aria-label=\"关闭\">\r\n ×\r\n </button>\r\n </div>\r\n <ModelListPanel />\r\n </div>\r\n )}\r\n </div>\r\n )\r\n}\r\n","/**\r\n * dsh-offpeak — 浏览器 half。\r\n *\r\n * 职责(全部 fail-open,D8):\r\n * - 在 composer 输入区右侧注册「畅跑」按钮与**纯展示**状态徽标(高峰/谷时/周末);\r\n * - **不做**冻结/会话动作——冻结按钮由 input-traffic 接管并经 /session-guard/rpc\r\n * 桥接 host 会话门;本插件客户端不注册任何按钮,避免与 input-traffic 冲突。\r\n *\r\n * 兼容性注记(0.1.7 line):**客户端**服务 `settingsScope` 在 dsh 0.1.7 起不再提供\r\n * (它原本只为设置卡片席位服务)。若把它写进静态 `inject`,客户端条目会永远 pending\r\n * (waiting for service: settingsScope)并让应用 **web boot 致命失败** —— 这是实测到的\r\n * 崩溃原因,与宿主 `settings` 服务无关(宿主那个服务仍在,只是已无 `register`/`get`)。\r\n * 上游 compat/0.1.7 线因此整体去掉了设置卡片(`inject = ['slots','locale']`);这里跟随\r\n * 同一条口径:设置表单由宿主 `Config` 的 `.volatile()` 字段自动生成,客户端不再承载设置面板。\r\n *\r\n * 构建:tsdown → lib/client.js(__ModuleLoader__.load 注册,与 input-traffic 同构)。\r\n */\r\nimport { StatusBadge } from './status-badge'\r\n\r\n/** 客户端所需服务:slots(状态徽标)+ locale。 */\r\nexport const inject = ['slots', 'locale']\r\n\r\n/** 轻量 ctx 类型(仅本客户端用到的方法;构建时类型被剥离)。 */\r\ninterface SlotsFace {\r\n inject: (_name: string, _fn: () => unknown) => () => void\r\n register: (_options: Record<string, unknown>, _component: unknown) => () => void\r\n}\r\ninterface ClientCtx {\r\n slots: SlotsFace\r\n}\r\n\r\nexport function apply(ctx: ClientCtx) {\r\n // 「畅跑」按钮已去掉(v3.1.6):畅跑 UI 内嵌在状态徽标面板最上方,不再单独占位。\r\n\r\n // 状态徽标:输入区右侧。徽标即按钮 —— 点开「畅跑任务 + 高峰拦截列表」面板。\r\n ctx.slots.inject('conversation.input.right', () => ctx.slots.register({\r\n name: 'conversation.input.right',\r\n id: 'offpeak-status',\r\n order: 40,\r\n locale: 'offpeak',\r\n }, StatusBadge))\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["useState","useRef","POLL_MS","useState","useCallback","useState","useState","useRef"],"sources":["../src/client/badge-text.ts","../src/client/free-run-button-text.ts","../src/client/date-range.ts","../src/client/date-range-picker.tsx","../src/client/styles.ts","../src/client/free-run-button.tsx","../src/client/model-list.ts","../src/client/model-panel.tsx","../src/client/status-badge.tsx","../src/client/index.ts"],"sourcesContent":["/**\r\n * dsh-offpeak — 状态徽标文案(纯函数,零依赖,可单测)。\r\n *\r\n * 分离出来的原因:徽标语义(高峰的「无任务 / 拦截中 / 畅跑中」三态、谷时/周末)需要单测,\r\n * 而组件文件是 TSX(含 React 运行时)。这里只有字符串逻辑。\r\n */\r\n\r\n/** /session-guard/status 返回的全局阶段(组件与测试共用)。 */\r\nexport interface Status {\r\n phase: 'peak' | 'off-peak' | 'weekend'\r\n enabled: boolean\r\n weekendMode: boolean\r\n /** 官方源二维判定是否开启(开启时高峰期只拦官方源)。 */\r\n providerGuard?: boolean\r\n /** 拦截列表里的模型数(> 0 = 有拦截任务)。 */\r\n modelPatterns?: number\r\n /** 当前被挂起的请求数(hold 模式)。 */\r\n held?: number\r\n /** error 模式记下的延后会话数。 */\r\n deferred?: number\r\n /** 当前被 step 门挂起的会话数(v0.2.0)。 */\r\n stepHeld?: number\r\n timezone: string\r\n}\r\n\r\n/** 阶段文案。 */\r\nexport const PHASE_LABELS = {\r\n peak: '高峰',\r\n 'off-peak': '谷时',\r\n weekend: '周末',\r\n} as const\r\n\r\n/** 徽标要表达的任务态(高峰期的三种文案)。 */\r\nexport interface BadgeTasks {\r\n /** 拦截列表里有模型(= 有拦截任务)。 */\r\n intercepting?: boolean\r\n /** 本会话有畅跑任务。 */\r\n freeRun?: boolean\r\n}\r\n\r\n/**\r\n * 徽标文案(v3.1.6):\r\n * - 非高峰 → 阶段文案(谷时 / 周末);\r\n * - 高峰 + 有畅跑任务 → `高峰 · 畅跑中`;\r\n * - 高峰 + 有拦截任务 → `高峰 · 拦截中`;\r\n * - 高峰 + 都没任务 → `高峰`。\r\n *\r\n * 畅跑优先于拦截:畅跑是「本会话无视峰谷照常跑」,比「有拦截规则」更直接地描述此刻状态。\r\n */\r\nexport function peakLabel(status: Pick<Status, 'phase'>, tasks: BadgeTasks = {}): string {\r\n if (status.phase !== 'peak') return PHASE_LABELS[status.phase]\r\n if (tasks.freeRun === true) return '高峰 · 畅跑中'\r\n if (tasks.intercepting === true) return '高峰 · 拦截中'\r\n return '高峰'\r\n}\r\n\r\n/**\r\n * 徽标的 CSS 类:**每个阶段都是同一个按钮**(`sg-status sg-status-btn` + 配色变体)。\r\n *\r\n * 这条不变量要钉住:高峰(拦截)与谷时/周末(放行)**只有文案与颜色变**,交互形态一致 ——\r\n * 都是可点击按钮,点开同一个「高峰拦截列表」面板,绝不因为不是高峰就退化成不可点的展示元素。\r\n */\r\nexport function badgeClassNames(status: Pick<Status, 'phase'>): string {\r\n const variant = status.phase === 'peak' ? 'sg-peak' : status.phase === 'weekend' ? 'sg-weekend' : 'sg-off'\r\n return `sg-status sg-status-btn ${variant}`\r\n}\r\n\r\n/** 悬浮说明:把判定口径与当前挂起/延后数量写清楚。 */\r\nexport function badgeTitle(status: Status): string {\r\n const base = `${PHASE_LABELS[status.phase]} · ${status.timezone}${status.weekendMode ? ' · 周末模式' : ''}`\r\n if (status.phase !== 'peak') return base\r\n const mode = status.providerGuard === true ? '仅拦截 DeepSeek 官方源' : '全部会话暂停(未启用二维判定)'\r\n const held = status.held ?? 0\r\n const deferred = status.deferred ?? 0\r\n const stepHeld = status.stepHeld ?? 0\r\n const extra = held > 0 || deferred > 0 || stepHeld > 0\r\n ? ` · 挂起 ${held} · 延后 ${deferred} · step 挂起 ${stepHeld}`\r\n : ''\r\n return `${base} · ${mode}${extra}`\r\n}\r\n","/**\r\n * dsh-offpeak — 「畅跑」按钮文案与状态投影(纯函数,零依赖,可单测)。\r\n *\r\n * 交互(用户定案):**点击按钮一律打开任务管理面板**,面板里可以新建 / 暂停 /\r\n * 删除畅跑任务。因此按钮文案是**固定**的「畅跑」(任务数 >1 时带 `×N`),\r\n * 不再用文案表示生效状态——生效状态由按钮高亮色 + 悬浮提示表达,\r\n * 每段的「暂停 / 恢复」在面板里逐行操作。\r\n *\r\n * 这样也解决了上一版的死角:旧版「畅跑中」点击是直接暂停,导致面板在生效期间\r\n * 不可达;现在点击永远是打开面板。\r\n *\r\n * 本文件只有字符串与状态投影逻辑,不含 React;组件在 free-run-button.tsx。\r\n */\r\n\r\n/** 与主机 `FREE_RUN_STATES` 对齐。 */\r\nexport type FreeRunStateName = 'none' | 'scheduled' | 'active' | 'suspended' | 'expired'\r\n\r\n/** 单段状态(含每段暂停)。 */\r\nexport type FreeRunWindowStatus = 'scheduled' | 'active' | 'paused' | 'ended'\r\n\r\n/** 单个畅跑时段(`/session-guard/state` 的 `freeRun.windows[]` 形状)。 */\r\nexport interface FreeRunWindow {\r\n id: string\r\n from: string\r\n to: string\r\n fromInput: string\r\n toInput: string\r\n fromDisplay: string\r\n toDisplay: string\r\n paused: boolean\r\n status: FreeRunWindowStatus\r\n}\r\n\r\n/** `/session-guard/state` 的 `freeRun` 字段。 */\r\nexport interface FreeRunView {\r\n state: FreeRunStateName\r\n /** 当前是否有未暂停的时段正在生效(按钮高亮用)。 */\r\n active: boolean\r\n available: boolean\r\n timezone: string\r\n windows: FreeRunWindow[]\r\n activeId: string | null\r\n msRemaining: number | null\r\n nextStartMs: number | null\r\n nextStartDisplay: string | null\r\n}\r\n\r\n/** 空视图(请求失败 / 未加载时的安全默认)。 */\r\nexport const EMPTY_FREE_RUN: FreeRunView = {\r\n state: 'none',\r\n active: false,\r\n available: false,\r\n timezone: '',\r\n windows: [],\r\n activeId: null,\r\n msRemaining: null,\r\n nextStartMs: null,\r\n nextStartDisplay: null,\r\n}\r\n\r\n/** 按钮文案:固定「畅跑」,多段时带计数。 */\r\nexport function freeRunLabel(view: FreeRunView | null | undefined): string {\r\n const count = view?.windows?.length ?? 0\r\n return count > 1 ? `畅跑 ×${count}` : '畅跑'\r\n}\r\n\r\n/** 悬浮提示:当前是否生效 + 逐条排期(多任务的重要信息出口)。 */\r\nexport function freeRunTitle(view: FreeRunView | null | undefined): string {\r\n const tz = view?.timezone !== undefined && view.timezone !== '' ? view.timezone : '本地时区'\r\n const lines: string[] = ['点击打开畅跑任务管理']\r\n if (view?.active === true) {\r\n const left = remainingText(view?.msRemaining ?? null)\r\n lines.unshift(left === null ? '畅跑生效中' : `畅跑生效中(剩 ${left})`)\r\n } else if ((view?.windows?.length ?? 0) > 0) {\r\n const next = view?.nextStartDisplay ?? null\r\n lines.unshift(next === null ? '畅跑未生效' : `畅跑未生效,下一段 ${next}`)\r\n }\r\n const windows = view?.windows ?? []\r\n if (windows.length > 0) {\r\n lines.push('—— 畅跑任务 ——')\r\n for (const w of windows) {\r\n lines.push(`${w.fromDisplay} – ${w.toDisplay} ${statusLabel(w.status)}`)\r\n }\r\n } else {\r\n lines.push(`还没有畅跑任务(时间按 ${tz})`)\r\n }\r\n return lines.join('\\n')\r\n}\r\n\r\n/** 单段状态标签。 */\r\nexport function statusLabel(status: FreeRunWindowStatus): string {\r\n if (status === 'active') return '进行中'\r\n if (status === 'paused') return '已暂停'\r\n if (status === 'scheduled') return '待开始'\r\n return '已结束'\r\n}\r\n\r\n/** 该行是否显示「暂停」图标(已结束的段无从暂停)。 */\r\nexport function canPause(w: FreeRunWindow): boolean {\r\n return w.status !== 'ended' && w.paused !== true\r\n}\r\n\r\n/** 该行是否显示「恢复」图标。 */\r\nexport function canResume(w: FreeRunWindow): boolean {\r\n return w.status !== 'ended' && w.paused === true\r\n}\r\n\r\n/** 未结束(可操作)的任务。 */\r\nexport function liveWindows(view: FreeRunView | null | undefined): FreeRunWindow[] {\r\n return (view?.windows ?? []).filter((w) => w.status !== 'ended')\r\n}\r\n\r\n/** 是否所有未结束的任务都已被暂停(决定顶部按钮显示「暂停全部任务」还是「恢复全部任务」)。 */\r\nexport function allTasksPaused(view: FreeRunView | null | undefined): boolean {\r\n const live = liveWindows(view)\r\n return live.length > 0 && live.every((w) => w.paused === true)\r\n}\r\n\r\n/** 顶部第二个文本按钮的文案。 */\r\nexport function pauseAllLabel(view: FreeRunView | null | undefined): string {\r\n return allTasksPaused(view) ? '恢复全部任务' : '暂停全部任务'\r\n}\r\n\r\n/** 顶部三个文本按钮是否可用(没有任务时后两个禁用)。 */\r\nexport function toolbarDisabled(view: FreeRunView | null | undefined): boolean {\r\n return liveWindows(view).length === 0\r\n}\r\n\r\n/**\r\n * 图标字形。带 `\\uFE0E`(text presentation)强制按文字渲染,\r\n * 避免 `⏸` 在部分平台被渲染成彩色 emoji。零图标库依赖。\r\n */\r\nexport const ICON_PAUSE = '⏸\\uFE0E'\r\nexport const ICON_RESUME = '▶\\uFE0E'\r\nexport const ICON_DELETE = '×'\r\n\r\n/** 每行暂停 / 恢复图标按钮的悬浮提示。 */\r\nexport function rowPauseTitle(w: FreeRunWindow): string {\r\n return w.paused === true ? '恢复这一段畅跑' : '暂停这一段畅跑(其他段不受影响)'\r\n}\r\n\r\n/** 每行删除图标按钮的悬浮提示。 */\r\nexport function rowDeleteTitle(): string {\r\n return '删除这一段畅跑'\r\n}\r\n\r\n/** 剩余时长文案(中文,粗粒度)。 */\r\nexport function remainingText(ms: number | null): string | null {\r\n if (ms === null || !Number.isFinite(ms) || ms <= 0) return null\r\n const totalMinutes = Math.ceil(ms / 60_000)\r\n if (totalMinutes < 60) return `${totalMinutes} 分钟`\r\n const hours = Math.floor(totalMinutes / 60)\r\n const minutes = totalMinutes % 60\r\n return minutes === 0 ? `${hours} 小时` : `${hours} 小时 ${minutes} 分`\r\n}\r\n\r\n/** 从 `/session-guard/state` 响应体里取出 `freeRun`(形状异常 → 安全空视图)。 */\r\nexport function readFreeRun(body: unknown): FreeRunView {\r\n const b = (body ?? {}) as { ok?: unknown; freeRun?: unknown }\r\n if (b.ok !== true) return EMPTY_FREE_RUN\r\n const f = b.freeRun\r\n if (f === null || typeof f !== 'object') return EMPTY_FREE_RUN\r\n const raw = f as Partial<FreeRunView>\r\n const windows = Array.isArray(raw.windows)\r\n ? (raw.windows.filter((w) => w !== null && typeof w === 'object') as FreeRunWindow[])\r\n : []\r\n return {\r\n state: typeof raw.state === 'string' ? (raw.state as FreeRunStateName) : 'none',\r\n active: raw.active === true,\r\n available: raw.available === true,\r\n timezone: typeof raw.timezone === 'string' ? raw.timezone : '',\r\n windows,\r\n activeId: typeof raw.activeId === 'string' ? raw.activeId : null,\r\n msRemaining: typeof raw.msRemaining === 'number' ? raw.msRemaining : null,\r\n nextStartMs: typeof raw.nextStartMs === 'number' ? raw.nextStartMs : null,\r\n nextStartDisplay: typeof raw.nextStartDisplay === 'string' ? raw.nextStartDisplay : null,\r\n }\r\n}\r\n","/**\r\n * dsh-offpeak — 日期范围选择的纯逻辑(零依赖,可单测)。\r\n *\r\n * 供 `date-range-picker.tsx` 使用:双月日历的网格、范围高亮、默认值播种。\r\n * 全部按**本地日期**(年/月/日)运算,不做时区换算 —— 选出来的字符串是要交给\r\n * 主机按配置时区解释的墙钟日期。\r\n *\r\n * 约定:一周从**周一**开始(与 DSH 排期面板一致:一 二 三 四 五 六 日)。\r\n */\r\n\r\n/** `YYYY-MM-DD` 形态。 */\r\nexport interface Ymd {\r\n y: number\r\n m: number // 1-12\r\n d: number // 1-31\r\n}\r\n\r\nconst WEEKDAY_LABELS = ['一', '二', '三', '四', '五', '六', '日']\r\n\r\n/** 周一到周日的表头。 */\r\nexport function weekdayLabels(): string[] {\r\n return [...WEEKDAY_LABELS]\r\n}\r\n\r\n/** 两位补零。 */\r\nfunction pad2(n: number): string {\r\n return String(n).padStart(2, '0')\r\n}\r\n\r\n/** `Ymd` → `YYYY-MM-DD`。 */\r\nexport function toIso({ y, m, d }: Ymd): string {\r\n return `${y}-${pad2(m)}-${pad2(d)}`\r\n}\r\n\r\n/** `YYYY-MM-DD` → `Ymd`;非法返回 null。 */\r\nexport function fromIso(value: string | null | undefined): Ymd | null {\r\n const m = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(typeof value === 'string' ? value : '')\r\n if (m === null) return null\r\n const y = Number(m[1])\r\n const mo = Number(m[2])\r\n const d = Number(m[3])\r\n if (mo < 1 || mo > 12 || d < 1 || d > 31) return null\r\n return { y, m: mo, d }\r\n}\r\n\r\n/** 该月天数(用「下个月 0 号」拿到,天然处理闰年)。 */\r\nexport function daysInMonth(y: number, m: number): number {\r\n return new Date(y, m, 0).getDate()\r\n}\r\n\r\n/** 该月 1 号是周几(0=周日…6=周六)。 */\r\nfunction firstWeekday(y: number, m: number): number {\r\n return new Date(y, m - 1, 1).getDay()\r\n}\r\n\r\n/** 月份偏移(可跨年)。 */\r\nexport function addMonths({ y, m }: Pick<Ymd, 'y' | 'm'>, delta: number): { y: number; m: number } {\r\n const total = y * 12 + (m - 1) + delta\r\n return { y: Math.floor(total / 12), m: (total % 12) + 1 }\r\n}\r\n\r\n/**\r\n * 该月的日历网格:周一开头,长度固定为 7 的倍数(可能 5 或 6 周)。\r\n * 非本月的格子用 `inMonth: false` 标出(界面上淡显)。\r\n */\r\nexport function monthGrid(y: number, m: number): Array<{ day: number; iso: string; inMonth: boolean }> {\r\n const lead = (firstWeekday(y, m) + 6) % 7 // 周一=0\r\n const total = daysInMonth(y, m)\r\n const cells: Array<{ day: number; iso: string; inMonth: boolean }> = []\r\n // 上月补位\r\n const prev = addMonths({ y, m }, -1)\r\n const prevTotal = daysInMonth(prev.y, prev.m)\r\n for (let i = lead - 1; i >= 0; i -= 1) {\r\n const d = prevTotal - i\r\n cells.push({ day: d, iso: toIso({ y: prev.y, m: prev.m, d }), inMonth: false })\r\n }\r\n for (let d = 1; d <= total; d += 1) cells.push({ day: d, iso: toIso({ y, m, d }), inMonth: true })\r\n // 下月补位到整周\r\n const next = addMonths({ y, m }, 1)\r\n let d = 1\r\n while (cells.length % 7 !== 0) {\r\n cells.push({ day: d, iso: toIso({ y: next.y, m: next.m, d }), inMonth: false })\r\n d += 1\r\n }\r\n return cells\r\n}\r\n\r\n/** 月份标题,如 `2026年 9月`。 */\r\nexport function monthTitle(y: number, m: number): string {\r\n return `${y}年 ${m}月`\r\n}\r\n\r\n/** 日期先后比较:a<b → -1,相等 → 0,a>b → 1(字符串直接比即可,形态定长)。 */\r\nexport function compareIso(a: string, b: string): number {\r\n if (a === b) return 0\r\n return a < b ? -1 : 1\r\n}\r\n\r\n/** 该日是否落在 `[from, to]` 闭区间内(任一端为空则不算命中)。 */\r\nexport function inRange(iso: string, from: string, to: string): boolean {\r\n if (from === '' || to === '') return false\r\n const [lo, hi] = compareIso(from, to) <= 0 ? [from, to] : [to, from]\r\n return compareIso(iso, lo) >= 0 && compareIso(iso, hi) <= 0\r\n}\r\n\r\n/** 是否区间的端点。 */\r\nexport function isEdge(iso: string, from: string, to: string): boolean {\r\n return iso === from || iso === to\r\n}\r\n\r\n/**\r\n * 小时选择的语义:**按小时包含**。\r\n *\r\n * - **开始**小时 H → `H:00`(从这一小时的开头开始);\r\n * - **结束**小时 H → `H:59`(把这一小时**整个包含**进去)。\r\n *\r\n * 为什么结束取 `:59` 而不是 `:00`:主机侧的窗口是半开的 `[from, to)`。若结束也取 `:00`,\r\n * 选「23 时」就会得到 `23:00`,当天最后一小时(23:00–24:00)根本选不进来;取 `:59`\r\n * 后「开始 12 时 → 结束 14 时」= `12:00 → 14:59`,正好覆盖 12、13、14 三个小时,\r\n * 而「结束 23 时」= `23:59`,即当天最后一刻。\r\n */\r\nexport const END_MINUTE = 59\r\n\r\n/** 把「日期 + 小时」拼成主机要的墙钟串(按配置时区解释):开始 = `H:00`。 */\r\nexport function combineStart(date: string, hour: string): string {\r\n return `${date}T${pad2(Number(hour))}:00`\r\n}\r\n\r\n/** 把「日期 + 小时」拼成主机要的墙钟串:结束 = `H:59`(含该小时)。 */\r\nexport function combineEnd(date: string, hour: string): string {\r\n return `${date}T${pad2(Number(hour))}:${pad2(END_MINUTE)}`\r\n}\r\n\r\n/**\r\n * 默认区间:**开始 = 当前时刻**(本小时整点),结束 = 下一个小时(按上面的「含该小时」\r\n * 语义即覆盖 2 个自然小时;跨天/跨月/跨年会顺延到下一天)。\r\n *\r\n * 与用户要求一致:「默认开始时间是当前时刻」。返回 `YYYY-MM-DD` 与 `HH` 四段,\r\n * 直接喂给日历 + 小时下拉。\r\n */\r\nexport function seedRange(now: Date): { fromDate: string; fromHour: string; toDate: string; toHour: string } {\r\n const start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), 0, 0, 0)\r\n // 结束小时 = 开始小时 + 1(显示用的小时;配合 `:59` 语义 = 覆盖开始那一小时与下一小时)。\r\n const end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours() + 1, 0, 0, 0)\r\n return {\r\n fromDate: toIso({ y: start.getFullYear(), m: start.getMonth() + 1, d: start.getDate() }),\r\n fromHour: pad2(start.getHours()),\r\n toDate: toIso({ y: end.getFullYear(), m: end.getMonth() + 1, d: end.getDate() }),\r\n toHour: pad2(end.getHours()),\r\n }\r\n}\r\n\r\n/**\r\n * 一次点击后的范围状态机:\r\n * - 还没选起点,或已有完整区间 → 该点成为**新起点**,终点清空(等待第二次点击);\r\n * - 已有起点、没有终点 → 该点成为终点;若它在起点之前,则两者对调(允许反着选)。\r\n */\r\nexport function nextRange(\r\n from: string,\r\n to: string,\r\n picked: string,\r\n): { from: string; to: string; done: boolean } {\r\n if (from === '' || to !== '') return { from: picked, to: '', done: false }\r\n return compareIso(picked, from) >= 0\r\n ? { from, to: picked, done: true }\r\n : { from: picked, to: from, done: true }\r\n}\r\n\r\n/** 触发器上显示的区间文案(未选满时给占位)。 */\r\nexport function rangeLabel(from: string, to: string): { text: string; placeholder: boolean } {\r\n if (from !== '' && to !== '') return { text: `${from} → ${to}`, placeholder: false }\r\n if (from !== '') return { text: `${from} → 结束日期`, placeholder: true }\r\n return { text: '开始日期 → 结束日期', placeholder: true }\r\n}\r\n","/**\r\n * dsh-offpeak — 日期范围选择器(双月日历,风格对齐 DSH 排期面板)。\r\n *\r\n * 交互:\r\n * - 触发器是一行 `开始日期 → 结束日期` + 日历图标;\r\n * - 点开后是两个并排月份,`‹‹ ‹ 2026年 9月 2026年 10月 › ››`;\r\n * - 第一次点选起点、第二次点选终点(反着点会自动对调);选满即收起;\r\n * - 已有完整区间时再点一次 = 重新开始选。\r\n *\r\n * 只有日期;**小时**由调用方在日历旁另配下拉(保持「到小时」的精度)。\r\n * 纯展示 + 受控回调,不做校验(非法组合由主机侧拒绝并回报错误)。\r\n */\r\nimport { useEffect, useRef, useState } from 'react'\r\nimport {\r\n addMonths,\r\n compareIso,\r\n fromIso,\r\n inRange,\r\n isEdge,\r\n monthGrid,\r\n monthTitle,\r\n nextRange,\r\n rangeLabel,\r\n seedRange,\r\n weekdayLabels,\r\n} from './date-range'\r\n\r\nexport interface DateRangePickerProps {\r\n from: string\r\n to: string\r\n /** 任一端变化都会回调(`YYYY-MM-DD`;未选满时另一侧为 '')。 */\r\n onChange: (_from: string, _to: string) => void\r\n disabled?: boolean\r\n}\r\n\r\n/** 双月范围日历。 */\r\nexport function DateRangePicker({ from, to, onChange, disabled = false }: DateRangePickerProps) {\r\n const [open, setOpen] = useState(false)\r\n // 「今天」的 ISO 走既有的 seedRange(`Date` → `YYYY-MM-DD` 的转换只在那一处实现)。\r\n const todayIso = seedRange(new Date()).fromDate\r\n const [anchor, setAnchor] = useState(() => {\r\n const seed = fromIso(from) ?? fromIso(to) ?? fromIso(todayIso)!\r\n return { y: seed.y, m: seed.m }\r\n })\r\n const rootRef = useRef<HTMLDivElement | null>(null)\r\n\r\n // 外部值变化时把视图跟到起点所在月(只在「打开」这一下跟随,\r\n // 避免用户翻月时被立刻拽回去)。\r\n useEffect(() => {\r\n const seed = fromIso(from) ?? fromIso(to)\r\n if (seed !== null && open) setAnchor({ y: seed.y, m: seed.m })\r\n }, [open])\r\n\r\n // 点外面 / Esc 收起。\r\n useEffect(() => {\r\n if (!open) return\r\n const onDown = (ev: MouseEvent): void => {\r\n const el = rootRef.current\r\n if (el !== null && ev.target instanceof Node && !el.contains(ev.target)) setOpen(false)\r\n }\r\n const onKey = (ev: KeyboardEvent): void => {\r\n if (ev.key === 'Escape') setOpen(false)\r\n }\r\n document.addEventListener('mousedown', onDown)\r\n document.addEventListener('keydown', onKey)\r\n return () => {\r\n document.removeEventListener('mousedown', onDown)\r\n document.removeEventListener('keydown', onKey)\r\n }\r\n }, [open])\r\n\r\n const label = rangeLabel(from, to)\r\n const right = addMonths(anchor, 1)\r\n\r\n function pick(iso: string): void {\r\n const r = nextRange(from, to, iso)\r\n onChange(r.from, r.to)\r\n if (r.done) setOpen(false)\r\n }\r\n\r\n function cellClass(iso: string, inMonth: boolean): string {\r\n const parts = ['sg-dr-day']\r\n if (!inMonth) parts.push('sg-dr-out')\r\n if (inRange(iso, from, to)) parts.push('sg-dr-in')\r\n if (isEdge(iso, from, to) && compareIso(from, to) !== 0) parts.push('sg-dr-edge')\r\n else if (isEdge(iso, from, to)) parts.push('sg-dr-edge', 'sg-dr-single')\r\n if (iso === todayIso) parts.push('sg-dr-today')\r\n return parts.join(' ')\r\n }\r\n\r\n function renderMonth(y: number, m: number) {\r\n const cells = monthGrid(y, m)\r\n return (\r\n <div className=\"sg-dr-month\">\r\n <div className=\"sg-dr-mtitle\">{monthTitle(y, m)}</div>\r\n <div className=\"sg-dr-grid\">\r\n {weekdayLabels().map((w) => (\r\n <span key={w} className=\"sg-dr-wd\">\r\n {w}\r\n </span>\r\n ))}\r\n {cells.map((c) => (\r\n <button\r\n key={c.iso}\r\n type=\"button\"\r\n className={cellClass(c.iso, c.inMonth)}\r\n disabled={disabled}\r\n onClick={() => pick(c.iso)}\r\n >\r\n {c.day}\r\n </button>\r\n ))}\r\n </div>\r\n </div>\r\n )\r\n }\r\n\r\n return (\r\n <div className=\"sg-dr-root\" ref={rootRef}>\r\n <button\r\n type=\"button\"\r\n className={`sg-dr-trigger ${label.placeholder ? 'sg-dr-ph' : ''}`}\r\n disabled={disabled}\r\n aria-expanded={open}\r\n onClick={() => setOpen((p) => !p)}\r\n >\r\n <span className=\"sg-dr-text\">{label.text}</span>\r\n <span className=\"sg-dr-icon\" aria-hidden=\"true\">\r\n {/* 极简日历字形,零图标依赖 */}\r\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\">\r\n <rect x=\"2\" y=\"3\" width=\"12\" height=\"11\" rx=\"2\" stroke=\"currentColor\" strokeWidth=\"1.3\" />\r\n <path d=\"M2 6.5h12M5.5 2v2.5M10.5 2v2.5\" stroke=\"currentColor\" strokeWidth=\"1.3\" strokeLinecap=\"round\" />\r\n </svg>\r\n </span>\r\n </button>\r\n\r\n {open && (\r\n <div className=\"sg-dr-panel\" role=\"dialog\" aria-label=\"选择日期范围\">\r\n <div className=\"sg-dr-head\">\r\n <button\r\n type=\"button\"\r\n className=\"sg-dr-nav\"\r\n title=\"上一年\"\r\n onClick={() => setAnchor((a) => addMonths(a, -12))}\r\n >\r\n ‹‹\r\n </button>\r\n <button type=\"button\" className=\"sg-dr-nav\" title=\"上个月\" onClick={() => setAnchor((a) => addMonths(a, -1))}>\r\n ‹\r\n </button>\r\n <span className=\"sg-dr-spacer\" />\r\n <button type=\"button\" className=\"sg-dr-nav\" title=\"下个月\" onClick={() => setAnchor((a) => addMonths(a, 1))}>\r\n ›\r\n </button>\r\n <button\r\n type=\"button\"\r\n className=\"sg-dr-nav\"\r\n title=\"下一年\"\r\n onClick={() => setAnchor((a) => addMonths(a, 12))}\r\n >\r\n ››\r\n </button>\r\n </div>\r\n <div className=\"sg-dr-months\">\r\n {renderMonth(anchor.y, anchor.m)}\r\n {renderMonth(right.y, right.m)}\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n )\r\n}\r\n","/**\r\n * dsh-offpeak — 客户端样式(与 composer 右侧 input-traffic 冻结按钮同一视觉语言)。\r\n *\r\n * 为什么注入 `<style>` 而不是 CSS Modules:本插件的 tsdown 配置没有 CSS Modules 管线\r\n * (input-traffic 有),而 settings-card 已经用 `<style data-plugin-css>` 的既有约定。\r\n *\r\n * 覆盖两块:\r\n * - 「畅跑」按钮(四态:默认 / 畅跑中 / 畅跑暂停)与状态徽标,对齐到同一行的其它控件\r\n * (高度 24px、圆角 6px、12px 字号、同样的 border / hover 令牌);\r\n * - 畅跑任务的**管理弹层**(列表 + 日期/小时选择器 + 操作按钮),同样只用原生控件。\r\n */\r\n\r\n/** 与 `dsh-input-traffic/src/client/freeze-button.module.css` 对齐的控件外观。 */\r\nexport const CLIENT_CSS = `\r\n.sg-fr-root{position:relative;display:inline-flex;flex:none}\r\n.sg-fr{display:inline-flex;align-items:center;gap:4px;height:24px;padding:0 8px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:transparent;color:var(--dsw-alias-label-secondary,#6b7280);font-size:12px;line-height:1;cursor:pointer;white-space:nowrap;flex:none}\r\n.sg-fr:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.04))}\r\n.sg-fr:disabled{opacity:.55;cursor:default}\r\n.sg-fr[aria-pressed='true'],.sg-fr-active{border-color:var(--dsw-alias-state-success-primary,#30a46c);color:var(--dsw-alias-state-success-primary,#30a46c);background:color-mix(in srgb,var(--dsw-alias-state-success-primary,#30a46c) 8%,transparent)}\r\n.sg-fr-suspended{border-color:var(--dsw-alias-state-warning-primary,#d97706);color:var(--dsw-alias-state-warning-primary,#d97706);background:color-mix(in srgb,var(--dsw-alias-state-warning-primary,#d97706) 8%,transparent)}\r\n.sg-status{display:inline-flex;align-items:center;height:24px;padding:0 8px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:transparent;font-size:12px;line-height:1;color:var(--dsw-alias-label-secondary,#6b7280);white-space:nowrap;flex:none}\r\n.sg-status.sg-peak{border-color:var(--dsw-alias-state-error-primary,#dc2626);color:var(--dsw-alias-state-error-primary,#dc2626)}\r\n.sg-status.sg-off{border-color:var(--dsw-alias-state-success-primary,#30a46c);color:var(--dsw-alias-state-success-primary,#30a46c)}\r\n.sg-status.sg-weekend{border-color:var(--dsw-alias-state-success-primary,#30a46c);color:var(--dsw-alias-state-success-primary,#30a46c)}\r\n/* 徽标即按钮(v3.1.4):所有阶段同一个按钮;v3.1.6 去掉了灰色填充与按钮内箭头 */\r\n.sg-status-root{position:relative;display:inline-flex;flex:none}\r\n.sg-status.sg-status-btn{cursor:pointer}\r\n.sg-status.sg-status-btn:hover{border-color:var(--dsw-alias-border-l4,rgba(0,0,0,.28))}\r\n.sg-status-caret{flex:none;line-height:1;transition:transform .16s}\r\n.sg-status-caret-open{transform:rotate(180deg)}\r\n/* 「高峰拦截列表」面板(.sgMl* 由状态徽标与设置表单共用同一份样式) */\r\n.sgMlBody{margin-top:8px;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));border-radius:8px;background:var(--dsw-alias-bg-layer-2,transparent)}\r\n.sgMlBody *{box-sizing:border-box}\r\n.sgMlFilterRow{display:flex;align-items:center;gap:6px;padding-bottom:6px;flex-wrap:wrap}\r\n.sgMlInput{flex:1 1 160px;min-width:0;height:24px;padding:0 8px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:var(--dsw-alias-bg-layer-2,transparent);color:inherit;font:inherit;font-size:12px}\r\n.sgMlInput:disabled{opacity:.6;cursor:not-allowed}\r\n.sgMlError{color:var(--dsw-alias-state-error-primary,#dc2626);font-size:12px;line-height:18px;margin:6px 0 0;word-break:break-word}\r\n.sgMlPatterns{display:flex;align-items:center;flex-wrap:wrap;gap:6px;padding-bottom:6px;border-bottom:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.08))}\r\n.sgMlSentinel{color:var(--dsw-alias-label-tertiary,#6b7280);font-size:12px}\r\n.sgMlChip{font-size:12px;padding:1px 6px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:4px;font-variant-numeric:tabular-nums}\r\n.sgMlRefresh{margin-left:auto;flex:none;height:22px;padding:0 8px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:transparent;color:inherit;font:inherit;font-size:12px;cursor:pointer}\r\n.sgMlRefresh:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06))}\r\n.sgMlRefresh:disabled{opacity:.55;cursor:default}\r\n.sgMlBtn{flex:none;height:24px;padding:0 8px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:transparent;color:inherit;font:inherit;font-size:12px;cursor:pointer}\r\n.sgMlBtn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06))}\r\n.sgMlBtn:disabled{opacity:.55;cursor:default}\r\n.sgMlChipUndo{margin-left:4px;padding:0 2px;border:0;background:transparent;color:inherit;font:inherit;font-size:12px;line-height:1;cursor:pointer;opacity:.7}\r\n.sgMlChipUndo:hover:not(:disabled){opacity:1;color:var(--dsw-alias-state-error-primary,#dc2626)}\r\n.sgMlChipUndo:disabled{opacity:.4;cursor:default}\r\n.sgMlHead{display:flex;align-items:baseline;gap:8px;margin:10px 0 4px}\r\n/* 畅跑任务区内嵌在徽标面板最上方(v3.1.6) */\r\n.sg-fr-section{padding-bottom:8px;border-bottom:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.08))}\r\n.sg-fr-section .sg-fr-head{margin-bottom:6px}\r\n.sgMlList{list-style:none;margin:0;padding:0;max-height:240px;overflow:auto}\r\n.sgMlProvider{margin:8px 0 2px;font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary,#6b7280)}\r\n.sgMlItem{display:flex;align-items:center;padding:2px 0}\r\n.sgMlCheck{display:flex;align-items:center;gap:6px;min-width:0;font-size:12px;cursor:pointer}\r\n.sgMlCheck>input{cursor:pointer;flex:none;margin-left:auto}\r\n.sgMlCheck>input:disabled{cursor:not-allowed}\r\n.sgMlCovered{color:var(--dsw-alias-state-error-primary,#dc2626)}\r\n.sgMlDot{flex:none;line-height:1}\r\n.sgMlModel{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\r\n.sgMlTag{flex:none;font-size:11px;color:var(--dsw-alias-label-tertiary,#6b7280);border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.08));border-radius:4px;padding:0 4px}\r\n.sgMlHint{color:var(--dsw-alias-label-tertiary,#6b7280);font-size:12px;line-height:18px;margin:6px 0 0}\r\n.sg-fr-panel,.sg-status-panel{position:absolute;bottom:calc(100% + 8px);right:0;z-index:40;width:min(448px,calc(100vw - 24px));max-height:min(70vh,560px);overflow:auto;overscroll-behavior:contain;box-sizing:border-box;padding:10px 12px 12px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));border-radius:10px;background:var(--dsw-specific-input-major,#fff);box-shadow:var(--dsw-elevation-panel,0 6px 24px rgba(0,0,0,.16));color:var(--dsw-alias-label-primary,#111);font-size:12px;line-height:1.5}\r\n.sg-status-panel{width:min(380px,calc(100vw - 24px));z-index:45}\r\n.sg-status-panel *{box-sizing:border-box}\r\n.sg-fr-panel *{box-sizing:border-box}\r\n.sg-fr-head{display:flex;align-items:baseline;gap:8px;font-size:13px;font-weight:600;margin-bottom:8px}\r\n.sg-fr-title{flex:1;min-width:0}\r\n.sg-fr-tz{font-weight:400;font-size:11px;color:var(--dsw-alias-label-tertiary,#6b7280)}\r\n.sg-fr-closeicon{flex:none;width:20px;height:20px;line-height:1;padding:0;border:0;border-radius:4px;background:transparent;color:var(--dsw-alias-label-tertiary,#6b7280);cursor:pointer;font-size:14px;font-weight:400}\r\n.sg-fr-closeicon:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06));color:var(--dsw-alias-label-primary,#111)}\r\n.sg-fr-bar{display:flex;gap:6px;margin-bottom:8px}\r\n.sg-fr-bar-btn{flex:1;min-width:0;height:26px;padding:0 6px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:transparent;color:var(--dsw-alias-label-secondary,#6b7280);font:inherit;font-size:12px;line-height:1;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\r\n.sg-fr-bar-btn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06));color:var(--dsw-alias-label-primary,#111)}\r\n.sg-fr-bar-btn:disabled{opacity:.5;cursor:default}\r\n.sg-fr-bar-on{border-color:var(--dsw-alias-button-primary-fill,#4d6bfe);color:var(--dsw-alias-button-primary-fill,#4d6bfe)}\r\n.sg-fr-bar-danger:hover:not(:disabled){border-color:var(--dsw-alias-state-error-primary,#dc2626);color:var(--dsw-alias-state-error-primary,#dc2626)}\r\n.sg-fr-empty{color:var(--dsw-alias-label-tertiary,#6b7280);padding:2px 0 8px}\r\n.sg-fr-list{list-style:none;margin:0 0 8px;padding:0;max-height:168px;overflow:auto}\r\n.sg-fr-item{display:flex;align-items:center;gap:6px;padding:4px 0;border-bottom:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.06))}\r\n.sg-fr-range{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-variant-numeric:tabular-nums}\r\n.sg-fr-tag{flex:none;font-size:11px;padding:0 4px;border-radius:4px;border:1px solid transparent}\r\n.sg-fr-tag-active{border-color:var(--dsw-alias-state-success-primary,#30a46c);color:var(--dsw-alias-state-success-primary,#30a46c)}\r\n.sg-fr-tag-scheduled{color:var(--dsw-alias-label-tertiary,#6b7280)}\r\n.sg-fr-tag-paused{border-color:var(--dsw-alias-state-warning-primary,#d97706);color:var(--dsw-alias-state-warning-primary,#d97706)}\r\n.sg-fr-tag-ended{color:var(--dsw-alias-label-dimmed,#9ca3af)}\r\n.sg-fr-icon{flex:none;width:22px;height:22px;line-height:1;padding:0;border:1px solid transparent;border-radius:4px;background:transparent;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;font-size:12px;display:inline-flex;align-items:center;justify-content:center}\r\n.sg-fr-icon:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06));color:var(--dsw-alias-label-primary,#111)}\r\n.sg-fr-icon:disabled{opacity:.4;cursor:default}\r\n.sg-fr-icon-danger:hover:not(:disabled){color:var(--dsw-alias-state-error-primary,#dc2626)}\r\n.sg-fr-form{display:flex;flex-direction:column;gap:6px;margin:2px 0 8px;padding:8px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.08));border-radius:8px}\r\n.sg-fr-form-actions{display:flex;justify-content:flex-end;gap:6px}\r\n.sg-fr-add,.sg-fr-cancel{height:24px;padding:0 10px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:transparent;color:inherit;font:inherit;font-size:12px;cursor:pointer}\r\n.sg-fr-add{border-color:var(--dsw-alias-button-primary-fill,#4d6bfe);color:var(--dsw-alias-button-primary-fill,#4d6bfe)}\r\n.sg-fr-add:hover:not(:disabled),.sg-fr-cancel:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06))}\r\n.sg-fr-add:disabled,.sg-fr-cancel:disabled{opacity:.55;cursor:default}\r\n.sg-fr-error{color:var(--dsw-alias-state-error-primary,#dc2626);margin-top:6px}\r\n/* ── 日期范围选择器(双月日历,风格对齐 DSH 排期面板)── */\r\n.sg-dr-root{position:relative}\r\n.sg-dr-trigger{display:flex;align-items:center;gap:6px;width:100%;height:28px;padding:0 8px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:var(--dsw-alias-bg-layer-2,transparent);color:inherit;font:inherit;font-size:12px;cursor:pointer;text-align:left}\r\n.sg-dr-trigger:hover:not(:disabled){border-color:var(--dsw-alias-border-l4,rgba(0,0,0,.2))}\r\n.sg-dr-trigger:disabled{opacity:.55;cursor:default}\r\n.sg-dr-text{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-variant-numeric:tabular-nums}\r\n.sg-dr-ph .sg-dr-text{color:var(--dsw-alias-label-tertiary,#6b7280)}\r\n.sg-dr-icon{flex:none;display:inline-flex;color:var(--dsw-alias-label-tertiary,#6b7280)}\r\n.sg-dr-panel{position:absolute;top:calc(100% + 6px);left:0;z-index:60;padding:8px 10px 10px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.1));border-radius:10px;background:var(--dsw-specific-input-major,#fff);box-shadow:var(--dsw-elevation-panel,0 6px 24px rgba(0,0,0,.16))}\r\n.sg-dr-head{display:flex;align-items:center;gap:2px;margin-bottom:4px}\r\n.sg-dr-spacer{flex:1}\r\n.sg-dr-nav{width:22px;height:22px;padding:0;border:0;border-radius:4px;background:transparent;color:var(--dsw-alias-label-secondary,#6b7280);font-size:12px;line-height:1;cursor:pointer}\r\n.sg-dr-nav:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06));color:var(--dsw-alias-label-primary,#111)}\r\n.sg-dr-months{display:flex;gap:14px}\r\n.sg-dr-month{width:196px}\r\n.sg-dr-mtitle{text-align:center;font-size:12px;font-weight:600;margin-bottom:4px}\r\n.sg-dr-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:1px}\r\n.sg-dr-wd{text-align:center;font-size:11px;color:var(--dsw-alias-label-tertiary,#6b7280);padding-bottom:2px}\r\n.sg-dr-day{height:24px;padding:0;border:0;border-radius:4px;background:transparent;color:inherit;font:inherit;font-size:12px;line-height:1;cursor:pointer;font-variant-numeric:tabular-nums}\r\n.sg-dr-day:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06))}\r\n.sg-dr-out{color:var(--dsw-alias-label-dimmed,#9ca3af)}\r\n.sg-dr-in{background:var(--dsw-alias-interactive-bg-selected,rgba(77,107,254,.12))}\r\n.sg-dr-edge{background:var(--dsw-alias-button-primary-fill,#4d6bfe);color:#fff;font-weight:600}\r\n.sg-dr-edge:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,#3f5ce8)}\r\n.sg-dr-today{outline:1px solid var(--dsw-alias-border-l4,rgba(0,0,0,.2));outline-offset:-1px}\r\n.sg-fr-hours{display:flex;align-items:center;gap:6px}\r\n.sg-fr-hour{display:flex;align-items:center;gap:4px;flex:1;min-width:0}\r\n.sg-fr-hour>span:first-child{flex:none;color:var(--dsw-alias-label-tertiary,#6b7280)}\r\n.sg-fr-hour>select{flex:1;min-width:0;font:inherit;font-size:12px;padding:3px 6px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:var(--dsw-alias-bg-layer-2,transparent);color:inherit}\r\n.sg-fr-unit{flex:none;color:var(--dsw-alias-label-tertiary,#6b7280)}\r\n.sg-fr-now{flex:none;height:24px;padding:0 8px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:transparent;color:inherit;font:inherit;font-size:12px;cursor:pointer}\r\n.sg-fr-now:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06))}\r\n.sg-fr-form{display:flex;flex-direction:column;gap:6px;margin:8px 0}\r\n.sg-fr-add{align-self:flex-end;height:24px;padding:0 10px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:transparent;color:inherit;font:inherit;font-size:12px;cursor:pointer}\r\n.sg-fr-add:hover:not(:disabled),.sg-fr-resume:hover:not(:disabled),.sg-fr-clear:hover:not(:disabled),.sg-fr-close:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06))}\r\n.sg-fr-add:disabled,.sg-fr-resume:disabled,.sg-fr-clear:disabled{opacity:.55;cursor:default}\r\n.sg-fr-error{color:var(--dsw-alias-state-error-primary,#dc2626);margin-bottom:6px}\r\n.sg-fr-actions{display:flex;justify-content:flex-end;gap:6px;margin-top:4px}\r\n.sg-fr-actions>button{height:24px;padding:0 10px;border:1px solid var(--dsw-alias-border-l3,rgba(0,0,0,.12));border-radius:6px;background:transparent;color:inherit;font:inherit;font-size:12px;cursor:pointer}\r\n.sg-fr-resume{border-color:var(--dsw-alias-state-success-primary,#30a46c)!important;color:var(--dsw-alias-state-success-primary,#30a46c)!important}\r\n/* ── 响应式:窄窗口不裁切、日历改弹性列(v3.1.7)── */\r\n.sg-dr-months{flex-wrap:wrap;justify-content:center;gap:10px}\r\n.sg-dr-month{width:auto;flex:1 1 168px;min-width:0}\r\n.sg-dr-panel{max-width:calc(100vw - 24px);box-sizing:border-box}\r\n.sg-fr-list,.sgMlList{overflow-x:hidden}\r\n@media (max-width:520px){\r\n /* 单月:第二个日历换行后依然可读,但优先只显示一个(隐藏第二个) */\r\n .sg-dr-month+.sg-dr-month{display:none}\r\n .sg-dr-month{flex:1 1 100%;max-width:280px}\r\n .sg-fr-panel,.sg-status-panel{right:auto;left:0;bottom:calc(100% + 6px)}\r\n .sg-fr-bar{flex-wrap:wrap}\r\n .sg-fr-bar-btn{flex:1 1 30%}\r\n .sg-fr-hours{flex-wrap:wrap}\r\n .sg-fr-hour{flex:1 1 100%}\r\n}\r\n`\r\n\r\n/** 注入一次(幂等;无 document 时静默跳过)。 */\r\nexport function injectClientCss(): void {\r\n if (typeof document === 'undefined') return\r\n if (document.querySelector('style[data-plugin-css=\"offpeak-client\"]') !== null) return\r\n const tag = document.createElement('style')\r\n tag.dataset.plugin = 'dsh-offpeak'\r\n tag.dataset.pluginCss = 'offpeak-client'\r\n tag.textContent = CLIENT_CSS\r\n document.head.appendChild(tag)\r\n}\r\n","/**\r\n * dsh-offpeak — 畅跑任务区(**内嵌在状态徽标面板最上方**,v3.1.6 起没有独立按钮)。\r\n *\r\n * 布局(用户定案,对齐原「畅跑任务管理」弹层):\r\n * - **标题行**:`畅跑任务` + `按 <时区>`;\r\n * - **三个文本按钮**:`新建畅跑任务` / `暂停全部任务`(全暂停时变 `恢复全部任务`)/ `删除全部任务`;\r\n * 「新建」内联展开起止时间选择器(开始 / 结束,到小时);\r\n * - **每行两个图标按钮**:`⏸ / ▶`(暂停 / 恢复该段)与 `×`(删除该段)。\r\n *\r\n * 打开/关闭由父层(状态徽标面板)控制:这里没有触发按钮、没有 open 状态、也不点外关闭。\r\n * 时间选择器的输入值按**配置时区**解释(主机侧解析),标题行标注该时区。\r\n *\r\n * 状态更新:5s 轮询 `/session-guard/state`(畅跑是低频交互,轮询足够且最简单)\r\n * + 本地定时器保证到点后状态及时刷新。全部 fail-open,绝不抛错。\r\n */\r\nimport { useCallback, useEffect, useState } from 'react'\r\nimport {\r\n EMPTY_FREE_RUN,\r\n ICON_DELETE,\r\n ICON_PAUSE,\r\n ICON_RESUME,\r\n allTasksPaused,\r\n canPause,\r\n canResume,\r\n pauseAllLabel,\r\n readFreeRun,\r\n rowDeleteTitle,\r\n rowPauseTitle,\r\n statusLabel,\r\n toolbarDisabled,\r\n type FreeRunView,\r\n} from './free-run-button-text'\r\nimport { DateRangePicker } from './date-range-picker'\r\nimport { combineEnd, combineStart, seedRange } from './date-range'\r\nimport { injectClientCss } from './styles'\r\n\r\n/** slot 运行时注入的会话级 props。 */\r\nexport interface FreeRunPanelProps {\r\n sessionId?: string\r\n}\r\n\r\n/** 兜底轮询间隔(畅跑是低频交互,5s 足够;到点切换另有本地定时器)。 */\r\nconst POLL_MS = 5_000\r\n\r\n// 日期 + 小时的拼接与「按小时包含」语义见 date-range.ts 的 combineStart / combineEnd:\r\n// 开始 = H:00,结束 = H:59(否则选「23 时」会变成 23:00,当天最后一小时选不进来)。\r\n\r\n/** 拆出 `YYYY-MM-DD` 与 `HH`(供选择器回填)。 */\r\nexport function splitDateTime(value: string | null | undefined): { date: string; hour: string } {\r\n const s = typeof value === 'string' ? value : ''\r\n const m = /^(\\d{4}-\\d{2}-\\d{2})T(\\d{2})/.exec(s)\r\n if (m === null) return { date: '', hour: '' }\r\n return { date: m[1], hour: m[2] }\r\n}\r\n\r\nconst HOURS = Array.from({ length: 24 }, (_, i) => String(i).padStart(2, '0'))\r\n\r\n/**\r\n * 畅跑任务区(**内嵌在状态徽标面板顶部**,不再有独立按钮)。\r\n *\r\n * 布局对齐原「畅跑任务管理」弹层:标题 + 时区 → 新建 / 暂停全部 / 删除全部 → 任务列表。\r\n * 打开由父层(状态徽标面板)控制,所以这里没有「打开/关闭」状态、没有触发按钮、也不点外关闭。\r\n */\r\nexport function FreeRunPanel({ sessionId }: FreeRunPanelProps) {\r\n const [view, setView] = useState<FreeRunView>(EMPTY_FREE_RUN)\r\n const [busy, setBusy] = useState(false)\r\n const [error, setError] = useState('')\r\n const [creating, setCreating] = useState(false)\r\n const [fromDate, setFromDate] = useState('')\r\n const [fromHour, setFromHour] = useState('')\r\n const [toDate, setToDate] = useState('')\r\n const [toHour, setToHour] = useState('')\r\n\r\n /** 用「当前时刻」重新播种起止(开始 = 本小时整点,结束 = +2h)。 */\r\n const seedFromNow = useCallback((): void => {\r\n const s = seedRange(new Date())\r\n setFromDate(s.fromDate)\r\n setFromHour(s.fromHour)\r\n setToDate(s.toDate)\r\n setToHour(s.toHour)\r\n }, [])\r\n\r\n injectClientCss()\r\n\r\n const poll = useCallback(async (): Promise<void> => {\r\n if (sessionId === undefined || sessionId === '') return\r\n try {\r\n const res = await fetch(`/session-guard/state?session=${encodeURIComponent(sessionId)}`)\r\n const body = await res.json().catch(() => null)\r\n setView(readFreeRun(body))\r\n } catch {\r\n // fail-open:路由不存在 / 网络失败 → 保持上一次状态。\r\n }\r\n }, [sessionId])\r\n\r\n useEffect(() => {\r\n if (sessionId === undefined || sessionId === '') return\r\n let cancelled = false\r\n void poll()\r\n const timer = setInterval(() => {\r\n if (!cancelled) void poll()\r\n }, POLL_MS)\r\n return () => {\r\n cancelled = true\r\n clearInterval(timer)\r\n }\r\n }, [sessionId, poll])\r\n\r\n // 到点后要立刻刷新(按钮高亮 / 列表状态):按剩余毫秒设一个本地定时器。\r\n useEffect(() => {\r\n const ms = view.msRemaining\r\n if (ms === null || !Number.isFinite(ms) || ms <= 0) return\r\n const timer = setTimeout(() => {\r\n void poll()\r\n }, ms + 500)\r\n return () => {\r\n clearTimeout(timer)\r\n }\r\n }, [view.msRemaining, poll])\r\n\r\n // 内嵌形态没有「打开面板」这一刻:挂载即按当前时刻播种新建表单。\r\n useEffect(() => {\r\n seedFromNow()\r\n }, [seedFromNow])\r\n\r\n const rpc = useCallback(\r\n async (action: string, payload: Record<string, unknown> = {}): Promise<boolean> => {\r\n if (sessionId === undefined || sessionId === '') return false\r\n setBusy(true)\r\n setError('')\r\n try {\r\n const res = await fetch('/session-guard/rpc', {\r\n method: 'POST',\r\n headers: { 'content-type': 'application/json' },\r\n body: JSON.stringify({ sessionId, action, ...payload }),\r\n })\r\n const body = await res.json().catch(() => null)\r\n if (body?.ok === true) {\r\n setView(readFreeRun({ ok: true, freeRun: body.result }))\r\n return true\r\n }\r\n setError(typeof body?.error === 'string' ? body.error : '操作失败')\r\n return false\r\n } catch {\r\n setError('操作失败(网络或路由不可用)')\r\n return false\r\n } finally {\r\n setBusy(false)\r\n }\r\n },\r\n [sessionId],\r\n )\r\n\r\n if (sessionId === undefined || sessionId === '') return null\r\n // 插件整体关掉且没有任何任务时不给按钮;已有任务则保留,便于清理。\r\n if (!view.available && view.windows.length === 0) return null\r\n\r\n const onAdd = async (): Promise<void> => {\r\n if (fromDate === '' || fromHour === '' || toDate === '' || toHour === '') {\r\n setError('请选择开始与结束时间')\r\n return\r\n }\r\n const ok = await rpc('freeRunAdd', {\r\n from: combineStart(fromDate, fromHour),\r\n to: combineEnd(toDate, toHour),\r\n })\r\n if (ok) {\r\n setError('')\r\n setCreating(false)\r\n }\r\n }\r\n\r\n const nothingToAct = toolbarDisabled(view)\r\n const allPaused = allTasksPaused(view)\r\n\r\n return (\r\n <div className=\"sg-fr-section\" role=\"group\" aria-label=\"畅跑任务\">\r\n <div className=\"sg-fr-head\">\r\n <span className=\"sg-fr-title\">畅跑任务</span>\r\n <span className=\"sg-fr-tz\">按 {view.timezone === '' ? '本地时区' : view.timezone}</span>\r\n </div>\r\n\r\n {/* 顶部三个文本按钮:新建 / 暂停全部 / 删除全部 */}\r\n <div className=\"sg-fr-bar\">\r\n <button\r\n type=\"button\"\r\n className={`sg-fr-bar-btn ${creating ? 'sg-fr-bar-on' : ''}`}\r\n disabled={busy}\r\n aria-pressed={creating}\r\n onClick={() => {\r\n setCreating((prev) => !prev)\r\n setError('')\r\n }}\r\n >\r\n 新建畅跑任务\r\n </button>\r\n <button\r\n type=\"button\"\r\n className=\"sg-fr-bar-btn\"\r\n disabled={busy || nothingToAct}\r\n onClick={() => {\r\n void rpc(allPaused ? 'freeRunResumeAll' : 'freeRunPauseAll')\r\n }}\r\n >\r\n {pauseAllLabel(view)}\r\n </button>\r\n <button\r\n type=\"button\"\r\n className=\"sg-fr-bar-btn sg-fr-bar-danger\"\r\n disabled={busy || view.windows.length === 0}\r\n onClick={() => {\r\n void rpc('freeRunClear')\r\n }}\r\n >\r\n 删除全部任务\r\n </button>\r\n </div>\r\n\r\n {creating && (\r\n <div className=\"sg-fr-form\">\r\n {/* 日期范围:双月日历(对齐 DSH 排期面板的样式)。\r\n 小时仍用下拉,保持「到小时」的精度。 */}\r\n <DateRangePicker\r\n from={fromDate}\r\n to={toDate}\r\n disabled={busy}\r\n onChange={(f, t) => {\r\n setFromDate(f)\r\n setToDate(t)\r\n setError('')\r\n }}\r\n />\r\n <div className=\"sg-fr-hours\">\r\n <label className=\"sg-fr-hour\">\r\n <span>开始</span>\r\n <select\r\n value={fromHour}\r\n disabled={busy}\r\n onChange={(e) => {\r\n setFromHour(e.target.value)\r\n }}\r\n >\r\n <option value=\"\">时</option>\r\n {HOURS.map((h) => (\r\n <option key={h} value={h}>\r\n {h}\r\n </option>\r\n ))}\r\n </select>\r\n <span className=\"sg-fr-unit\">时</span>\r\n </label>\r\n <label className=\"sg-fr-hour\">\r\n <span>结束</span>\r\n <select\r\n value={toHour}\r\n disabled={busy}\r\n onChange={(e) => {\r\n setToHour(e.target.value)\r\n }}\r\n >\r\n <option value=\"\">时</option>\r\n {HOURS.map((h) => (\r\n <option key={h} value={h}>\r\n {h}\r\n </option>\r\n ))}\r\n </select>\r\n <span className=\"sg-fr-unit\">时</span>\r\n </label>\r\n <button type=\"button\" className=\"sg-fr-now\" disabled={busy} title=\"重置为当前时刻\" onClick={seedFromNow}>\r\n 现在\r\n </button>\r\n </div>\r\n <div className=\"sg-fr-form-actions\">\r\n <button type=\"button\" className=\"sg-fr-add\" disabled={busy} onClick={() => void onAdd()}>\r\n 确定\r\n </button>\r\n <button\r\n type=\"button\"\r\n className=\"sg-fr-cancel\"\r\n disabled={busy}\r\n onClick={() => {\r\n setCreating(false)\r\n setError('')\r\n }}\r\n >\r\n 取消\r\n </button>\r\n </div>\r\n </div>\r\n )}\r\n\r\n {view.windows.length === 0 ? (\r\n <div className=\"sg-fr-empty\">还没有畅跑任务,点「新建畅跑任务」添加</div>\r\n ) : (\r\n <ul className=\"sg-fr-list\">\r\n {view.windows.map((w) => (\r\n <li key={w.id} className=\"sg-fr-item\">\r\n <span className=\"sg-fr-range\">\r\n {w.fromDisplay} – {w.toDisplay}\r\n </span>\r\n <span className={`sg-fr-tag sg-fr-tag-${w.status}`}>{statusLabel(w.status)}</span>\r\n <button\r\n type=\"button\"\r\n className=\"sg-fr-icon\"\r\n title={rowPauseTitle(w)}\r\n aria-label={w.paused === true ? '恢复' : '暂停'}\r\n disabled={busy || (!canPause(w) && !canResume(w))}\r\n onClick={() => {\r\n void rpc(w.paused === true ? 'freeRunResume' : 'freeRunPause', { id: w.id })\r\n }}\r\n >\r\n {w.paused === true ? ICON_RESUME : ICON_PAUSE}\r\n </button>\r\n <button\r\n type=\"button\"\r\n className=\"sg-fr-icon sg-fr-icon-danger\"\r\n title={rowDeleteTitle()}\r\n aria-label=\"删除\"\r\n disabled={busy}\r\n onClick={() => {\r\n void rpc('freeRunRemove', { id: w.id })\r\n }}\r\n >\r\n {ICON_DELETE}\r\n </button>\r\n </li>\r\n ))}\r\n </ul>\r\n )}\r\n\r\n {error !== '' && <div className=\"sg-fr-error\">{error}</div>}\r\n </div>\r\n )\r\n}\r\n","/**\n * dsh-offpeak — 「高峰拦截列表」的纯逻辑(零 React、零依赖,可单测)。\n *\n * 与 host 判定**同源**:列表项(`officialModelPatterns`)是**模型名**,匹配语义为**精确匹配**\n * (大小写不敏感、去首尾空白;v3.1.6 起不再有关键字/正则的包含匹配)。这里只做四件事:\n * 1. 某模型名是否在列表里(= 是否被拦);\n * 2. 单行勾选 / 取消勾选 → 新的列表;\n * 3. 关键词过滤(只影响展示与「全选」范围);\n * 4. 批量勾选 → 新的列表。\n *\n * **判定与存储逻辑都不在这里**:勾选只是往同一份列表里增删**同名条目**。\n */\n\n/** 一行的判定状态。 */\nexport interface ModelRowState {\n /** 命中的列表项(精确匹配下最多一项)。 */\n matched: string[]\n /** 列表里存在与模型名**完全相同**的条目(= 勾选状态)。 */\n explicit: boolean\n /** 是否已被拦截(精确匹配下等价于 `explicit`)。 */\n covered: boolean\n}\n\n/** 单个列表项是否命中模型名:**精确匹配**(大小写不敏感、去首尾空白)。 */\nexport function matchesPattern(model: string, pattern: string): boolean {\n if (typeof model !== 'string' || typeof pattern !== 'string') return false\n const a = model.trim().toLowerCase()\n const b = pattern.trim().toLowerCase()\n return a !== '' && b !== '' && a === b\n}\n\n/** 算出某模型名在给定列表下的行状态。 */\nexport function rowState(model: string, patterns: readonly string[]): ModelRowState {\n const list = Array.isArray(patterns) ? patterns : []\n const matched = list.filter((p) => matchesPattern(model, p))\n return { matched, explicit: matched.length > 0, covered: matched.length > 0 }\n}\n\n/** 给一行生成提示文案。 */\nexport function rowHint(state: ModelRowState): string {\n if (state.explicit) return '已拦截'\n return ''\n}\n\n/**\n * 勾选/取消勾选**单个**模型 → 新列表。\n *\n * - 已存在**完全同名**条目 → 删掉它(取消勾选;若仍有更宽的关键字覆盖,行会变成\n * 只读并提示是哪个关键字盖着 —— 那种情况需要改关键字,而不是逐个模型取消);\n * - 否则 → 把模型名作为一个新条目追加。\n *\n * 纯函数:不修改入参,返回新数组(去重、保持原有顺序)。\n */\nexport function toggleModel(patterns: readonly string[], model: string): string[] {\n const list = Array.isArray(patterns) ? [...patterns] : []\n if (typeof model !== 'string' || model === '') return list\n const at = list.indexOf(model)\n if (at !== -1) {\n list.splice(at, 1)\n return list\n }\n if (!list.includes(model)) list.push(model)\n return list\n}\n\n/** 模型行(面板与纯函数共用的最小形状)。 */\nexport interface ModelLike {\n provider: string\n model: string\n}\n\n/**\n * 关键词过滤:大小写不敏感的**子串**匹配,命中范围是 `provider/model`(只按 model 也能命中)。\n *\n * 只作用于**展示**与「全选」范围 —— 模型集合、拦截列表本身都不变(空关键词 = 不过滤)。\n */\nexport function filterRows<T extends ModelLike>(rows: readonly T[], keyword: string): T[] {\n const list = Array.isArray(rows) ? rows : []\n const q = typeof keyword === 'string' ? keyword.trim().toLowerCase() : ''\n if (q === '') return [...list]\n return list.filter((r) => `${r.provider}/${r.model}`.toLowerCase().includes(q))\n}\n\n/** 当前过滤结果是否已**全部**被拦截(决定「全选」按钮的文案与动作)。 */\nexport function allCovered(patterns: readonly string[], rows: readonly ModelLike[]): boolean {\n const list = Array.isArray(rows) ? rows : []\n if (list.length === 0) return false\n return list.every((r) => rowState(r.model, patterns).covered)\n}\n\n/**\n * 批量勾选:对**传入的这些行**(= 当前过滤结果)执行全选 / 取消全选。\n *\n * - `checked = true`:把还没被覆盖的模型名作为显式条目补进列表(已有条目不动);\n * - `checked = false`:把与这些模型名**完全同名**的条目删掉(被更宽关键字盖住的行仍会被拦,\n * 与单行勾选的语义一致 —— 那种情况要改关键字)。\n *\n * 纯函数:不修改入参,返回新数组。\n */\nexport function bulkToggle(patterns: readonly string[], rows: readonly ModelLike[], checked: boolean): string[] {\n const before = Array.isArray(patterns) ? patterns : []\n const list = [...before]\n const models = (Array.isArray(rows) ? rows : []).map((r) => r.model)\n if (checked) {\n for (const m of models) {\n if (typeof m !== 'string' || m === '') continue\n // 已被(关键字或显式条目)拦住的模型不重复追加 —— 避免把列表写成一堆冗余条目。\n // 覆盖判定用**进入本函数时**的列表:语义稳定(「给当前未覆盖的过滤结果补显式条目」)。\n if (rowState(m, before).covered) continue\n if (!list.includes(m)) list.push(m)\n }\n return list\n }\n const drop = new Set(models)\n return list.filter((p) => !drop.has(p))\n}\n","/**\n * dsh-offpeak — 「高峰拦截列表」面板(状态徽标展开区)。\n *\n * 形态(v3.1.6):\n * - 列表 = DSH 当前全部模型(`GET /session-guard/models`:各路由自报 + 本插件观察到的真实目标),\n * 每行一个模型、**复选框在行右侧**、按 provider 分组;\n * - 顶部**关键词过滤框** + **全选 / 取消全选**(只作用于当前过滤结果)+ **刷新**(重拉模型列表)\n * + **热重载**(先让 host 重读配置文件,再拉一次);\n * - **已拦截**:勾选即该模型名进列表(`POST /session-guard/rpc {action:'setModelPatterns'}` →\n * 写 `config/offpeak.json` 并立即 reload;0.1.7 客户端没有 settingsScope,这是本线唯一\n * 写入口)。列表项**精确匹配模型名**(大小写不敏感),没有关键字/正则的批量覆盖,\n * 因此每一项都能用它的 `×` **撤销**。\n *\n * 行状态与过滤/批量逻辑都在 `model-list.ts`(纯函数、可单测);host 侧判定逻辑不变。\n */\nimport { useEffect, useState } from 'react'\nimport type { JSX } from 'react'\nimport { allCovered, bulkToggle, filterRows, rowState, toggleModel } from './model-list'\n\n/** `GET /session-guard/models` 的一行。 */\nexport interface CatalogModel {\n provider: string\n model: string\n source?: string\n}\n\nexport function ModelListPanel(): JSX.Element {\n const [loading, setLoading] = useState(true)\n const [models, setModels] = useState<readonly CatalogModel[]>([])\n const [patterns, setPatterns] = useState<readonly string[]>([])\n const [error, setError] = useState<string | null>(null)\n const [writeError, setWriteError] = useState<string | null>(null)\n const [keyword, setKeyword] = useState('')\n const [pending, setPending] = useState(false)\n\n const load = async () => {\n setLoading(true)\n setError(null)\n const [modelsRes, settingsRes] = await Promise.allSettled([\n fetch('/session-guard/models').then((r) => r.json()),\n fetch('/session-guard/settings').then((r) => r.json()),\n ])\n const modelsBody = modelsRes.status === 'fulfilled' ? modelsRes.value : null\n if (modelsBody?.ok && Array.isArray(modelsBody.models)) setModels(modelsBody.models)\n else setError('模型列表不可用(host 需为 v3.1.5+:重启 DSH 后 /session-guard/models 才存在)')\n\n const settingsBody = settingsRes.status === 'fulfilled' ? settingsRes.value : null\n const list = settingsBody?.settings?.officialModelPatterns\n if (Array.isArray(list)) setPatterns(list)\n setLoading(false)\n }\n\n /** 热重载:让 host 重读配置文件,然后重拉列表与模型目录。 */\n const hotReload = async () => {\n try {\n await fetch('/session-guard/rpc', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ action: 'reloadConfig' }),\n })\n } catch {\n // fail-open:重读失败也照常重拉一次\n }\n await load()\n }\n\n useEffect(() => {\n // 只在挂载(= 展开)时拉一次;按钮负责后续更新。\n void load()\n }, [])\n\n /** 写回拦截列表(本线:写配置文件 + reload;失败只提示,不改本地展示)。 */\n const commit = async (next: string[]) => {\n setPending(true)\n setWriteError(null)\n const before = patterns\n setPatterns(next) // 乐观更新,失败时回滚\n try {\n const res = await fetch('/session-guard/rpc', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ action: 'setModelPatterns', patterns: next }),\n })\n const body = await res.json().catch(() => null)\n if (!res.ok || body?.ok !== true) {\n setPatterns(before)\n setWriteError(String(body?.error ?? `写入失败(HTTP ${res.status})`))\n } else if (Array.isArray(body.result?.patterns)) {\n setPatterns(body.result.patterns)\n }\n } catch (e) {\n setPatterns(before)\n setWriteError(`写入失败:${e instanceof Error ? e.message : String(e)}`)\n } finally {\n setPending(false)\n }\n }\n\n const filtered = filterRows(models, keyword)\n const providers = [...new Set(filtered.map((r) => r.provider))].sort()\n const everyCovered = allCovered(patterns, filtered)\n\n return (\n <div className=\"sgMlBody\">\n <div className=\"sgMlFilterRow\">\n <input\n className=\"sgMlInput\"\n type=\"text\"\n value={keyword}\n placeholder=\"过滤模型(provider/model 子串)\"\n onChange={(e) => setKeyword(e.currentTarget.value)}\n />\n <button\n type=\"button\"\n className=\"sgMlBtn\"\n disabled={pending || filtered.length === 0}\n onClick={() => void commit(bulkToggle(patterns, filtered, !everyCovered))}\n title=\"只作用于当前过滤结果\"\n >\n {everyCovered ? '取消全选' : '全选'}\n </button>\n <button type=\"button\" className=\"sgMlBtn\" disabled={loading} onClick={() => void load()} title=\"重新获取模型列表\">\n {loading ? '获取中…' : '刷新'}\n </button>\n <button type=\"button\" className=\"sgMlBtn\" disabled={loading} onClick={() => void hotReload()} title=\"重读配置文件后再拉一次\">\n 热重载\n </button>\n </div>\n {patterns.length > 0 && (\n <div className=\"sgMlPatterns\">\n <span className=\"sgMlSentinel\">已拦截</span>\n {patterns.map((p) => (\n <span key={p} className=\"sgMlChip\">\n {p}\n <button\n type=\"button\"\n className=\"sgMlChipUndo\"\n disabled={pending}\n title=\"撤销这一项\"\n aria-label={`撤销 ${p}`}\n onClick={() => void commit(patterns.filter((x) => x !== p))}\n >\n ×\n </button>\n </span>\n ))}\n </div>\n )}\n {writeError !== null && <p className=\"sgMlError\">✗ {writeError}</p>}\n {error !== null && <p className=\"sgMlHint\">{error}</p>}\n {error === null && !loading && filtered.length === 0 && (\n <p className=\"sgMlHint\">{models.length === 0 ? '没有可枚举的模型。' : '没有匹配的模型。'}</p>\n )}\n {filtered.length > 0 && (\n <ul className=\"sgMlList\">\n {providers.map((provider) => (\n <li key={provider}>\n <div className=\"sgMlProvider\">{provider}</div>\n <ul className=\"sgMlList\">\n {filtered\n .filter((r) => r.provider === provider)\n .map((r) => {\n const state = rowState(r.model, patterns)\n return (\n <li key={`${provider}/${r.model}`} className=\"sgMlItem\">\n <label className=\"sgMlCheck\" title=\"勾选 = 加入高峰拦截列表\">\n <span className=\"sgMlModel\">{r.model}</span>\n <input\n type=\"checkbox\"\n checked={state.covered}\n disabled={pending}\n onChange={() => void commit(toggleModel(patterns, r.model))}\n />\n </label>\n </li>\n )\n })}\n </ul>\n </li>\n ))}\n </ul>\n )}\n </div>\n )\n}\n","/**\n * dsh-offpeak — 状态徽标(**徽标即按钮**,fail-open)。\n *\n * 轮询 host 的 `/session-guard/status`(全局阶段 + 拦截列表规模)与\n * `/session-guard/state`(本会话畅跑任务),显示四态文案(v3.1.6):\n * - 高峰 + 有畅跑任务 → `高峰 · 畅跑中`;\n * - 高峰 + 有拦截任务 → `高峰 · 拦截中`;\n * - 高峰 + 都没任务 → `高峰`;\n * - 非高峰 → `谷时` / `周末`。\n *\n * 点开面板 = **畅跑任务区(最上方)+ 高峰拦截列表**(布局按用户定案);**每个阶段都是同一个\n * 按钮**(类名见 `badgeClassNames`),只有文案与配色变,不因不是高峰就退化成展示元素。\n * 本线(0.1.7)客户端没有 settingsScope,拦截列表的写入口是 `setModelPatterns`(写配置文件)。\n * 不做任何队列/会话动作;冻结按钮由 input-traffic 经桥接管(D6/D8)。\n */\nimport { useEffect, useRef, useState } from 'react'\nimport { badgeClassNames, badgeTitle, peakLabel, type Status } from './badge-text'\nimport { FreeRunPanel } from './free-run-button'\nimport { EMPTY_FREE_RUN, readFreeRun, type FreeRunView } from './free-run-button-text'\nimport { ModelListPanel } from './model-panel'\nimport { injectClientCss } from './styles'\n\n/** slot 运行时注入的会话级 props(sessionId 由 dsh 的 SessionStandardProps 提供)。 */\nexport interface StatusBadgeProps {\n sessionId?: string\n}\n\nconst POLL_MS = 15_000\n\n/** 状态徽标:轮询全局阶段,显示 高峰/谷时/周末(enabled 关闭或请求失败时静默隐藏)。 */\nexport function StatusBadge({ sessionId }: StatusBadgeProps) {\n const [status, setStatus] = useState<Status | null>(null)\n const [freeRun, setFreeRun] = useState<FreeRunView>(EMPTY_FREE_RUN)\n const [open, setOpen] = useState(false)\n const rootRef = useRef<HTMLDivElement | null>(null)\n\n injectClientCss()\n\n useEffect(() => {\n if (!sessionId) return\n let cancelled = false\n const poll = async () => {\n try {\n const res = await fetch('/session-guard/status')\n const body = await res.json().catch(() => null)\n if (!cancelled && body?.ok && body.status) setStatus(body.status)\n } catch {\n // fail-open:路由/网络不可达 → 静默,徽标隐藏。\n }\n try {\n const res = await fetch(`/session-guard/state?session=${encodeURIComponent(sessionId)}`)\n const body = await res.json().catch(() => null)\n if (!cancelled && body !== null) setFreeRun(readFreeRun(body))\n } catch {\n // fail-open:畅跑状态取不到 → 保持上一次。\n }\n }\n void poll()\n const timer = setInterval(poll, POLL_MS)\n return () => {\n cancelled = true\n clearInterval(timer)\n }\n }, [sessionId])\n\n // 点击面板之外的区域 → 自动收回(与再次点击徽标收起的效果一致)。\n useEffect(() => {\n if (!open) return\n const onPointerDown = (ev: MouseEvent) => {\n const root = rootRef.current\n const target = ev.target\n if (root === null || !(target instanceof Node)) return\n if (!root.contains(target)) setOpen(false)\n }\n document.addEventListener('mousedown', onPointerDown)\n return () => document.removeEventListener('mousedown', onPointerDown)\n }, [open])\n\n if (!status || !status.enabled) return null\n\n const tasks = {\n intercepting: (status.modelPatterns ?? 0) > 0,\n freeRun: freeRun.windows.length > 0,\n }\n return (\n <div className=\"sg-status-root\" ref={rootRef}>\n <button\n type=\"button\"\n className={badgeClassNames(status)}\n title={`${badgeTitle(status)} — 点击展开/收起高峰面板`}\n aria-expanded={open}\n data-sg-phase={status.phase}\n data-sg-provider-guard={status.providerGuard === true ? 'on' : 'off'}\n data-sg-step-held={String(status.stepHeld ?? 0)}\n onClick={() => setOpen(!open)}\n >\n {peakLabel(status, tasks)}\n </button>\n {open && (\n <div className=\"sg-status-panel\" role=\"dialog\" aria-label=\"高峰面板\">\n {/* 畅跑任务区在最上方(v3.1.6:独立的「畅跑」按钮已去掉,UI 内嵌到这里) */}\n <FreeRunPanel sessionId={sessionId} />\n <div className=\"sgMlHead\">\n <span className=\"sg-fr-title\">高峰拦截列表(按模型名)</span>\n <button type=\"button\" className=\"sg-fr-closeicon\" onClick={() => setOpen(false)} aria-label=\"关闭\">\n ×\n </button>\n </div>\n <ModelListPanel />\n </div>\n )}\n </div>\n )\n}\n","/**\r\n * dsh-offpeak — 浏览器 half。\r\n *\r\n * 职责(全部 fail-open,D8):\r\n * - 在 composer 输入区右侧注册「畅跑」按钮与**纯展示**状态徽标(高峰/谷时/周末);\r\n * - **不做**冻结/会话动作——冻结按钮由 input-traffic 接管并经 /session-guard/rpc\r\n * 桥接 host 会话门;本插件客户端不注册任何按钮,避免与 input-traffic 冲突。\r\n *\r\n * 兼容性注记(0.1.7 line):**客户端**服务 `settingsScope` 在 dsh 0.1.7 起不再提供\r\n * (它原本只为设置卡片席位服务)。若把它写进静态 `inject`,客户端条目会永远 pending\r\n * (waiting for service: settingsScope)并让应用 **web boot 致命失败** —— 这是实测到的\r\n * 崩溃原因,与宿主 `settings` 服务无关(宿主那个服务仍在,只是已无 `register`/`get`)。\r\n * 上游 compat/0.1.7 线因此整体去掉了设置卡片(`inject = ['slots','locale']`);这里跟随\r\n * 同一条口径:设置表单由宿主 `Config` 的 `.volatile()` 字段自动生成,客户端不再承载设置面板。\r\n *\r\n * 构建:tsdown → lib/client.js(__ModuleLoader__.load 注册,与 input-traffic 同构)。\r\n */\r\nimport { StatusBadge } from './status-badge'\r\n\r\n/** 客户端所需服务:slots(状态徽标)+ locale。 */\r\nexport const inject = ['slots', 'locale']\r\n\r\n/** 轻量 ctx 类型(仅本客户端用到的方法;构建时类型被剥离)。 */\r\ninterface SlotsFace {\r\n inject: (_name: string, _fn: () => unknown) => () => void\r\n register: (_options: Record<string, unknown>, _component: unknown) => () => void\r\n}\r\ninterface ClientCtx {\r\n slots: SlotsFace\r\n}\r\n\r\nexport function apply(ctx: ClientCtx) {\r\n // 「畅跑」按钮已去掉(v3.1.6):畅跑 UI 内嵌在状态徽标面板最上方,不再单独占位。\r\n\r\n // 状态徽标:输入区右侧。徽标即按钮 —— 点开「畅跑任务 + 高峰拦截列表」面板。\r\n ctx.slots.inject('conversation.input.right', () => ctx.slots.register({\r\n name: 'conversation.input.right',\r\n id: 'offpeak-status',\r\n order: 40,\r\n locale: 'offpeak',\r\n }, StatusBadge))\r\n}\r\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-offpeak",
3
- "version": "3.1.6",
3
+ "version": "3.1.7",
4
4
  "publishConfig": {
5
5
  "tag": "dsh-0.1.7"
6
6
  },