migrate ui components to gpui-kit

This commit is contained in:
2026-10-21 13:40:29 +07:00
parent 0d4acf5b65
commit d0ad1a6f57
173 changed files with 2775 additions and 10885 deletions
+189
View File
@@ -0,0 +1,189 @@
#!/usr/bin/env python3
"""Phase A sweep: mechanical import + token rewrites toward gpui-kit.
Run from repo root: python3 script/migration/sweep.py
Idempotent; only touches consumer crates.
"""
import re
import sys
from pathlib import Path
FILES = [
"crates/settings/src/lib.rs",
"crates/nip4e/src/lib.rs",
"crates/nip29_ui/src/lib.rs",
"crates/nip29_ui/src/message.rs",
"crates/nip29_ui/src/details.rs",
"crates/nip29_ui/src/invite.rs",
"crates/nip29_ui/src/metadata.rs",
"crates/nip29_ui/src/util.rs",
"crates/chat_ui/src/lib.rs",
"crates/chat_ui/src/text.rs",
"crates/chat_ui/src/actions.rs",
"crates/chat_ui/src/file.rs",
"crates/workspace/src/lib.rs",
"crates/workspace/src/dialogs/import.rs",
"crates/workspace/src/dialogs/join_group.rs",
"crates/workspace/src/dialogs/new_chat.rs",
"crates/workspace/src/dialogs/new_group.rs",
"crates/workspace/src/dialogs/profile.rs",
"crates/workspace/src/dialogs/relays.rs",
"crates/workspace/src/dialogs/restore.rs",
"crates/workspace/src/dialogs/screening.rs",
"crates/workspace/src/dialogs/settings.rs",
"crates/workspace/src/panels/backup.rs",
"crates/workspace/src/panels/browse.rs",
"crates/workspace/src/panels/contact_list.rs",
"crates/workspace/src/panels/greeter.rs",
"crates/workspace/src/panels/inbox.rs",
"crates/workspace/src/sidebar/group_view.rs",
"crates/workspace/src/sidebar/header.rs",
"crates/workspace/src/sidebar/inbox.rs",
"crates/workspace/src/sidebar/list.rs",
"crates/workspace/src/sidebar/mod.rs",
"crates/workspace/src/sidebar/onboarding.rs",
"crates/workspace/src/sidebar/rows.rs",
"crates/workspace/src/sidebar/search.rs",
]
KIT = "gpui_kit::component"
# ui:: items that move to the kit's component root
KIT_ITEMS = {
"Icon", "IconName", "Sizable", "Size", "Disableable", "Selectable",
"StyledExt", "h_flex", "v_flex", "WindowExt",
"Notification", "NotificationType",
"Button", "ButtonVariants", "ButtonVariant", "ButtonCustomVariant", "IconPosition",
"Switch", "Avatar", "GroupBox", "GroupBoxVariants", "GroupBoxVariant",
"Tab", "TabBar", "Scrollbar", "ScrollableElement", "Tooltip",
"Input", "InputState", "Textarea", "TextareaState", "InputEvent",
"PopupMenu", "PopupMenuItem", "DropdownMenu", "ContextMenu",
"Dialog", "DialogButtonProps", "AlertDialog",
"ActiveTheme", "Theme", "ThemeMode",
"Spinner", "Kbd", "TitleBar",
"DockArea", "DockLayout", "DockPlacement", "Panel", "PanelEvent",
"PanelHandle", "ClosePanel",
"Divider", "Separator",
}
# ui:: items that stay in the coop ui crate (utilities)
UI_ITEMS = {
"TextStyle", "Typeset", "Metrics", "base_text_size", "DEFAULT_BASE_TEXT_SIZE",
"SPACE", "CONTENT_MARGIN", "EDGE", "BUTTON_HEIGHT", "CONTROL_HEIGHT_SMALL",
"BASE_RADIUS", "bubble_radius", "surface_radius", "panel_radius", "button_radius",
"control_radius", "inset_radius",
"TABBAR_HEIGHT", "SIDEBAR_WIDTH", "TRAFFIC_LIGHT_PADDING",
"CLIENT_SIDE_DECORATION_ROUNDING", "CLIENT_SIDE_DECORATION_SHADOW",
"CLIENT_SIDE_DECORATION_BORDER",
"title_bar_drag_handlers",
}
TOKEN_RENAMES = [
# order matters: longest first
("surface_raised_hover", "secondary_hover"),
("surface_raised", "secondary"),
("surface_dialog", "popover"),
("surface_overlay", "popover"),
("surface_card", "muted"),
("surface", "sidebar"),
("element_hover", "accent"),
("element_active", "list_active"),
("border_faint", "border"),
("border_strong", "border"),
("text_muted", "muted_foreground"),
("text_faint", "muted_foreground"),
("text_dim", "muted_foreground"),
("on_solid", "primary_foreground"),
("solid", "primary"),
("accent_strong", "primary"),
("accent", "primary"),
("danger_strong", "button_danger"),
("danger_muted", "danger"),
("warning_muted", "warning.opacity(0.15)"),
("drop_line", "drag_border"),
("input_bg", "input"),
("bg", "background"),
("text", "foreground"),
]
def rewrite_use(line: str):
"""Rewrite one `use ui::...;` or `use theme::...;` line. Returns new lines."""
s = line.strip()
if s.startswith("use ui::{") and s.endswith(";"):
inner = s[len("use ui::{"):-2]
items = [i.strip() for i in inner.split(",") if i.strip()]
kit, ui = [], []
for item in items:
name = item.split(" as ")[0].strip()
(ui if name in UI_ITEMS else kit).append(item)
out = []
if kit:
out.append(f"use {KIT}::{{{', '.join(sorted(set(kit)))}}};")
if ui:
out.append(f"use ui::{{{', '.join(sorted(set(ui)))}}};")
return out
if s.startswith("use ui::") and s.endswith(";"):
path = s[4:-1]
item = path.split("::")[-1]
if item in UI_ITEMS:
return [f"use ui::{item};"]
return [f"use {KIT}::{{{item}}};"]
if s.startswith("use theme::{") and s.endswith(";"):
inner = s[len("use theme::{"):-2]
items = [i.strip() for i in inner.split(",") if i.strip()]
kit, ui = [], []
for item in items:
name = item.split("::")[-1]
if name == "ActiveTheme" or name in KIT_ITEMS:
kit.append(name)
else:
ui.append(name)
out = []
if kit:
out.append(f"use {KIT}::{{{', '.join(sorted(set(kit)))}}};")
if ui:
out.append(f"use ui::{{{', '.join(sorted(set(ui)))}}};")
return out
if s.startswith("use theme::") and s.endswith(";"):
name = s[11:-1].split("::")[-1]
if name == "ActiveTheme" or name in KIT_ITEMS:
return [f"use {KIT}::{name};"]
return [f"use ui::{name};"]
return [line]
def process(path: Path):
src = path.read_text()
lines = src.split("\n")
out = []
for line in lines:
s = line.strip()
if s.startswith("use ui::") or s.startswith("use theme::"):
out.extend(rewrite_use(line))
else:
out.append(line)
src = "\n".join(out)
# theme token renames, applied to `theme().x` reads only
for old, new in TOKEN_RENAMES:
src = re.sub(
re.escape("theme()." + old) + r"(?![a-z_0-9])",
"theme()." + new,
src,
)
# method/type renames
src = src.replace("WindowExtension", "WindowExt")
src = src.replace(".clear_notification_by_id::<", ".remove_notification::<")
src = src.replace(".clear_notification::<", ".remove_notification::<")
src = src.replace("with_kind(NotificationKind::", "with_type(NotificationType::")
src = src.replace("NotificationKind", "NotificationType")
path.write_text(src)
for f in FILES:
p = Path(f)
if p.exists():
process(p)
print("swept", f)
else:
print("MISSING", f, file=sys.stderr)