netizen-cli 0.10.0__py3-none-any.whl
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.
- netizen_cli/__init__.py +3 -0
- netizen_cli/__main__.py +4 -0
- netizen_cli/admin/__init__.py +1 -0
- netizen_cli/admin/auth.py +928 -0
- netizen_cli/admin/errors.py +9 -0
- netizen_cli/admin/port_config.py +115 -0
- netizen_cli/admin/presentation.py +257 -0
- netizen_cli/admin/queries.py +337 -0
- netizen_cli/admin/static/admin.css +260 -0
- netizen_cli/admin/static/admin.js +2898 -0
- netizen_cli/admin/static/index.html +327 -0
- netizen_cli/admin/transport.py +935 -0
- netizen_cli/admin/web.py +2717 -0
- netizen_cli/bindings.py +3215 -0
- netizen_cli/builtin_skills.py +93 -0
- netizen_cli/cards/__init__.py +105 -0
- netizen_cli/cards/callbacks.py +565 -0
- netizen_cli/cards/controls.py +2273 -0
- netizen_cli/cards/defaults.py +213 -0
- netizen_cli/cards/model_info.py +80 -0
- netizen_cli/cards/questions.py +220 -0
- netizen_cli/cards/reply.py +2247 -0
- netizen_cli/cards/scheduled.py +836 -0
- netizen_cli/channel/__init__.py +1 -0
- netizen_cli/channel/completion_mentions.py +60 -0
- netizen_cli/channel/input_preparation.py +644 -0
- netizen_cli/channel/messages.py +57 -0
- netizen_cli/channel/ports.py +52 -0
- netizen_cli/channel/question_inputs.py +51 -0
- netizen_cli/channel/reactions.py +293 -0
- netizen_cli/channel/reply_presenter.py +1505 -0
- netizen_cli/channel/topics.py +70 -0
- netizen_cli/channel_app.py +6593 -0
- netizen_cli/cli.py +287 -0
- netizen_cli/cli_data.py +536 -0
- netizen_cli/cli_packages.py +526 -0
- netizen_cli/cli_services.py +651 -0
- netizen_cli/cli_setup.py +242 -0
- netizen_cli/cli_update.py +303 -0
- netizen_cli/cli_update_restore.py +53 -0
- netizen_cli/cli_update_worker.py +333 -0
- netizen_cli/codex_runtime.py +7125 -0
- netizen_cli/completion_mention.py +16 -0
- netizen_cli/database_migrations.py +218 -0
- netizen_cli/defaults/__init__.py +5 -0
- netizen_cli/defaults/models.py +39 -0
- netizen_cli/defaults/service.py +232 -0
- netizen_cli/defaults/store.py +260 -0
- netizen_cli/deployment/__init__.py +1 -0
- netizen_cli/deployment/restart_worker.py +134 -0
- netizen_cli/deployment/update_executor.py +258 -0
- netizen_cli/deployment/update_protocol.py +281 -0
- netizen_cli/domain.py +416 -0
- netizen_cli/error_messages.py +124 -0
- netizen_cli/experience.py +531 -0
- netizen_cli/feishu_app_onboarding.py +187 -0
- netizen_cli/feishu_app_permissions.py +123 -0
- netizen_cli/git_status.py +63 -0
- netizen_cli/image_inputs.py +579 -0
- netizen_cli/instance.py +84 -0
- netizen_cli/lark_app.py +125 -0
- netizen_cli/main.py +903 -0
- netizen_cli/management/__init__.py +83 -0
- netizen_cli/management/blocking_io.py +352 -0
- netizen_cli/management/chat_labels.py +266 -0
- netizen_cli/management/coordination.py +32 -0
- netizen_cli/management/service.py +2187 -0
- netizen_cli/management/updates.py +214 -0
- netizen_cli/markdown_images.py +78 -0
- netizen_cli/message_content.py +786 -0
- netizen_cli/message_history.py +643 -0
- netizen_cli/message_preparation.py +60 -0
- netizen_cli/message_projection.py +923 -0
- netizen_cli/migrations/__init__.py +1 -0
- netizen_cli/migrations/schema.py +103 -0
- netizen_cli/migrations/v14.py +438 -0
- netizen_cli/model_settings.py +269 -0
- netizen_cli/package_resources.py +22 -0
- netizen_cli/projects.py +327 -0
- netizen_cli/prompt_projection.py +327 -0
- netizen_cli/quoted_context.py +312 -0
- netizen_cli/resources/config.example.yaml +35 -0
- netizen_cli/resources/skills/netizen-lark/SKILL.md +64 -0
- netizen_cli/resources/skills/netizen-user-guide/SKILL.md +37 -0
- netizen_cli/resources/skills/netizen-user-guide/references/user-guide.md +842 -0
- netizen_cli/result_images.py +123 -0
- netizen_cli/runtime/__init__.py +1 -0
- netizen_cli/runtime/contracts.py +792 -0
- netizen_cli/runtime/name_writes.py +67 -0
- netizen_cli/runtime/thread_naming.py +451 -0
- netizen_cli/schedules/__init__.py +1 -0
- netizen_cli/schedules/mcp.py +535 -0
- netizen_cli/schedules/models.py +394 -0
- netizen_cli/schedules/scheduler.py +374 -0
- netizen_cli/schedules/service.py +766 -0
- netizen_cli/schedules/store.py +771 -0
- netizen_cli/sdk_gap_adapter.py +1151 -0
- netizen_cli/service_launcher.py +583 -0
- netizen_cli/session_settings.py +126 -0
- netizen_cli/settings.py +216 -0
- netizen_cli/skill_references.py +40 -0
- netizen_cli/terminal_cleanup.py +155 -0
- netizen_cli/turn_activity.py +688 -0
- netizen_cli/turn_files.py +812 -0
- netizen_cli/turn_patch_children.py +254 -0
- netizen_cli/turn_plan_observer.py +315 -0
- netizen_cli/user_questions.py +106 -0
- netizen_cli-0.10.0.dist-info/METADATA +18 -0
- netizen_cli-0.10.0.dist-info/RECORD +112 -0
- netizen_cli-0.10.0.dist-info/WHEEL +5 -0
- netizen_cli-0.10.0.dist-info/entry_points.txt +2 -0
- netizen_cli-0.10.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
"""Admin query values and filter-bound pagination cursors."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import base64
|
|
6
|
+
import hashlib
|
|
7
|
+
import json
|
|
8
|
+
import re
|
|
9
|
+
from collections.abc import Mapping, Sequence
|
|
10
|
+
from datetime import UTC, datetime
|
|
11
|
+
|
|
12
|
+
from .errors import AdminWebError
|
|
13
|
+
from ..bindings import BindingCursor, SideTopicCursor
|
|
14
|
+
from ..domain import ScopeKind
|
|
15
|
+
from ..management import SessionInventoryState
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
_MAX_TEXT_BYTES = 4_096
|
|
19
|
+
_SESSION_PAGE_SIZES = frozenset((10, 20, 50, 100))
|
|
20
|
+
_ISO_INSTANT_PATTERN = re.compile(
|
|
21
|
+
r"\A[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}"
|
|
22
|
+
r"(?::[0-9]{2}(?:\.[0-9]{1,6})?)?(?:Z|[+-][0-9]{2}:[0-9]{2})\Z"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _optional_one(values: Mapping[str, list[str]], name: str) -> str | None:
|
|
27
|
+
items = values.get(name)
|
|
28
|
+
if items is None:
|
|
29
|
+
return None
|
|
30
|
+
if len(items) != 1 or not items[0]:
|
|
31
|
+
raise AdminWebError(400, "invalid_query", f"查询参数 {name} 无效。")
|
|
32
|
+
return items[0]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _require_query_keys(
|
|
36
|
+
values: Mapping[str, list[str]],
|
|
37
|
+
allowed: set[str],
|
|
38
|
+
) -> None:
|
|
39
|
+
unknown = values.keys() - allowed
|
|
40
|
+
if unknown:
|
|
41
|
+
raise AdminWebError(400, "invalid_query", "包含未知查询参数。")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _page_size(values: Mapping[str, list[str]]) -> int:
|
|
45
|
+
raw = _optional_one(values, "pageSize")
|
|
46
|
+
if raw is None:
|
|
47
|
+
return 25
|
|
48
|
+
try:
|
|
49
|
+
value = int(raw, 10)
|
|
50
|
+
except ValueError:
|
|
51
|
+
raise AdminWebError(400, "invalid_page_size", "分页大小无效。") from None
|
|
52
|
+
if not 1 <= value <= 50:
|
|
53
|
+
raise AdminWebError(400, "invalid_page_size", "分页大小必须为 1 到 50。")
|
|
54
|
+
return value
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _session_page_size(values: Mapping[str, list[str]]) -> int:
|
|
58
|
+
raw = _optional_one(values, "pageSize")
|
|
59
|
+
if raw is None:
|
|
60
|
+
return 20
|
|
61
|
+
try:
|
|
62
|
+
value = int(raw, 10)
|
|
63
|
+
except ValueError:
|
|
64
|
+
raise AdminWebError(400, "invalid_page_size", "分页大小无效。") from None
|
|
65
|
+
if value not in _SESSION_PAGE_SIZES:
|
|
66
|
+
raise AdminWebError(
|
|
67
|
+
400,
|
|
68
|
+
"invalid_page_size",
|
|
69
|
+
"Sessions 分页大小必须为 10、20、50 或 100。",
|
|
70
|
+
)
|
|
71
|
+
return value
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _optional_text_query(
|
|
75
|
+
values: Mapping[str, list[str]],
|
|
76
|
+
name: str,
|
|
77
|
+
*,
|
|
78
|
+
maximum: int = _MAX_TEXT_BYTES,
|
|
79
|
+
) -> str | None:
|
|
80
|
+
value = _optional_one(values, name)
|
|
81
|
+
if value is None:
|
|
82
|
+
return None
|
|
83
|
+
if len(value.encode("utf-8")) > maximum or value.strip() != value:
|
|
84
|
+
raise AdminWebError(400, "invalid_query", f"查询参数 {name} 无效。")
|
|
85
|
+
return value
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _text_set_query(
|
|
89
|
+
values: Mapping[str, list[str]],
|
|
90
|
+
name: str,
|
|
91
|
+
) -> tuple[str, ...] | None:
|
|
92
|
+
items = values.get(name)
|
|
93
|
+
if items is None:
|
|
94
|
+
return None
|
|
95
|
+
if not items or any(
|
|
96
|
+
not item
|
|
97
|
+
or item.strip() != item
|
|
98
|
+
or len(item.encode("utf-8")) > _MAX_TEXT_BYTES
|
|
99
|
+
for item in items
|
|
100
|
+
):
|
|
101
|
+
raise AdminWebError(400, "invalid_query", f"查询参数 {name} 无效。")
|
|
102
|
+
return tuple(sorted(set(items)))
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _created_range_query(
|
|
106
|
+
values: Mapping[str, list[str]],
|
|
107
|
+
) -> tuple[str | None, str | None]:
|
|
108
|
+
created_from = _optional_created_time_query(values, "createdFrom")
|
|
109
|
+
created_before = _optional_created_time_query(values, "createdBefore")
|
|
110
|
+
if (
|
|
111
|
+
created_from is not None
|
|
112
|
+
and created_before is not None
|
|
113
|
+
and created_from >= created_before
|
|
114
|
+
):
|
|
115
|
+
raise AdminWebError(
|
|
116
|
+
400,
|
|
117
|
+
"invalid_time_range",
|
|
118
|
+
"创建时间的开始时间必须早于结束时间。",
|
|
119
|
+
)
|
|
120
|
+
return created_from, created_before
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _optional_created_time_query(
|
|
124
|
+
values: Mapping[str, list[str]],
|
|
125
|
+
name: str,
|
|
126
|
+
) -> str | None:
|
|
127
|
+
raw = _optional_text_query(values, name, maximum=64)
|
|
128
|
+
if raw is None:
|
|
129
|
+
return None
|
|
130
|
+
if _ISO_INSTANT_PATTERN.fullmatch(raw) is None:
|
|
131
|
+
raise AdminWebError(
|
|
132
|
+
400,
|
|
133
|
+
"invalid_time",
|
|
134
|
+
f"查询参数 {name} 必须是带时区的 ISO-8601 时间。",
|
|
135
|
+
)
|
|
136
|
+
try:
|
|
137
|
+
parsed = datetime.fromisoformat(raw.replace("Z", "+00:00"))
|
|
138
|
+
if parsed.utcoffset() is None:
|
|
139
|
+
raise ValueError("timezone is required")
|
|
140
|
+
return parsed.astimezone(UTC).isoformat(timespec="microseconds")
|
|
141
|
+
except (OverflowError, ValueError):
|
|
142
|
+
raise AdminWebError(
|
|
143
|
+
400,
|
|
144
|
+
"invalid_time",
|
|
145
|
+
f"查询参数 {name} 必须是带时区的 ISO-8601 时间。",
|
|
146
|
+
) from None
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _optional_bool_query(
|
|
150
|
+
values: Mapping[str, list[str]],
|
|
151
|
+
name: str,
|
|
152
|
+
) -> bool | None:
|
|
153
|
+
value = _optional_one(values, name)
|
|
154
|
+
if value is None:
|
|
155
|
+
return None
|
|
156
|
+
if value == "true":
|
|
157
|
+
return True
|
|
158
|
+
if value == "false":
|
|
159
|
+
return False
|
|
160
|
+
raise AdminWebError(400, "invalid_query", f"查询参数 {name} 必须是布尔值。")
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _scope_kinds_query(
|
|
164
|
+
values: Mapping[str, list[str]],
|
|
165
|
+
) -> tuple[ScopeKind, ...] | None:
|
|
166
|
+
raw = _text_set_query(values, "scopeKind")
|
|
167
|
+
if raw is None:
|
|
168
|
+
return None
|
|
169
|
+
try:
|
|
170
|
+
kinds = tuple(ScopeKind(value) for value in raw)
|
|
171
|
+
except ValueError:
|
|
172
|
+
raise AdminWebError(400, "invalid_scope_kind", "Scope 类型无效。") from None
|
|
173
|
+
return None if set(kinds) == set(ScopeKind) else kinds
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _current_query(values: Mapping[str, list[str]]) -> bool | None:
|
|
177
|
+
raw = _text_set_query(values, "current")
|
|
178
|
+
if raw is None:
|
|
179
|
+
return None
|
|
180
|
+
if any(value not in {"true", "false"} for value in raw):
|
|
181
|
+
raise AdminWebError(400, "invalid_query", "查询参数 current 必须是布尔值。")
|
|
182
|
+
return raw[0] == "true" if len(raw) == 1 else None
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _session_inventory_states(
|
|
186
|
+
values: Mapping[str, list[str]],
|
|
187
|
+
) -> tuple[SessionInventoryState, ...] | None:
|
|
188
|
+
raw = _text_set_query(values, "inventoryState")
|
|
189
|
+
if raw is None:
|
|
190
|
+
return (
|
|
191
|
+
SessionInventoryState.ACTIVE,
|
|
192
|
+
SessionInventoryState.LAZY,
|
|
193
|
+
SessionInventoryState.UNKNOWN,
|
|
194
|
+
)
|
|
195
|
+
if raw == ("all",):
|
|
196
|
+
return None
|
|
197
|
+
try:
|
|
198
|
+
states = tuple(SessionInventoryState(value) for value in raw)
|
|
199
|
+
except ValueError:
|
|
200
|
+
raise AdminWebError(
|
|
201
|
+
400,
|
|
202
|
+
"invalid_inventory_state",
|
|
203
|
+
"会话状态无效。",
|
|
204
|
+
) from None
|
|
205
|
+
return None if set(states) == set(SessionInventoryState) else states
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _id_query(values: Mapping[str, list[str]], name: str) -> tuple[str, ...]:
|
|
209
|
+
raw_values = values.get(name, [])
|
|
210
|
+
result: list[str] = []
|
|
211
|
+
for raw in raw_values:
|
|
212
|
+
for value in raw.split(","):
|
|
213
|
+
if not value or value.strip() != value or len(value.encode("utf-8")) > 256:
|
|
214
|
+
raise AdminWebError(400, "invalid_ids", f"{name} 包含无效 ID。")
|
|
215
|
+
result.append(value)
|
|
216
|
+
return tuple(result)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _fingerprint(route: str, filters: object) -> str:
|
|
220
|
+
canonical = json.dumps(
|
|
221
|
+
{"route": route, "filters": filters},
|
|
222
|
+
ensure_ascii=False,
|
|
223
|
+
separators=(",", ":"),
|
|
224
|
+
sort_keys=True,
|
|
225
|
+
).encode("utf-8")
|
|
226
|
+
return hashlib.sha256(canonical).hexdigest()[:32]
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _encode_cursor(kind: str, values: Sequence[str], fingerprint: str) -> str:
|
|
230
|
+
payload = json.dumps(
|
|
231
|
+
{"v": 1, "t": kind, "k": list(values), "f": fingerprint},
|
|
232
|
+
separators=(",", ":"),
|
|
233
|
+
sort_keys=True,
|
|
234
|
+
).encode("utf-8")
|
|
235
|
+
return base64.urlsafe_b64encode(payload).rstrip(b"=").decode("ascii")
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def _decode_cursor(
|
|
239
|
+
encoded: str | None,
|
|
240
|
+
*,
|
|
241
|
+
kind: str,
|
|
242
|
+
length: int,
|
|
243
|
+
fingerprint: str,
|
|
244
|
+
) -> tuple[str, ...] | None:
|
|
245
|
+
if encoded is None:
|
|
246
|
+
return None
|
|
247
|
+
if len(encoded) > 2_048:
|
|
248
|
+
raise AdminWebError(400, "invalid_cursor", "分页游标无效。")
|
|
249
|
+
try:
|
|
250
|
+
padding = "=" * (-len(encoded) % 4)
|
|
251
|
+
raw = base64.b64decode(
|
|
252
|
+
encoded + padding,
|
|
253
|
+
altchars=b"-_",
|
|
254
|
+
validate=True,
|
|
255
|
+
)
|
|
256
|
+
payload = json.loads(raw)
|
|
257
|
+
except (ValueError, json.JSONDecodeError):
|
|
258
|
+
raise AdminWebError(400, "invalid_cursor", "分页游标无效。") from None
|
|
259
|
+
if (
|
|
260
|
+
not isinstance(payload, dict)
|
|
261
|
+
or set(payload) != {"v", "t", "k", "f"}
|
|
262
|
+
or payload["v"] != 1
|
|
263
|
+
or payload["t"] != kind
|
|
264
|
+
or payload["f"] != fingerprint
|
|
265
|
+
or not isinstance(payload["k"], list)
|
|
266
|
+
or len(payload["k"]) != length
|
|
267
|
+
or any(not isinstance(value, str) or not value for value in payload["k"])
|
|
268
|
+
):
|
|
269
|
+
raise AdminWebError(400, "invalid_cursor", "分页游标与当前筛选不匹配。")
|
|
270
|
+
canonical = _encode_cursor(kind, payload["k"], fingerprint)
|
|
271
|
+
if canonical != encoded:
|
|
272
|
+
raise AdminWebError(400, "invalid_cursor", "分页游标无效。")
|
|
273
|
+
return tuple(payload["k"])
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _encode_binding_cursor(
|
|
277
|
+
cursor: BindingCursor | None,
|
|
278
|
+
fingerprint: str,
|
|
279
|
+
) -> str | None:
|
|
280
|
+
if cursor is None:
|
|
281
|
+
return None
|
|
282
|
+
return _encode_cursor(
|
|
283
|
+
"binding",
|
|
284
|
+
(cursor.created_at, cursor.binding_id),
|
|
285
|
+
fingerprint,
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def _decode_binding_cursor(
|
|
290
|
+
encoded: str | None,
|
|
291
|
+
fingerprint: str,
|
|
292
|
+
) -> BindingCursor | None:
|
|
293
|
+
values = _decode_cursor(
|
|
294
|
+
encoded,
|
|
295
|
+
kind="binding",
|
|
296
|
+
length=2,
|
|
297
|
+
fingerprint=fingerprint,
|
|
298
|
+
)
|
|
299
|
+
return BindingCursor(*values) if values is not None else None
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def _encode_side_cursor(
|
|
303
|
+
cursor: SideTopicCursor | None,
|
|
304
|
+
fingerprint: str,
|
|
305
|
+
) -> str | None:
|
|
306
|
+
if cursor is None:
|
|
307
|
+
return None
|
|
308
|
+
return _encode_cursor("side", (cursor.created_at, cursor.side_id), fingerprint)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def _decode_side_cursor(
|
|
312
|
+
encoded: str | None,
|
|
313
|
+
fingerprint: str,
|
|
314
|
+
) -> SideTopicCursor | None:
|
|
315
|
+
values = _decode_cursor(
|
|
316
|
+
encoded,
|
|
317
|
+
kind="side",
|
|
318
|
+
length=2,
|
|
319
|
+
fingerprint=fingerprint,
|
|
320
|
+
)
|
|
321
|
+
return SideTopicCursor(*values) if values is not None else None
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def _encode_project_cursor(cursor: str | None, fingerprint: str) -> str | None:
|
|
325
|
+
if cursor is None:
|
|
326
|
+
return None
|
|
327
|
+
return _encode_cursor("project", (cursor,), fingerprint)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def _decode_project_cursor(encoded: str | None, fingerprint: str) -> str | None:
|
|
331
|
+
values = _decode_cursor(
|
|
332
|
+
encoded,
|
|
333
|
+
kind="project",
|
|
334
|
+
length=1,
|
|
335
|
+
fingerprint=fingerprint,
|
|
336
|
+
)
|
|
337
|
+
return values[0] if values is not None else None
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
color-scheme: light;
|
|
3
|
+
--ink: #18202f;
|
|
4
|
+
--muted: #647083;
|
|
5
|
+
--line: #dce1e9;
|
|
6
|
+
--paper: #ffffff;
|
|
7
|
+
--wash: #f3f6fa;
|
|
8
|
+
--blue: #1858d7;
|
|
9
|
+
--blue-soft: #e9f0ff;
|
|
10
|
+
--danger: #b42318;
|
|
11
|
+
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
12
|
+
}
|
|
13
|
+
* { box-sizing: border-box; }
|
|
14
|
+
body { margin: 0; color: var(--ink); background: var(--wash); }
|
|
15
|
+
body.time-range-modal-open { overflow: hidden; }
|
|
16
|
+
button, input, select, textarea { font: inherit; }
|
|
17
|
+
button { cursor: pointer; }
|
|
18
|
+
.topbar { display: flex; justify-content: space-between; align-items: center; padding: 24px clamp(20px, 4vw, 56px); background: #10213f; color: white; }
|
|
19
|
+
h1, h2, h3, p { margin-top: 0; }
|
|
20
|
+
h1 { margin-bottom: 0; letter-spacing: -0.03em; }
|
|
21
|
+
.eyebrow { margin-bottom: 5px; font-size: 11px; font-weight: 750; letter-spacing: .14em; color: #86a9ec; }
|
|
22
|
+
.instance-root { overflow-wrap: anywhere; font-family: monospace; font-size: 13px; }
|
|
23
|
+
.tabs { display: flex; gap: 6px; padding: 12px clamp(20px, 4vw, 56px); background: var(--paper); border-bottom: 1px solid var(--line); }
|
|
24
|
+
.tab, .quiet, .refresh, .next { border: 1px solid var(--line); border-radius: 8px; background: white; padding: 8px 13px; color: var(--ink); }
|
|
25
|
+
.tab.active { border-color: var(--blue); color: var(--blue); background: var(--blue-soft); }
|
|
26
|
+
.quiet { background: transparent; color: white; border-color: #52627d; }
|
|
27
|
+
main { max-width: 1500px; margin: 0 auto; padding: 24px clamp(14px, 3vw, 42px) 56px; }
|
|
28
|
+
.status { min-height: 24px; color: var(--muted); }
|
|
29
|
+
.status.error { color: var(--danger); }
|
|
30
|
+
.panel { background: var(--paper); border: 1px solid var(--line); border-radius: 14px; padding: clamp(16px, 2.5vw, 28px); box-shadow: 0 8px 28px rgb(23 39 68 / 6%); }
|
|
31
|
+
.section-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
|
|
32
|
+
.section-head h2 { margin-bottom: 0; }
|
|
33
|
+
.forms-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 14px; margin: 22px 0; }
|
|
34
|
+
.card-form { border: 1px solid var(--line); border-radius: 10px; padding: 16px; }
|
|
35
|
+
.card-form label, .filter-bar label { display: grid; gap: 5px; color: var(--muted); font-size: 13px; }
|
|
36
|
+
input, select, textarea { min-width: 0; border: 1px solid #bac3d0; border-radius: 7px; padding: 8px 9px; background: white; color: var(--ink); }
|
|
37
|
+
.card-form input { width: 100%; margin: 5px 0 12px; }
|
|
38
|
+
.card-form button, .filter-bar > button[type="submit"], .action { border: 0; border-radius: 7px; padding: 8px 11px; background: var(--blue); color: white; }
|
|
39
|
+
.filter-bar { display: grid; grid-template-columns: repeat(auto-fit, minmax(145px, 1fr)); align-items: end; gap: 10px; margin: 20px 0; }
|
|
40
|
+
.multi-filter { position: relative; min-width: 0; margin: 0; padding: 0; border: 0; }
|
|
41
|
+
.multi-filter legend { margin-bottom: 5px; padding: 0; color: var(--muted); font-size: 13px; }
|
|
42
|
+
.multi-filter-trigger { display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; min-height: 36px; border: 1px solid #bac3d0; border-radius: 7px; padding: 8px 10px; background: white; color: var(--ink); text-align: left; }
|
|
43
|
+
.multi-filter-trigger[aria-expanded="true"] { border-color: var(--blue); box-shadow: 0 0 0 2px rgb(24 88 215 / 12%); }
|
|
44
|
+
.multi-filter-summary { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
45
|
+
.multi-filter-popover[hidden] { display: none; }
|
|
46
|
+
.multi-filter-popover { position: absolute; z-index: 102; top: calc(100% + 8px); left: 0; width: max(100%, 240px); max-width: calc(100vw - 40px); padding: 10px; border: 1px solid var(--line); border-radius: 9px; background: white; box-shadow: 0 12px 32px rgb(23 39 68 / 18%); }
|
|
47
|
+
.multi-filter-search { width: 100%; margin-bottom: 8px; }
|
|
48
|
+
.multi-filter-clear { width: 100%; margin-bottom: 5px; border: 0; border-radius: 5px; padding: 7px 8px; background: var(--blue-soft); color: var(--blue); text-align: left; }
|
|
49
|
+
.multi-filter-options { display: grid; max-height: 240px; overflow-y: auto; }
|
|
50
|
+
.filter-bar .multi-filter-option { display: flex; align-items: center; gap: 8px; padding: 8px; color: var(--ink); cursor: pointer; overflow-wrap: anywhere; }
|
|
51
|
+
.filter-bar .multi-filter-option[hidden] { display: none; }
|
|
52
|
+
.multi-filter-option:hover { background: var(--wash); border-radius: 5px; }
|
|
53
|
+
.multi-filter-option input { width: 16px; height: 16px; flex: 0 0 auto; margin: 0; accent-color: var(--blue); }
|
|
54
|
+
.multi-filter-empty { margin: 8px; color: var(--muted); font-size: 13px; }
|
|
55
|
+
.time-range-filter { position: relative; min-width: 0; margin: 0; padding: 0; border: 0; grid-column: span 2; }
|
|
56
|
+
.time-range-filter legend { margin-bottom: 5px; padding: 0; color: var(--muted); font-size: 13px; }
|
|
57
|
+
.time-range-control { position: relative; }
|
|
58
|
+
.time-range-trigger { width: 100%; min-height: 36px; display: flex; align-items: center; gap: 9px; border: 1px solid #bac3d0; border-radius: 7px; padding: 8px 10px; background: white; color: var(--ink); text-align: left; }
|
|
59
|
+
.time-range-trigger:hover { border-color: #8290a3; }
|
|
60
|
+
.time-range-trigger[aria-expanded="true"] { border-color: var(--blue); box-shadow: 0 0 0 2px rgb(24 88 215 / 12%); }
|
|
61
|
+
.time-range-trigger.has-value { padding-right: 58px; }
|
|
62
|
+
.time-range-calendar-icon { position: relative; width: 15px; height: 14px; flex: 0 0 auto; border: 1.5px solid currentColor; border-radius: 3px; color: var(--muted); }
|
|
63
|
+
.time-range-calendar-icon::before { content: ""; position: absolute; left: -1.5px; right: -1.5px; top: 3px; border-top: 1.5px solid currentColor; }
|
|
64
|
+
.time-range-calendar-icon::after { content: ""; position: absolute; width: 2px; height: 2px; left: 3px; top: 7px; border-radius: 50%; background: currentColor; box-shadow: 4px 0 currentColor, 8px 0 currentColor; }
|
|
65
|
+
.time-range-summary { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
66
|
+
.time-range-chevron { flex: 0 0 auto; color: var(--muted); font-size: 16px; line-height: 1; }
|
|
67
|
+
.time-range-quick-clear { position: absolute; z-index: 1; top: 50%; right: 30px; width: 24px; height: 24px; transform: translateY(-50%); border: 0; border-radius: 50%; padding: 0; background: transparent; color: var(--muted); font-size: 18px; line-height: 22px; }
|
|
68
|
+
.time-range-quick-clear:hover { background: #edf1f6; color: var(--ink); }
|
|
69
|
+
.time-range-popover[hidden], .time-range-backdrop[hidden] { display: none; }
|
|
70
|
+
.time-range-popover { position: absolute; z-index: 101; top: calc(100% + 8px); right: 0; width: min(620px, calc(100vw - 48px)); display: grid; grid-template-columns: 172px minmax(0, 1fr); overflow: hidden; border: 1px solid var(--line); border-radius: 12px; background: white; box-shadow: 0 18px 48px rgb(23 39 68 / 20%); color: var(--ink); }
|
|
71
|
+
.time-range-presets { padding: 18px 14px; border-right: 1px solid var(--line); background: #f7f9fc; }
|
|
72
|
+
.time-range-presets h3, .time-range-custom h3 { margin: 0; font-size: 14px; }
|
|
73
|
+
.time-range-presets > p { margin: 14px 4px 7px; color: var(--muted); font-size: 11px; font-weight: 700; letter-spacing: .05em; }
|
|
74
|
+
.time-range-preset-list { display: grid; gap: 3px; }
|
|
75
|
+
.time-range-preset-list button { width: 100%; border: 0; border-radius: 6px; padding: 8px 9px; background: transparent; color: var(--ink); text-align: left; }
|
|
76
|
+
.time-range-preset-list button:hover { background: #e9edf4; }
|
|
77
|
+
.time-range-preset-list button[aria-pressed="true"] { background: var(--blue-soft); color: var(--blue); font-weight: 700; }
|
|
78
|
+
.time-range-custom { min-width: 0; padding: 18px; }
|
|
79
|
+
.time-range-fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-top: 15px; }
|
|
80
|
+
.time-range-fields input { width: 100%; }
|
|
81
|
+
.time-range-hint, .time-range-zone, .time-range-error { margin: 9px 0 0; font-size: 12px; line-height: 1.4; }
|
|
82
|
+
.time-range-hint, .time-range-zone { color: var(--muted); }
|
|
83
|
+
.time-range-error { min-height: 17px; color: var(--danger); }
|
|
84
|
+
.time-range-footer { display: flex; align-items: center; gap: 8px; margin-top: 14px; }
|
|
85
|
+
.time-range-footer-spacer { flex: 1; }
|
|
86
|
+
.time-range-footer button { border: 1px solid var(--line); border-radius: 7px; padding: 7px 11px; background: white; color: var(--ink); }
|
|
87
|
+
.time-range-footer .time-range-clear { border-color: transparent; color: var(--muted); }
|
|
88
|
+
.time-range-footer .time-range-done { border-color: var(--blue); background: var(--blue); color: white; }
|
|
89
|
+
.time-range-footer button:disabled { opacity: .48; }
|
|
90
|
+
.time-range-backdrop { display: none; }
|
|
91
|
+
.table-wrap { overflow: auto; border: 1px solid var(--line); border-radius: 9px; }
|
|
92
|
+
table { width: 100%; border-collapse: collapse; min-width: 900px; }
|
|
93
|
+
#sessions table { min-width: 1250px; table-layout: fixed; }
|
|
94
|
+
#sessions th:nth-child(1) { width: 14%; }
|
|
95
|
+
#sessions th:nth-child(2) { width: 5%; }
|
|
96
|
+
#sessions th:nth-child(3) { width: 26%; }
|
|
97
|
+
#sessions th:nth-child(4) { width: 10%; }
|
|
98
|
+
#sessions th:nth-child(5) { width: 6%; }
|
|
99
|
+
#sessions th:nth-child(6) { width: 6%; }
|
|
100
|
+
#sessions th:nth-child(7) { width: 8%; }
|
|
101
|
+
#sessions th:nth-child(8) { width: 10%; }
|
|
102
|
+
#sessions th:nth-child(9) { width: 15%; }
|
|
103
|
+
th, td { padding: 11px 12px; border-bottom: 1px solid var(--line); text-align: left; vertical-align: top; font-size: 13px; }
|
|
104
|
+
th { position: sticky; top: 0; background: #f7f9fc; color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .04em; }
|
|
105
|
+
tr:last-child td { border-bottom: 0; }
|
|
106
|
+
.id { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px; color: var(--muted); word-break: break-all; }
|
|
107
|
+
.chat-link { color: var(--blue); font-weight: 700; text-decoration: none; }
|
|
108
|
+
.chat-link:hover { text-decoration: underline; }
|
|
109
|
+
.meta-line { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin-top: 5px; }
|
|
110
|
+
.preview { max-width: 380px; margin-top: 4px; color: var(--muted); line-height: 1.4; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
111
|
+
.runtime-subscription { color: var(--muted); }
|
|
112
|
+
.badge { display: inline-block; border-radius: 999px; padding: 3px 7px; background: #edf1f6; font-size: 11px; }
|
|
113
|
+
.badge.on { background: #e5f6ed; color: #136c3c; }
|
|
114
|
+
.badge.warn { background: #fff2df; color: #925400; }
|
|
115
|
+
.actions { display: flex; flex-wrap: wrap; gap: 5px; }
|
|
116
|
+
.project-delete-result { margin: 16px 0; padding: 14px 16px; border: 1px solid var(--line); border-radius: 9px; background: var(--wash); font-size: 13px; line-height: 1.6; }
|
|
117
|
+
.project-delete-result.error { border-color: #e3b8b4; color: var(--danger); }
|
|
118
|
+
.project-delete-result p { margin: 0; white-space: pre-line; }
|
|
119
|
+
.project-delete-result ul { margin: 10px 0 0; padding-left: 20px; overflow-wrap: anywhere; }
|
|
120
|
+
.action { padding: 5px 8px; font-size: 12px; }
|
|
121
|
+
.action.danger { background: var(--danger); }
|
|
122
|
+
.next { margin-top: 14px; }
|
|
123
|
+
button:disabled { cursor: default; opacity: .48; }
|
|
124
|
+
.pagination { display: flex; align-items: center; justify-content: flex-end; gap: 10px; margin-top: 14px; color: var(--muted); font-size: 13px; }
|
|
125
|
+
.pagination .next { margin-top: 0; }
|
|
126
|
+
.update-versions { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 28px 0 20px; }
|
|
127
|
+
.update-versions p { margin: 8px 0; color: var(--muted); font-size: 13px; }
|
|
128
|
+
.update-versions strong { font-size: 24px; overflow-wrap: anywhere; }
|
|
129
|
+
.update-operation { padding: 16px; margin: 20px 0; border-radius: 9px; background: var(--wash); }
|
|
130
|
+
.update-operation p { margin: 8px 0 0; line-height: 1.6; }
|
|
131
|
+
.update-warning { color: var(--muted); font-size: 13px; line-height: 1.6; }
|
|
132
|
+
.update-actions { gap: 10px; }
|
|
133
|
+
.update-actions .action { padding: 8px 13px; font-size: inherit; }
|
|
134
|
+
.update-help { margin-top: 16px; font-size: 13px; }
|
|
135
|
+
.service-restart { margin-top: 28px; padding-top: 20px; border-top: 1px solid var(--line); }
|
|
136
|
+
.service-restart p { line-height: 1.6; }
|
|
137
|
+
#updates a { color: var(--blue); }
|
|
138
|
+
.update-notes { margin-top: 28px; padding-top: 20px; border-top: 1px solid var(--line); }
|
|
139
|
+
#update-notes { white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.6; }
|
|
140
|
+
button:focus-visible, input:focus-visible, select:focus-visible { outline: 3px solid #8cb4ff; outline-offset: 2px; }
|
|
141
|
+
.login-body { min-height: 100vh; display: grid; place-items: center; padding: 20px; }
|
|
142
|
+
.login-card { width: min(420px, 100%); background: white; border: 1px solid var(--line); border-radius: 14px; padding: 28px; }
|
|
143
|
+
.login-card label { display: grid; gap: 7px; }
|
|
144
|
+
.login-card input { margin-bottom: 14px; }
|
|
145
|
+
.login-card button { width: 100%; border: 0; border-radius: 8px; padding: 10px; background: var(--blue); color: white; }
|
|
146
|
+
@media (max-width: 650px) {
|
|
147
|
+
.topbar { padding: 18px; }
|
|
148
|
+
.tabs { overflow-x: auto; padding: 10px 14px; }
|
|
149
|
+
main { padding: 14px 8px 40px; }
|
|
150
|
+
.panel { border-radius: 9px; padding: 12px; }
|
|
151
|
+
.time-range-filter { grid-column: 1 / -1; }
|
|
152
|
+
.time-range-backdrop { position: fixed; z-index: 100; inset: 0; display: block; background: rgb(16 33 63 / 38%); }
|
|
153
|
+
.time-range-popover { position: fixed; z-index: 101; top: auto; right: 10px; bottom: 10px; left: 10px; width: auto; max-height: calc(100dvh - 20px); display: block; overflow-y: auto; border-radius: 14px; }
|
|
154
|
+
.time-range-presets { border-right: 0; border-bottom: 1px solid var(--line); padding: 16px; }
|
|
155
|
+
.time-range-presets > p { margin-top: 11px; }
|
|
156
|
+
.time-range-preset-list { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 5px; }
|
|
157
|
+
.time-range-custom { padding: 16px; }
|
|
158
|
+
.time-range-fields { grid-template-columns: 1fr; }
|
|
159
|
+
}
|
|
160
|
+
.schedule-help { margin-top: 18px; color: var(--muted); }
|
|
161
|
+
body.schedule-drawer-open, body.defaults-drawer-open { overflow: hidden; }
|
|
162
|
+
.editor-drawer { position: fixed; inset: 0 0 0 auto; margin: 0; padding: 0; width: min(720px, 100vw); max-width: 100vw; height: 100dvh; max-height: 100dvh; border: 0; color: var(--ink); background: var(--paper); box-shadow: -12px 0 40px rgb(23 39 68 / 16%); }
|
|
163
|
+
.editor-drawer::backdrop { background: rgb(16 33 63 / 35%); }
|
|
164
|
+
#schedule-editor { height: 100%; display: flex; flex-direction: column; border: 0; border-radius: 0; padding: 0; margin: 0; }
|
|
165
|
+
.schedule-editor-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 22px 24px; border-bottom: 1px solid var(--line); }
|
|
166
|
+
.schedule-editor-head h3 { margin: 0; font-size: 22px; }
|
|
167
|
+
.schedule-editor-head .eyebrow { color: var(--muted); }
|
|
168
|
+
#schedule-editor #schedule-close { padding: 4px 12px; font-size: 28px; background: transparent; color: var(--muted); }
|
|
169
|
+
#schedule-editor #schedule-fields { flex: 1; min-height: 0; display: flex; flex-direction: column; }
|
|
170
|
+
.schedule-editor-body { flex: 1; overflow-y: auto; padding: 24px; }
|
|
171
|
+
.schedule-editor-body h4 { margin: 24px 0 12px; }
|
|
172
|
+
.schedule-editor-footer { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 16px 24px; border-top: 1px solid var(--line); background: var(--paper); }
|
|
173
|
+
#schedule-editor #schedule-cancel, #schedule-editor #schedule-preview { color: var(--ink); background: white; border: 1px solid var(--line); }
|
|
174
|
+
#schedule-editor input { margin: 0; }
|
|
175
|
+
#schedule-timezone-note { margin-bottom: 14px; }
|
|
176
|
+
#schedule-filter { grid-template-columns: minmax(160px, 260px) repeat(2, minmax(120px, 180px)) max-content; justify-content: start; }
|
|
177
|
+
#schedules-body td { min-width: 90px; }
|
|
178
|
+
#schedules-body td:first-child { min-width: 150px; }
|
|
179
|
+
#schedules-body .schedule-time { min-width: 200px; }
|
|
180
|
+
#schedules-body .schedule-state { min-width: 118px; white-space: pre-line; }
|
|
181
|
+
#schedules-body .schedule-execution { min-width: 150px; white-space: pre-line; }
|
|
182
|
+
#schedules-body .schedule-time, #schedules-body .schedule-target { overflow-wrap: anywhere; }
|
|
183
|
+
#schedules-body .schedule-note { display: block; margin-top: 7px; color: var(--muted); font-size: 12px; line-height: 1.5; }
|
|
184
|
+
@media (max-width: 540px) {
|
|
185
|
+
.schedule-editor-head, .schedule-editor-body { padding: 16px; }
|
|
186
|
+
.schedule-editor-footer { padding: 12px 16px; flex-wrap: wrap; }
|
|
187
|
+
#schedule-filter { grid-template-columns: 1fr; }
|
|
188
|
+
}
|
|
189
|
+
#schedule-editor[hidden], #schedule-detail[hidden] { display: none; }
|
|
190
|
+
#schedule-fields { border: 0; padding: 0; margin: 0; min-width: 0; }
|
|
191
|
+
.schedule-form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; margin-bottom: 14px; }
|
|
192
|
+
.schedule-form-grid [hidden], #schedule-weekdays[hidden], #schedule-end-condition[hidden] { display: none; }
|
|
193
|
+
#schedule-end-condition { border: 1px solid var(--line); border-radius: 8px; margin: 14px 0; padding: 12px; }
|
|
194
|
+
#schedule-end-condition .schedule-form-grid { margin-bottom: 10px; }
|
|
195
|
+
#schedule-end-condition .meta-line { margin: 8px 0 0; }
|
|
196
|
+
#schedule-weekdays { display: flex; flex-wrap: wrap; gap: 12px; border: 1px solid var(--line); margin: 0 0 14px; }
|
|
197
|
+
#schedule-weekdays label, #schedule-editor .schedule-checkbox { display: flex; align-items: center; gap: 7px; }
|
|
198
|
+
#schedule-weekdays input, #schedule-editor .schedule-checkbox input { width: auto; margin: 0; }
|
|
199
|
+
#schedule-editor .schedule-checkbox { margin: 14px 0; }
|
|
200
|
+
#schedule-instructions { width: 100%; resize: vertical; }
|
|
201
|
+
#schedule-editor button:disabled { opacity: .5; cursor: default; }
|
|
202
|
+
#schedule-preview-message { margin: 14px 0 0; }
|
|
203
|
+
#schedule-preview-message.error { color: var(--danger); }
|
|
204
|
+
.schedule-detail { margin-top: 24px; padding-top: 20px; border-top: 1px solid var(--line); }
|
|
205
|
+
#schedule-detail-instructions { white-space: pre-wrap; overflow-wrap: anywhere; font: inherit; background: var(--wash); padding: 14px; border-radius: 8px; }
|
|
206
|
+
.preserve-lines { white-space: pre-line; overflow-wrap: anywhere; }
|
|
207
|
+
@media (max-width: 700px) { .tabs { flex-wrap: wrap; } }
|
|
208
|
+
.schedule-session-settings { margin-top: 16px; padding: 12px; border: 1px solid var(--line); border-radius: 8px; }
|
|
209
|
+
.schedule-session-settings summary { cursor: pointer; overflow-wrap: anywhere; }
|
|
210
|
+
.drawer-form { height: 100%; display: flex; flex-direction: column; border: 0; border-radius: 0; padding: 0; margin: 0; }
|
|
211
|
+
.drawer-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 22px 24px; border-bottom: 1px solid var(--line); }
|
|
212
|
+
.drawer-head h3 { margin: 0; font-size: 22px; }
|
|
213
|
+
.drawer-head .eyebrow { color: var(--muted); }
|
|
214
|
+
.drawer-form .drawer-close { padding: 4px 12px; font-size: 28px; background: transparent; color: var(--muted); }
|
|
215
|
+
.drawer-body { flex: 1; min-height: 0; overflow-y: auto; padding: 24px; }
|
|
216
|
+
.drawer-body h4 { margin: 24px 0 12px; }
|
|
217
|
+
.drawer-body h4:first-child { margin-top: 0; }
|
|
218
|
+
.drawer-footer { display: flex; justify-content: flex-end; gap: 10px; padding: 16px 24px; border-top: 1px solid var(--line); }
|
|
219
|
+
.drawer-form .drawer-cancel { color: var(--ink); background: white; border: 1px solid var(--line); }
|
|
220
|
+
.drawer-form input { margin: 0; }
|
|
221
|
+
#defaults [hidden], #defaults-editor[hidden], #defaults-editor [hidden] { display: none; }
|
|
222
|
+
.defaults-help, .defaults-order-help, .defaults-field-help { color: var(--muted); font-size: 13px; line-height: 1.6; }
|
|
223
|
+
.defaults-help { margin: 12px 0 24px; }
|
|
224
|
+
.defaults-toolbar { display: flex; justify-content: space-between; align-items: center; gap: 16px; margin-bottom: 18px; }
|
|
225
|
+
.defaults-toolbar > .action { padding: 8px 13px; font-size: 13px; }
|
|
226
|
+
.defaults-tabs { display: flex; gap: 4px; padding: 4px; border-radius: 9px; background: var(--wash); }
|
|
227
|
+
.defaults-tab { border: 0; border-radius: 6px; padding: 8px 16px; background: transparent; color: var(--muted); white-space: nowrap; }
|
|
228
|
+
.defaults-tab[aria-selected="true"] { background: var(--paper); color: var(--blue); box-shadow: 0 1px 4px rgb(23 39 68 / 12%); font-weight: 600; }
|
|
229
|
+
.defaults-tab:hover { color: var(--blue); }
|
|
230
|
+
.defaults-order-help { margin-bottom: 14px; }
|
|
231
|
+
.defaults-field-help { margin: 8px 0 0; }
|
|
232
|
+
#defaults-note { margin-top: 24px; }
|
|
233
|
+
#defaults-fields { border: 0; padding: 0; margin: 0; min-width: 0; }
|
|
234
|
+
#defaults-fields:disabled { opacity: .6; }
|
|
235
|
+
.defaults-feedback { display: flex; flex-wrap: wrap; gap: 18px; }
|
|
236
|
+
.defaults-feedback label { display: flex; align-items: center; gap: 7px; }
|
|
237
|
+
.drawer-form .defaults-feedback input { width: 16px; height: 16px; margin: 0; accent-color: var(--blue); }
|
|
238
|
+
#defaults-note.error { color: var(--danger); }
|
|
239
|
+
#defaults table { table-layout: fixed; }
|
|
240
|
+
#defaults th:first-child { width: 25%; }
|
|
241
|
+
#defaults th:nth-child(2) { width: 16%; }
|
|
242
|
+
#defaults th:last-child { width: 17%; }
|
|
243
|
+
#defaults-panel-group_name th:first-child { width: 8%; }
|
|
244
|
+
#defaults-panel-group_name th:nth-child(2) { width: 22%; }
|
|
245
|
+
#defaults-panel-group_name th:nth-child(3) { width: 14%; }
|
|
246
|
+
#defaults-panel-group_name th:last-child { width: 23%; }
|
|
247
|
+
#defaults td { overflow-wrap: anywhere; line-height: 1.6; }
|
|
248
|
+
#defaults td.actions { display: table-cell; }
|
|
249
|
+
#defaults .action.secondary { border: 1px solid var(--line); background: white; color: var(--ink); }
|
|
250
|
+
#defaults .action.secondary.danger { color: var(--danger); }
|
|
251
|
+
#defaults .default-settings-detail { margin-top: 5px; color: var(--muted); font-size: 12px; }
|
|
252
|
+
#defaults .defaults-empty { padding: 42px 20px; text-align: center; }
|
|
253
|
+
.defaults-empty p { margin: 0 0 14px; color: var(--muted); }
|
|
254
|
+
@media (max-width: 650px) {
|
|
255
|
+
.drawer-head, .drawer-body { padding: 16px; }
|
|
256
|
+
.drawer-footer { padding: 12px 16px; }
|
|
257
|
+
.defaults-toolbar { flex-wrap: wrap; gap: 12px; }
|
|
258
|
+
.defaults-tab { padding: 8px 12px; }
|
|
259
|
+
}
|
|
260
|
+
#schedule-session-note { color: var(--muted); margin: 12px 0; font-size: 13px; }
|