create-walle 0.9.21 → 0.9.22
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/README.md +5 -5
- package/package.json +2 -2
- package/template/claude-task-manager/api-prompts.js +13 -0
- package/template/claude-task-manager/api-reviews.js +5 -2
- package/template/claude-task-manager/db.js +348 -15
- package/template/claude-task-manager/docs/app-update-refresh-protocol.md +69 -0
- package/template/claude-task-manager/docs/image-paste-ux.md +3 -0
- package/template/claude-task-manager/docs/ipad-web-preview.md +88 -0
- package/template/claude-task-manager/git-utils.js +146 -17
- package/template/claude-task-manager/lib/auth-rate-limit.js +23 -3
- package/template/claude-task-manager/lib/auth-rules.js +3 -0
- package/template/claude-task-manager/lib/document-review.js +33 -2
- package/template/claude-task-manager/lib/microsoft-dev-tunnel-setup.js +83 -0
- package/template/claude-task-manager/lib/mobile-auth-api.js +14 -0
- package/template/claude-task-manager/lib/restart-guard.js +68 -0
- package/template/claude-task-manager/lib/session-standup.js +36 -13
- package/template/claude-task-manager/lib/session-stream.js +11 -4
- package/template/claude-task-manager/lib/transport-security.js +50 -0
- package/template/claude-task-manager/lib/walle-transcript.js +16 -0
- package/template/claude-task-manager/lib/worktree-active-sync.js +6 -3
- package/template/claude-task-manager/public/css/reviews.css +10 -0
- package/template/claude-task-manager/public/css/setup.css +13 -0
- package/template/claude-task-manager/public/css/walle.css +145 -0
- package/template/claude-task-manager/public/index.html +539 -44
- package/template/claude-task-manager/public/ipad.html +363 -0
- package/template/claude-task-manager/public/js/document-review-links.js +196 -0
- package/template/claude-task-manager/public/js/message-renderer.js +14 -3
- package/template/claude-task-manager/public/js/reviews.js +30 -6
- package/template/claude-task-manager/public/js/setup.js +42 -2
- package/template/claude-task-manager/public/js/stream-view.js +20 -1
- package/template/claude-task-manager/public/js/walle.js +314 -18
- package/template/claude-task-manager/public/m/app.css +789 -11
- package/template/claude-task-manager/public/m/app.js +1070 -67
- package/template/claude-task-manager/public/m/claim.html +9 -2
- package/template/claude-task-manager/public/m/index.html +17 -10
- package/template/claude-task-manager/public/m/sw.js +1 -1
- package/template/claude-task-manager/server.js +365 -95
- package/template/claude-task-manager/session-integrity.js +4 -0
- package/template/docs/designs/2026-05-17-portkey-gateway-provider-ux.md +86 -35
- package/template/package.json +1 -1
- package/template/wall-e/api-walle.js +19 -1
- package/template/wall-e/brain.js +152 -6
- package/template/wall-e/chat.js +85 -0
- package/template/wall-e/coding-orchestrator.js +106 -12
- package/template/wall-e/http/model-admin.js +131 -0
- package/template/wall-e/lib/service-health.js +194 -0
- package/template/wall-e/llm/anthropic.js +7 -0
- package/template/wall-e/llm/client.js +46 -12
- package/template/wall-e/llm/openai.js +17 -2
- package/template/wall-e/llm/portkey-sync.js +201 -0
- package/template/wall-e/server.js +13 -0
- package/template/website/index.html +10 -10
|
@@ -263,21 +263,16 @@ function inferProviderFromAgent(agent) {
|
|
|
263
263
|
|
|
264
264
|
function isWalleOwnedSession(session) {
|
|
265
265
|
if (!session || typeof session !== 'object') return false;
|
|
266
|
+
if (session.walle_owned === true || session.walleOwned === true) return true;
|
|
267
|
+
if (session.native_walle_session === true || session.nativeWalleSession === true) return true;
|
|
266
268
|
const explicitProvider = normalizeProvider(session.provider || session.providerId || session._providerId);
|
|
267
269
|
if (explicitProvider === 'walle') return true;
|
|
268
270
|
const explicitAgent = inferProviderFromAgent(session.agentType || session.agent || session.type);
|
|
269
271
|
if (explicitAgent === 'walle') return true;
|
|
270
272
|
|
|
271
|
-
|
|
273
|
+
// Labels, branches, and worktree names describe the task, not the runtime owner.
|
|
272
274
|
const cmd = String(session.cmd || '').trim().toLowerCase();
|
|
273
|
-
|
|
274
|
-
const worktreePath = String(session.worktree_path || session.worktreePath || session.worktreeStatus?.worktreePath || session.cwd || '').trim().toLowerCase();
|
|
275
|
-
const worktreeName = String(session.worktreeStatus?.worktreeName || worktreePath.split('/').filter(Boolean).pop() || '').trim().toLowerCase();
|
|
276
|
-
|
|
277
|
-
if (cmd === 'walle coding' || cmd.startsWith('walle coding ')) return true;
|
|
278
|
-
if (/^(wall-?e|walle)\s+coding\b/.test(label)) return true;
|
|
279
|
-
if (worktreeName === 'walle-coding' || worktreeName.startsWith('walle-coding-')) return true;
|
|
280
|
-
if (branch.includes('walle-coding')) return true;
|
|
275
|
+
if (cmd === 'walle' || cmd === 'walle coding' || cmd.startsWith('walle coding ')) return true;
|
|
281
276
|
return false;
|
|
282
277
|
}
|
|
283
278
|
|
|
@@ -406,10 +401,17 @@ function standupStatusForSession(session, status, summary, now = Date.now()) {
|
|
|
406
401
|
function worktreeEvidence(worktree) {
|
|
407
402
|
if (!worktree) return '';
|
|
408
403
|
const dirtyFiles = Number(worktree.dirtyFiles || 0);
|
|
409
|
-
const
|
|
404
|
+
const trackedDirtyFiles = worktree.trackedDirtyFiles != null
|
|
405
|
+
? Number(worktree.trackedDirtyFiles || 0)
|
|
406
|
+
: Math.max(0, dirtyFiles - Number(worktree.untrackedFiles || 0));
|
|
407
|
+
const untrackedFiles = Number(worktree.untrackedFiles || 0);
|
|
408
|
+
const localCommits = worktree.isMain
|
|
409
|
+
? Number(worktree.unpushedCommits || worktree.ahead || worktree.unmergedCommits || 0)
|
|
410
|
+
: Number(worktree.unmergedCommits || 0);
|
|
410
411
|
const parts = [];
|
|
411
|
-
if (
|
|
412
|
-
if (
|
|
412
|
+
if (trackedDirtyFiles) parts.push(`${trackedDirtyFiles} dirty`);
|
|
413
|
+
if (untrackedFiles) parts.push(`${untrackedFiles} untracked`);
|
|
414
|
+
if (localCommits) parts.push(worktree.isMain ? `${localCommits} local` : `${localCommits} unmerged`);
|
|
413
415
|
if (!parts.length && worktree.summary) parts.push(worktree.summary);
|
|
414
416
|
return parts.length ? `worktree ${parts.join(', ')}` : '';
|
|
415
417
|
}
|
|
@@ -418,8 +420,12 @@ function hasReviewableWork(session, summaryText, progress) {
|
|
|
418
420
|
const worktree = session?.worktreeStatus || session?.worktree || null;
|
|
419
421
|
if (worktree) {
|
|
420
422
|
if (worktree.needsAttention) return true;
|
|
421
|
-
|
|
423
|
+
const trackedDirtyFiles = worktree.trackedDirtyFiles != null
|
|
424
|
+
? Number(worktree.trackedDirtyFiles || 0)
|
|
425
|
+
: Math.max(0, Number(worktree.dirtyFiles || 0) - Number(worktree.untrackedFiles || 0));
|
|
426
|
+
if (trackedDirtyFiles > 0) return true;
|
|
422
427
|
if (Number(worktree.unmergedCommits || 0) > 0) return true;
|
|
428
|
+
if (Number(worktree.unpushedCommits || 0) > 0) return true;
|
|
423
429
|
}
|
|
424
430
|
const text = `${summaryText || ''} ${progressText(progress)}`.toLowerCase();
|
|
425
431
|
return /\b(done|completed|complete|verified|passed|ready for review|all work done)\b/.test(text);
|
|
@@ -664,6 +670,13 @@ function classifySessionStandup(session, signals = {}, now = Date.now()) {
|
|
|
664
670
|
);
|
|
665
671
|
const provider = inferSessionProvider(session);
|
|
666
672
|
const modelProvider = inferSessionModelProvider(session);
|
|
673
|
+
const nativeWalleSession = session?.native_walle_session === true
|
|
674
|
+
|| session?.nativeWalleSession === true
|
|
675
|
+
|| session?.type === 'walle';
|
|
676
|
+
const walleOwned = session?.walle_owned === true
|
|
677
|
+
|| session?.walleOwned === true
|
|
678
|
+
|| isWalleOwnedSession(session);
|
|
679
|
+
const sessionType = session?.session_type || session?.sessionType || session?.runtime_type || session?.runtimeType || session?.type || '';
|
|
667
680
|
|
|
668
681
|
const card = {
|
|
669
682
|
id: session?.id || session?.sessionId || '',
|
|
@@ -671,6 +684,16 @@ function classifySessionStandup(session, signals = {}, now = Date.now()) {
|
|
|
671
684
|
title,
|
|
672
685
|
agent: session?.agentType || session?.agent || session?.type || 'session',
|
|
673
686
|
provider,
|
|
687
|
+
type: session?.type || sessionType || '',
|
|
688
|
+
session_type: sessionType || '',
|
|
689
|
+
runtime_type: session?.runtime_type || session?.runtimeType || session?.type || '',
|
|
690
|
+
walle_owned: !!walleOwned,
|
|
691
|
+
walleOwned: !!walleOwned,
|
|
692
|
+
native_walle_session: !!nativeWalleSession,
|
|
693
|
+
nativeWalleSession: !!nativeWalleSession,
|
|
694
|
+
agentMode: session?.agentMode || session?.agent_mode || null,
|
|
695
|
+
agentKind: session?.agentKind || session?.agent_kind || (walleOwned && !nativeWalleSession ? 'walle-coding' : null),
|
|
696
|
+
taskType: session?.taskType || session?.task_type || (walleOwned && !nativeWalleSession ? 'coding' : null),
|
|
674
697
|
modelProvider,
|
|
675
698
|
model: inferSessionModel(session),
|
|
676
699
|
cwd: session?.cwd || '',
|
|
@@ -136,10 +136,17 @@ class JsonlTailer {
|
|
|
136
136
|
function extractText(content) {
|
|
137
137
|
if (typeof content === 'string') return content;
|
|
138
138
|
if (!Array.isArray(content)) return '';
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
139
|
+
const parts = [];
|
|
140
|
+
let imageCount = 0;
|
|
141
|
+
for (const block of content) {
|
|
142
|
+
if (!block || typeof block !== 'object') continue;
|
|
143
|
+
if ((block.type === 'text' || block.type === 'input_text' || block.type === 'output_text') && block.text) {
|
|
144
|
+
parts.push(block.text);
|
|
145
|
+
} else if (block.type === 'image' || block.type === 'input_image' || block.type === 'image_ref') {
|
|
146
|
+
parts.push(`[Image #${++imageCount}]`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return parts.join('\n');
|
|
143
150
|
}
|
|
144
151
|
|
|
145
152
|
function stripBase64Blobs(contentBlocks) {
|
|
@@ -94,11 +94,59 @@ function trustedForwardedHost(req) {
|
|
|
94
94
|
);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
function cleanIpHeaderValue(value) {
|
|
98
|
+
let raw = stripHeaderQuotes(firstHeaderValue(value));
|
|
99
|
+
if (!raw || raw.toLowerCase() === 'unknown') return '';
|
|
100
|
+
if (raw.startsWith('[')) {
|
|
101
|
+
const end = raw.indexOf(']');
|
|
102
|
+
raw = end >= 0 ? raw.slice(1, end) : stripIpv6Brackets(raw);
|
|
103
|
+
} else if (net.isIP(raw) === 0) {
|
|
104
|
+
const colonCount = (raw.match(/:/g) || []).length;
|
|
105
|
+
if (colonCount === 1 && /^\d+\.\d+\.\d+\.\d+:\d+$/.test(raw)) {
|
|
106
|
+
raw = raw.replace(/:\d+$/, '');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
raw = stripIpv6Brackets(raw);
|
|
110
|
+
return net.isIP(raw) ? raw : '';
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function forwardedClientIp(req) {
|
|
114
|
+
const forwarded = parseForwardedHeader(req?.headers?.forwarded || '');
|
|
115
|
+
for (const value of [
|
|
116
|
+
req?.headers?.['x-forwarded-for'],
|
|
117
|
+
req?.headers?.['x-real-ip'],
|
|
118
|
+
req?.headers?.['cf-connecting-ip'],
|
|
119
|
+
forwarded.for,
|
|
120
|
+
]) {
|
|
121
|
+
const ip = cleanIpHeaderValue(value);
|
|
122
|
+
if (ip) return ip;
|
|
123
|
+
}
|
|
124
|
+
return '';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function requestClientIp(req) {
|
|
128
|
+
const direct = stripIpv6Brackets(req?.socket?.remoteAddress || '');
|
|
129
|
+
if (!isLoopbackAddress(direct)) return direct;
|
|
130
|
+
const tunnelHost = trustedForwardedHost(req) || cleanHostHeaderValue(req?.headers?.host);
|
|
131
|
+
if (!isManagedHttpsTunnelHost(tunnelHost)) return direct;
|
|
132
|
+
const forwardedIp = forwardedClientIp(req);
|
|
133
|
+
if (forwardedIp && !isLoopbackAddress(forwardedIp)) return forwardedIp;
|
|
134
|
+
const tunnelName = hostHeaderName(tunnelHost);
|
|
135
|
+
return tunnelName ? `tunnel:${tunnelName}` : direct;
|
|
136
|
+
}
|
|
137
|
+
|
|
97
138
|
function isLoopbackAddress(address) {
|
|
98
139
|
const h = stripIpv6Brackets(address).toLowerCase();
|
|
99
140
|
return LOOPBACK_HOSTS.has(h) || h === '::ffff:127.0.0.1';
|
|
100
141
|
}
|
|
101
142
|
|
|
143
|
+
function primaryProcessIpLockoutExemptions(primaryHost) {
|
|
144
|
+
const out = new Set(['127.0.0.1', '::1']);
|
|
145
|
+
const host = stripIpv6Brackets(primaryHost).trim();
|
|
146
|
+
if (host && !isLoopbackHost(host)) out.add(host);
|
|
147
|
+
return Array.from(out);
|
|
148
|
+
}
|
|
149
|
+
|
|
102
150
|
function hasNonLoopbackBrowserOrigin(req) {
|
|
103
151
|
for (const value of [req?.headers?.origin, req?.headers?.referer, req?.headers?.referrer]) {
|
|
104
152
|
const raw = firstHeaderValue(value);
|
|
@@ -296,6 +344,8 @@ module.exports = {
|
|
|
296
344
|
normalizeBindHost,
|
|
297
345
|
originAllowed,
|
|
298
346
|
parseAllowedOrigins,
|
|
347
|
+
primaryProcessIpLockoutExemptions,
|
|
348
|
+
requestClientIp,
|
|
299
349
|
requestBrowserOrigin,
|
|
300
350
|
requestOrigin,
|
|
301
351
|
requestProtocol,
|
|
@@ -165,6 +165,21 @@ function readLastUuid(filePath) {
|
|
|
165
165
|
return '';
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
function readSessionMeta(filePath) {
|
|
169
|
+
let raw;
|
|
170
|
+
try { raw = fs.readFileSync(filePath, 'utf8'); } catch { return null; }
|
|
171
|
+
const lines = raw.split('\n').filter(Boolean);
|
|
172
|
+
for (const line of lines) {
|
|
173
|
+
try {
|
|
174
|
+
const row = JSON.parse(line);
|
|
175
|
+
if (row && row.type === 'session_meta') return row;
|
|
176
|
+
} catch {
|
|
177
|
+
// Ignore malformed tail rows; the transcript writer is append-only.
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
|
|
168
183
|
module.exports = {
|
|
169
184
|
VERSION,
|
|
170
185
|
WALLE_PROJECT_ENTRY,
|
|
@@ -177,4 +192,5 @@ module.exports = {
|
|
|
177
192
|
appendPart,
|
|
178
193
|
extractText,
|
|
179
194
|
readLastUuid,
|
|
195
|
+
readSessionMeta,
|
|
180
196
|
};
|
|
@@ -59,8 +59,11 @@ function activeSyncWorktreePolicy(wt, quiescence, opts = {}) {
|
|
|
59
59
|
if (!wt.branch || wt.branch === 'HEAD' || wt.state === 'detached') {
|
|
60
60
|
return { ok: false, status: quiescence.status, reason: 'Recover this worktree onto a branch before syncing from main.' };
|
|
61
61
|
}
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
const trackedDirty = wt.trackedDirtyFiles != null
|
|
63
|
+
? Number(wt.trackedDirtyFiles || 0)
|
|
64
|
+
: Math.max(0, Number(wt.dirtyFiles || 0) - Number(wt.untrackedFiles || 0));
|
|
65
|
+
if (trackedDirty > 0) {
|
|
66
|
+
return { ok: false, status: quiescence.status, reason: 'Commit or stash tracked dirty files before syncing from main.' };
|
|
64
67
|
}
|
|
65
68
|
if ((wt.ahead || 0) > 0 || wt.state === 'diverged') {
|
|
66
69
|
return {
|
|
@@ -75,7 +78,7 @@ function activeSyncWorktreePolicy(wt, quiescence, opts = {}) {
|
|
|
75
78
|
return {
|
|
76
79
|
ok: true,
|
|
77
80
|
status: quiescence.status,
|
|
78
|
-
reason: 'Active session is idle and
|
|
81
|
+
reason: 'Active session is idle and tracked files are clean; sync can proceed with confirmation.',
|
|
79
82
|
};
|
|
80
83
|
}
|
|
81
84
|
|
|
@@ -54,6 +54,16 @@
|
|
|
54
54
|
.cr-btn.success { background: var(--green, #9ece6a); color: #1a1b26; border-color: var(--green); }
|
|
55
55
|
.cr-btn.danger { background: var(--red, #f7768e); color: #fff; border-color: var(--red); }
|
|
56
56
|
.cr-btn:disabled { opacity: 0.4; cursor: default; }
|
|
57
|
+
.ctm-doc-review-link {
|
|
58
|
+
color: var(--accent, #7aa2f7);
|
|
59
|
+
text-decoration: underline;
|
|
60
|
+
text-decoration-style: dotted;
|
|
61
|
+
text-underline-offset: 2px;
|
|
62
|
+
cursor: pointer;
|
|
63
|
+
}
|
|
64
|
+
.ctm-doc-review-link:hover {
|
|
65
|
+
color: var(--fg, #c0caf5);
|
|
66
|
+
}
|
|
57
67
|
|
|
58
68
|
/* Commit summary card (shown at top of diff area when a commit is selected) */
|
|
59
69
|
.cr-file-item.summary {
|
|
@@ -553,12 +553,25 @@
|
|
|
553
553
|
font-size: 11px;
|
|
554
554
|
line-height: 1.35;
|
|
555
555
|
}
|
|
556
|
+
#setup-panel .setup-tunnel-actions {
|
|
557
|
+
display: flex;
|
|
558
|
+
flex-direction: column;
|
|
559
|
+
gap: 8px;
|
|
560
|
+
align-items: stretch;
|
|
561
|
+
}
|
|
556
562
|
#setup-panel .setup-tunnel-primary {
|
|
557
563
|
min-width: 116px;
|
|
558
564
|
min-height: 38px;
|
|
559
565
|
padding: 8px 14px;
|
|
560
566
|
white-space: nowrap;
|
|
561
567
|
}
|
|
568
|
+
#setup-panel .setup-tunnel-secondary {
|
|
569
|
+
min-width: 116px;
|
|
570
|
+
min-height: 32px;
|
|
571
|
+
padding: 6px 10px;
|
|
572
|
+
font-size: 12px;
|
|
573
|
+
white-space: nowrap;
|
|
574
|
+
}
|
|
562
575
|
#setup-panel .setup-tunnel-steps {
|
|
563
576
|
display: grid;
|
|
564
577
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
@@ -410,11 +410,156 @@
|
|
|
410
410
|
border: 1px solid rgba(255,255,255,0.10);
|
|
411
411
|
background: rgba(255,255,255,0.04);
|
|
412
412
|
}
|
|
413
|
+
.we-health-card {
|
|
414
|
+
padding: 0;
|
|
415
|
+
overflow: hidden;
|
|
416
|
+
border-color: rgba(116,192,252,0.22);
|
|
417
|
+
background: rgba(28,36,58,0.72);
|
|
418
|
+
}
|
|
419
|
+
.we-health-card.error {
|
|
420
|
+
border-color: rgba(247,118,142,0.40);
|
|
421
|
+
background: rgba(247,118,142,0.075);
|
|
422
|
+
}
|
|
423
|
+
.we-health-card.warning {
|
|
424
|
+
border-color: rgba(250,176,5,0.36);
|
|
425
|
+
background: rgba(250,176,5,0.07);
|
|
426
|
+
}
|
|
427
|
+
.we-health-card.info {
|
|
428
|
+
border-color: rgba(116,192,252,0.28);
|
|
429
|
+
}
|
|
430
|
+
.we-health-header {
|
|
431
|
+
display: grid;
|
|
432
|
+
grid-template-columns: 10px minmax(0, 1fr) auto;
|
|
433
|
+
align-items: start;
|
|
434
|
+
gap: 10px;
|
|
435
|
+
padding: 12px;
|
|
436
|
+
}
|
|
437
|
+
.we-health-status-dot {
|
|
438
|
+
width: 8px;
|
|
439
|
+
height: 8px;
|
|
440
|
+
margin-top: 5px;
|
|
441
|
+
border-radius: 999px;
|
|
442
|
+
background: #9ece6a;
|
|
443
|
+
box-shadow: 0 0 0 4px rgba(158,206,106,0.12);
|
|
444
|
+
}
|
|
445
|
+
.we-health-card.error .we-health-status-dot {
|
|
446
|
+
background: #f7768e;
|
|
447
|
+
box-shadow: 0 0 0 4px rgba(247,118,142,0.14);
|
|
448
|
+
}
|
|
449
|
+
.we-health-card.warning .we-health-status-dot {
|
|
450
|
+
background: #fab005;
|
|
451
|
+
box-shadow: 0 0 0 4px rgba(250,176,5,0.14);
|
|
452
|
+
}
|
|
453
|
+
.we-health-card.info .we-health-status-dot {
|
|
454
|
+
background: #74c0fc;
|
|
455
|
+
box-shadow: 0 0 0 4px rgba(116,192,252,0.13);
|
|
456
|
+
}
|
|
413
457
|
.we-service-alerts-title {
|
|
414
458
|
font-weight: 700;
|
|
415
459
|
margin-bottom: 4px;
|
|
416
460
|
color: #d7dde8;
|
|
417
461
|
}
|
|
462
|
+
.we-health-title-block {
|
|
463
|
+
min-width: 0;
|
|
464
|
+
}
|
|
465
|
+
.we-health-title {
|
|
466
|
+
color: var(--fg, #d7dde8);
|
|
467
|
+
font-size: 13px;
|
|
468
|
+
font-weight: 800;
|
|
469
|
+
line-height: 1.25;
|
|
470
|
+
}
|
|
471
|
+
.we-health-message {
|
|
472
|
+
margin-top: 3px;
|
|
473
|
+
color: var(--fg-muted, #9aa4b2);
|
|
474
|
+
line-height: 1.45;
|
|
475
|
+
}
|
|
476
|
+
.we-health-meta {
|
|
477
|
+
max-width: 280px;
|
|
478
|
+
color: var(--fg-muted, #9aa4b2);
|
|
479
|
+
font-size: 11px;
|
|
480
|
+
font-weight: 700;
|
|
481
|
+
line-height: 1.35;
|
|
482
|
+
text-align: right;
|
|
483
|
+
}
|
|
484
|
+
.we-health-current {
|
|
485
|
+
display: flex;
|
|
486
|
+
justify-content: space-between;
|
|
487
|
+
gap: 12px;
|
|
488
|
+
margin: 0 12px 12px;
|
|
489
|
+
padding: 11px 12px;
|
|
490
|
+
border: 1px solid rgba(247,118,142,0.30);
|
|
491
|
+
border-radius: 6px;
|
|
492
|
+
background: rgba(10,14,26,0.28);
|
|
493
|
+
}
|
|
494
|
+
.we-health-current-copy {
|
|
495
|
+
min-width: 0;
|
|
496
|
+
}
|
|
497
|
+
.we-health-current-title {
|
|
498
|
+
color: #fff;
|
|
499
|
+
font-weight: 800;
|
|
500
|
+
line-height: 1.3;
|
|
501
|
+
}
|
|
502
|
+
.we-health-current-body {
|
|
503
|
+
margin-top: 4px;
|
|
504
|
+
color: #f2c0c8;
|
|
505
|
+
line-height: 1.45;
|
|
506
|
+
overflow-wrap: anywhere;
|
|
507
|
+
}
|
|
508
|
+
.we-health-current-meta {
|
|
509
|
+
margin-top: 6px;
|
|
510
|
+
color: #9fb0c8;
|
|
511
|
+
font-size: 11px;
|
|
512
|
+
font-weight: 700;
|
|
513
|
+
}
|
|
514
|
+
.we-health-current-actions {
|
|
515
|
+
display: flex;
|
|
516
|
+
align-items: flex-start;
|
|
517
|
+
gap: 6px;
|
|
518
|
+
flex-shrink: 0;
|
|
519
|
+
}
|
|
520
|
+
.we-health-group {
|
|
521
|
+
border-top: 1px solid rgba(255,255,255,0.08);
|
|
522
|
+
}
|
|
523
|
+
.we-health-group summary {
|
|
524
|
+
display: flex;
|
|
525
|
+
align-items: center;
|
|
526
|
+
justify-content: space-between;
|
|
527
|
+
gap: 12px;
|
|
528
|
+
padding: 9px 12px;
|
|
529
|
+
color: #cfd7e6;
|
|
530
|
+
cursor: pointer;
|
|
531
|
+
font-weight: 800;
|
|
532
|
+
list-style: none;
|
|
533
|
+
}
|
|
534
|
+
.we-health-group summary::-webkit-details-marker {
|
|
535
|
+
display: none;
|
|
536
|
+
}
|
|
537
|
+
.we-health-group summary::before {
|
|
538
|
+
content: '›';
|
|
539
|
+
color: var(--fg-muted, #9aa4b2);
|
|
540
|
+
transform: rotate(0deg);
|
|
541
|
+
transition: transform 0.14s ease;
|
|
542
|
+
}
|
|
543
|
+
.we-health-group[open] summary::before {
|
|
544
|
+
transform: rotate(90deg);
|
|
545
|
+
}
|
|
546
|
+
.we-health-group summary span {
|
|
547
|
+
flex: 1;
|
|
548
|
+
min-width: 0;
|
|
549
|
+
}
|
|
550
|
+
.we-health-group summary strong {
|
|
551
|
+
display: inline-grid;
|
|
552
|
+
min-width: 22px;
|
|
553
|
+
height: 22px;
|
|
554
|
+
place-items: center;
|
|
555
|
+
border: 1px solid rgba(255,255,255,0.10);
|
|
556
|
+
border-radius: 999px;
|
|
557
|
+
color: #d7dde8;
|
|
558
|
+
font-size: 11px;
|
|
559
|
+
}
|
|
560
|
+
.we-health-group-body {
|
|
561
|
+
padding: 0 12px 9px;
|
|
562
|
+
}
|
|
418
563
|
.we-service-alert-item {
|
|
419
564
|
display: flex;
|
|
420
565
|
align-items: center;
|