Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
9352439
feat(web): option to always say "pull request" instead of host terms
Nov 2, 2026
b468170
feat(web,mobile): name cross-project PR surfaces after the user's hosts
Nov 2, 2026
9a33c4d
refactor: simplify merge request terminology to a global toggle
Nov 2, 2026
a84444f
fix(mobile): support the merge request terminology toggle
Nov 2, 2026
6109e03
fix(clients): apply review terminology throughout the UI
Nov 2, 2026
4cfe1e0
fix(clients): address terminology review and mobile CI failures
Nov 2, 2026
e717434
fix(clients): merge main while preserving request terminology
maria-rcks Nov 3, 2026
cf8bcb2
fix(clients): refresh cached terminology and mobile close confirmation
maria-rcks Nov 3, 2026
a16ebd4
fix(mobile): refresh retained work log terminology
maria-rcks Nov 3, 2026
3af4200
Merge remote-tracking branch 'github/main' into pr-29576-exact
maria-rcks Nov 3, 2026
1cf225a
fix(web): find copied review links by selected terminology
maria-rcks Nov 3, 2026
26f87ed
test(mobile): mock terminology state in file preview test
MasonNight Nov 3, 2026
ab296cc
fix(web): refresh keybinding labels when terminology changes
MasonNight Nov 4, 2026
4f1d64c
Merge remote-tracking branch 'origin/main' into pr-29576
MasonNight Nov 4, 2026
28236b8
Merge remote-tracking branch 'origin/main' into pr-29576
MasonNight Nov 4, 2026
6e2e1e7
Merge remote-tracking branch 'origin/main' into pr-29576
MasonNight Nov 5, 2026
f57571c
Merge remote-tracking branch 'origin/main' into pr-29576
MasonNight Nov 5, 2026
88c98e0
Merge remote-tracking branch 'origin/main' into pr-29576
MasonNight Nov 5, 2026
bfe7b67
Merge remote-tracking branch 'origin/main' into pr-29576
MasonNight Nov 5, 2026
4f9db81
Merge remote-tracking branch 'origin/main' into pr-29576
MasonNight Nov 5, 2026
9414e5e
Merge remote-tracking branch 'origin/main' into pr-29576
MasonNight Nov 5, 2026
473ab49
Merge remote-tracking branch 'origin/main' into pr-29576
MasonNight Nov 5, 2026
1720d7b
Merge remote-tracking branch 'origin/main' into pr-29576
MasonNight Nov 6, 2026
a8fa449
Merge remote-tracking branch 'origin/main' into pr-29576
MasonNight Nov 6, 2026
1613f3f
Merge remote-tracking branch 'origin/main' into pr-29576
MasonNight Nov 6, 2026
1d84ba4
fix(web): use selected terms for conversation context titles
MasonNight Nov 6, 2026
83c970b
Merge remote-tracking branch 'github/main' into pr-29576
maria-rcks Nov 6, 2026
b9d552e
fix(mobile): update native Android merge request labels
MasonNight Nov 6, 2026
5bf0551
Merge remote-tracking branch 'github/main' into pr-29576
maria-rcks Nov 6, 2026
c9521c5
Merge origin/main into feature/vcs-specific-terminology
MasonNight Nov 7, 2026
f036996
fix(mobile): update compact iOS tab terminology
MasonNight Nov 7, 2026
72fbfc6
fix(client-runtime): format review progress from older servers
MasonNight Nov 8, 2026
449c13d
fix(client-runtime): normalize older server review completion labels
MasonNight Nov 8, 2026
8e31712
Merge origin/main into feature/vcs-specific-terminology
MasonNight Nov 8, 2026
305986a
fix(clients): normalize older GitLab action labels
MasonNight Nov 8, 2026
58b6903
fix(web): keep terminology preference title fixed
MasonNight Nov 8, 2026
071ab37
fix(web): index selected terminology in settings search
MasonNight Nov 9, 2026
e6839bc
merge: bring main into terminology branch
MasonNight Nov 9, 2026
e4bfacd
merge: update terminology branch with main
MasonNight Nov 9, 2026
c62cf2d
refactor(web): reuse diff settings terminology subscription
MasonNight Nov 9, 2026
f60df1f
fix(web): preserve original settings search labels
MasonNight Nov 9, 2026
932a214
fix(web): retain canonical titles in settings search
MasonNight Nov 9, 2026
5068e51
Merge remote-tracking branch 'github/main' into feature/vcs-specific-…
maria-rcks Nov 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ class T3WorkspaceNavigationModule : Module() {
Prop("selectedTab") { view: T3WorkspaceNavigationView, value: String ->
view.selectedTab = value
}
Prop("pullRequestsTitle") { view: T3WorkspaceNavigationView, value: String ->
view.pullRequestsTitle = value
}
Prop("pullRequestsExpandedTitle") { view: T3WorkspaceNavigationView, value: String ->
view.pullRequestsExpandedTitle = value
}
Prop("floating") { view: T3WorkspaceNavigationView, value: Boolean ->
view.floating = value
view.clipToPadding = !value
Expand Down Expand Up @@ -128,6 +134,8 @@ class T3WorkspaceNavigationView(
private val onOverlayChange by EventDispatcher()
var tabs by mutableStateOf(listOf("threads", "pullRequests", "usage", "settings"))
var selectedTab by mutableStateOf("threads")
var pullRequestsTitle by mutableStateOf("Pulls")
var pullRequestsExpandedTitle by mutableStateOf("Pull Requests")
var rail by mutableStateOf(false)
var floating by mutableStateOf(false)
var dark by mutableStateOf(false)
Expand Down Expand Up @@ -414,13 +422,13 @@ class T3WorkspaceNavigationView(

private fun tabTitle(tab: String) = when (tab) {
"threads" -> "Chats"
"pullRequests" -> "Pulls"
"pullRequests" -> pullRequestsTitle
"usage" -> "Usage"
else -> "Settings"
}

private fun expandedTabTitle(tab: String) =
if (tab == "pullRequests") "Pull Requests" else tabTitle(tab)
if (tab == "pullRequests") pullRequestsExpandedTitle else tabTitle(tab)

@Composable
private fun tabIconPainter(tab: String) = painterResource(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public final class T3NativeControlsModule: Module {
Events("onSelectionChange", "onCompose")
Prop("tabs") { (view: T3SidebarTabBarView, value: [String]) in view.tabs = value }
Prop("selectedTab") { (view: T3SidebarTabBarView, value: String) in view.selectedTab = value }
Prop("pullRequestsTitle") { (view: T3SidebarTabBarView, value: String) in view.pullRequestsTitle = value }
Prop("selectedColor") { (view: T3SidebarTabBarView, value: UIColor) in view.selectedColor = value }
Prop("showsCompose") { (view: T3SidebarTabBarView, value: Bool) in view.showsCompose = value }
Prop("compact") { (view: T3SidebarTabBarView, value: Bool) in view.compact = value }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ final class T3SidebarTabBarView: ExpoView, UITabBarDelegate {
var tabs = ["threads", "pullRequests", "usage", "settings"] {
didSet { if tabs != oldValue { updateItems() } }
}
var pullRequestsTitle = "PRs" {
didSet { itemsByTab["pullRequests"]?.accessibilityLabel = pullRequestsTitle }
}
var selectedTab = "threads" { didSet { updateSelection() } }
var selectedColor: UIColor = .systemBlue { didSet { tabBar.tintColor = selectedColor } }
var compact = false {
Expand Down Expand Up @@ -106,7 +109,7 @@ final class T3SidebarTabBarView: ExpoView, UITabBarDelegate {
private func prepareItems() {
let iconBundle = Bundle(for: Self.self).url(forResource: "T3NativeControlIcons", withExtension: "bundle").flatMap(Bundle.init(url:))
let pullRequestIcon = UIImage(named: "pull-request", in: iconBundle, compatibleWith: nil)?.withRenderingMode(.alwaysTemplate)
let labels = ["threads": "Chats", "pullRequests": "PRs", "usage": "Usage", "settings": "Settings"]
let labels = ["threads": "Chats", "pullRequests": pullRequestsTitle, "usage": "Usage", "settings": "Settings"]
let symbols = ["threads": "text.bubble", "usage": "chart.bar.xaxis", "settings": "gearshape"]
for (tab, label) in labels {
let icon = tab == "pullRequests" ? pullRequestIcon : UIImage(systemName: symbols[tab] ?? "circle")
Expand Down
2 changes: 2 additions & 0 deletions apps/mobile/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ import {
} from "./lib/foundation-fast-refresh";
import { useMobileNavigationTheme } from "./lib/useMobileNavigationTheme";
import { installUiRuntimeMemoryWarningGc } from "./lib/useUiRuntimeMemoryWarningGc";
import { installChangeRequestTerminologyRuntime } from "./state/change-request-terminology";
import { SubscriptionUsageCoordinator } from "./widgets/SubscriptionUsageCoordinator";
import { installSubscriptionUsageRefresh } from "./widgets/useSubscriptionUsage";
import { GlobalVoiceInputControl } from "./features/voice-input/GlobalVoiceInputControl";
Expand Down Expand Up @@ -190,6 +191,7 @@ const appServiceDisposers = [
pendingThreadIdsAtom: threadEnvironment.pendingThreadIdsAtom,
}),
installAppearanceRuntime(),
installChangeRequestTerminologyRuntime(),
installSubscriptionUsageRefresh(),
installPermissionUpdateNotice(),
installThreadOutboxDelivery(),
Expand Down
7 changes: 2 additions & 5 deletions apps/mobile/src/components/ComposerContextSheet.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useChangeRequestTerms } from "../state/change-request-terminology";
import { Button } from "./ui/Button";
import * as Dialog from "./ui/Dialog";
import { useOpenPullRequest } from "../native/useOpenPullRequest";
Expand All @@ -11,7 +12,6 @@ import type {
ScopedThreadRef,
} from "@t3tools/contracts";
import { formatAttachmentSize } from "@t3tools/client-runtime/state/attachments";
import { sourceControlClients } from "@t3tools/client-runtime/source-control-clients";
import { videoMimeType } from "@t3tools/shared/video";
import { Pressable, ScrollView, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
Expand Down Expand Up @@ -98,6 +98,7 @@ export function ComposerContextSheet(props: {
});
const reviewColors = useReviewCommentColors();
const insets = useSafeAreaInsets();
const changeRequestTerm = useChangeRequestTerms().singular;
const { themeId, themeAppearance } = useAppearancePreferences();
const terminalTheme = getMobileTerminalTheme(themeId, themeAppearance);
const record = props.record;
Expand Down Expand Up @@ -183,10 +184,6 @@ export function ComposerContextSheet(props: {
record?.kind === "review-comment" && "pullRequest" in record
? record.pullRequest?.url
: undefined;
const changeRequestTerm = (
(pullRequestUrl ? sourceControlClients.findByChangeRequestUrl(pullRequestUrl) : undefined) ??
sourceControlClients.get(undefined)
).changeRequest.singular;
const terminal = record?.kind === "terminal" && !("payload" in record) ? record : null;
return (
<Sheet title={terminal?.terminalLabel ?? props.label} onClose={props.onClose}>
Expand Down
55 changes: 31 additions & 24 deletions apps/mobile/src/features/connection/PullRequestRoutingSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useChangeRequestTerms } from "../../state/change-request-terminology";
import { capitalizeTerm } from "@t3tools/client-runtime/source-control-clients";
import { ListChoiceRow } from "../../components/ui/ListChoiceRow";
import * as Dialog from "../../components/ui/Dialog";
import { useAtomValue } from "@effect/atom-react";
Expand All @@ -16,26 +18,31 @@ import { environmentCatalog } from "../../connection/catalog";
import { useAtomCommand } from "../../state/use-atom-command";
import { SettingsSection } from "../settings/components/SettingsSection";

const options: ReadonlyArray<{
value: PullRequestRoutingPermission;
label: string;
description: string;
}> = [
{ value: "off", label: "Off", description: "Keep source control requests on this environment." },
{
value: "read",
label: "Read PRs",
description: "Share PR data with other enabled environments.",
},
{
value: "read-write",
label: "Read and act",
description:
"Actions may use broader source control permissions than the original environment.",
},
];

export function PullRequestRoutingSettings() {
const terms = useChangeRequestTerms();
const options: ReadonlyArray<{
value: PullRequestRoutingPermission;
label: string;
description: string;
}> = [
{
value: "off",
label: "Off",
description: "Keep source control requests on this environment.",
},
{
value: "read",
label: `Read ${terms.shortLabel}s`,
description: `Share ${terms.shortLabel} data with other enabled environments.`,
},
{
value: "read-write",
label: "Read and act",
description:
"Actions may use broader source control permissions than the original environment.",
},
];

const catalog = useAtomValue(environmentCatalog.catalogValueAtom);
const permissions = useAtomValue(environmentCatalog.pullRequestRoutingPermissionsValueAtom);
const update = useAtomCommand(environmentCatalog.setPullRequestRoutingPermission);
Expand All @@ -45,7 +52,7 @@ export function PullRequestRoutingSettings() {

return (
<View className="mt-5 gap-3">
<SettingsSection title="Pull request routing">
<SettingsSection title={`${capitalizeTerm(terms.singular)} routing`}>
{[...catalog.entries.values()].map((entry) => {
const environmentId = entry.target.environmentId;
const selected = pullRequestRoutingPermissionFor(entry, permissions);
Expand All @@ -55,7 +62,7 @@ export function PullRequestRoutingSettings() {
<View key={environmentId}>
<Pressable
accessibilityRole="button"
accessibilityLabel={`${entry.target.label} pull request routing`}
accessibilityLabel={`${entry.target.label} ${terms.singular} routing`}
accessibilityState={{ expanded: expanded === environmentId }}
className="flex-row items-center gap-3 p-4"
onPress={() => setExpanded(expanded === environmentId ? null : environmentId)}
Expand Down Expand Up @@ -91,7 +98,7 @@ export function PullRequestRoutingSettings() {
setSaving(false);
if (result._tag === "Failure")
Dialog.alert(
"Could not save pull request routing permission",
`Could not save ${terms.singular} routing permission`,
"Try again before leaving this screen.",
);
});
Expand All @@ -104,8 +111,8 @@ export function PullRequestRoutingSettings() {
})}
</SettingsSection>
<Text className="px-2 text-sm text-foreground-muted">
Choose environments you trust to share PR data and use each other's source control access.
Enable both environments. This applies only to this client.
Choose environments you trust to share {terms.shortLabel} data and use each other's source
control access. Enable both environments. This applies only to this client.
</Text>
</View>
);
Expand Down
15 changes: 12 additions & 3 deletions apps/mobile/src/features/keyboard/CommandPalette.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useChangeRequestTerms } from "../../state/change-request-terminology";
import { formatChangeRequestText } from "@t3tools/client-runtime/source-control-clients";
import { useAtomValue } from "@effect/atom-react";
import { uuidv4 } from "../../lib/uuid";
import { useNavigation } from "@react-navigation/native";
Expand Down Expand Up @@ -113,6 +115,7 @@ function PaletteRow(props: {
readonly searchQuery: string;
readonly onSelect: () => void;
}) {
const terms = useChangeRequestTerms();
const foregroundClassName = props.selected
? "text-thread-selected-foreground"
: "text-foreground";
Expand Down Expand Up @@ -190,7 +193,7 @@ function PaletteRow(props: {
/>
) : props.item.matchHint ? (
<Text numberOfLines={1} className={cn("text-sm", mutedForegroundClassName)}>
{props.item.matchHint.label}:{" "}
{formatChangeRequestText(props.item.matchHint.label, terms)}:{" "}
{splitSearchText(props.item.matchHint.text, props.item.matchHint.ranges).map((part) => (
<Text key={part.start} className={cn("text-sm", part.highlighted && "font-t3-bold")}>
{part.text}
Expand Down Expand Up @@ -220,6 +223,7 @@ export function CommandPalette(props: {
readonly onClose: () => void;
readonly onCommand: (command: HardwareKeyboardCommand) => void;
}) {
const terms = useChangeRequestTerms();
const navigation = useNavigation();
const selectTab = useSelectWorkspaceTab();
const { themeVariables } = useAppearancePreferences();
Expand Down Expand Up @@ -388,8 +392,12 @@ export function CommandPalette(props: {
const threadActions = [
["files", "Go to file", ["open", "files", "browse", "search"]],
["terminal", "Open terminal", ["shell", "console"]],
["review", "Review changes", ["diff", "git", "pull request"]],
["copyThreadReference", "Copy PR link or thread", ["reference", "clipboard"]],
["review", "Review changes", ["diff", "git", "pull request", "merge request"]],
[
"copyThreadReference",
`Copy ${terms.shortLabel} link or thread`,
["reference", "clipboard"],
],
] as const;
actions.push(
...threadActions.map(([command, title, searchTerms]) => ({
Expand Down Expand Up @@ -461,6 +469,7 @@ export function CommandPalette(props: {
);
return [...actions, ...projectItems, ...threadItems];
}, [
terms.shortLabel,
selectTab,
activeThread,
activeThreadRef,
Expand Down
6 changes: 4 additions & 2 deletions apps/mobile/src/features/layout/WorkspaceEmptyDetail.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useChangeRequestTerms } from "../../state/change-request-terminology";
import { SymbolView } from "../../components/ui/AppSymbol";
import { Platform, Pressable, View } from "react-native";

Expand All @@ -13,6 +14,7 @@ export function WorkspaceEmptyDetail(props: {
readonly onStartNewTask?: () => void;
readonly onAddConnection?: () => void;
}) {
const terms = useChangeRequestTerms();
const pullRequests = props.tab === "pullRequests";
const settings = props.tab === "settings";
if (props.tab === "usage") return <View className="flex-1 bg-screen" />;
Expand Down Expand Up @@ -54,14 +56,14 @@ export function WorkspaceEmptyDetail(props: {
{settings
? "Select a setting"
: pullRequests
? "Select a pull request"
? `Select a ${terms.singular}`
: "Select a thread"}
</Text>
<Text className="text-center text-base text-foreground-muted">
{settings
? "Choose a page from the sidebar."
: pullRequests
? "Choose a pull request from the sidebar to review it."
? `Choose a ${terms.singular} from the sidebar to review it.`
: Platform.OS === "android"
? "Choose a thread from the sidebar or start a new thread."
: "Choose a thread from the sidebar or start a new task."}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { selectedNavigationTab, visibleNavigationTabs } from "./navigation-tab-order";
import { requireNativeView } from "expo";
import { capitalizeTerm } from "@t3tools/client-runtime/source-control-clients";
import { CommonActions, useNavigation } from "@react-navigation/native";
import {
Keyboard,
Expand All @@ -15,6 +16,7 @@ import { useAndroidControlSizing } from "../../../components/ui/useAndroidContro
import { useToolbarLayout } from "../../../components/ui/useToolbarLayout";
import { rootNavigationRef } from "../../../lib/rootNavigation";
import { useWorkspaceConnectionState } from "../../../state/workspace";
import { useChangeRequestTerms } from "../../../state/change-request-terminology";
import { androidWorkspaceNavigationColors } from "./android-workspace-navigation-colors";
import {
androidWorkspaceTabNavigationState,
Expand All @@ -36,6 +38,8 @@ interface NativeNavigationProps extends ViewProps {
readonly floating: boolean;
readonly tabs: readonly string[];
readonly selectedTab: AndroidWorkspaceNavigationTab;
readonly pullRequestsTitle: string;
readonly pullRequestsExpandedTitle: string;
readonly dark: boolean;
readonly textScale: number;
readonly newEnabled: boolean;
Expand Down Expand Up @@ -73,6 +77,7 @@ export function AndroidWorkspaceNavigation(props: {
readonly onOverlayChange?: (overlay: boolean) => void;
}) {
const workspace = useWorkspaceTabs()!;
const terms = useChangeRequestTerms();
const order = useNavigationTabOrder();
const hidden = useHiddenNavigationTabs();
const root = useAndroidWorkspaceRoot();
Expand Down Expand Up @@ -121,6 +126,8 @@ export function AndroidWorkspaceNavigation(props: {
floating={floating}
tabs={tabs}
selectedTab={selectedTab}
pullRequestsTitle={terms.shortLabel === "MR" ? "MRs" : "Pulls"}
pullRequestsExpandedTitle={capitalizeTerm(terms.plural)}
dark={themeAppearance === "dark"}
textScale={scale}
newEnabled={connectionState.hasConnections}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useChangeRequestTerms } from "../../../state/change-request-terminology";
import { useNavigation } from "@react-navigation/native";
import { useEffect } from "react";
import { BackHandler, Keyboard } from "react-native";
Expand All @@ -14,6 +15,7 @@ export function PullRequestToolbar(props: {
readonly filters: PullRequestFilterMenu | null;
readonly onRequestVisibility?: () => void;
}) {
const terms = useChangeRequestTerms();
const navigation = useNavigation();
const { filters } = props;
const workspace = useWorkspaceTabs()!;
Expand Down Expand Up @@ -57,14 +59,14 @@ export function PullRequestToolbar(props: {
title="T3 Code"
titleContent={
<WorkspaceBrandTitle
accessibilityLabel="T3 Code, Pull requests"
accessibilityLabel={`T3 Code, ${terms.plural}`}
onOpenEnvironments={() => openSettings(navigation, "SettingsEnvironments")}
/>
}
search={{
value: tab.query,
onChangeText: (query) => dispatch({ type: "query", tab: "pullRequests", query }),
placeholder: "Search pull requests",
placeholder: `Search ${terms.plural}`,
active: searching,
onOpen: openSearch,
onClose: closeSearch,
Expand All @@ -73,7 +75,7 @@ export function PullRequestToolbar(props: {
filters
? [
{
title: "Filter pull requests",
title: `Filter ${terms.plural}`,
icon: filters.customized
? "line.3.horizontal.decrease.circle.fill"
: "line.3.horizontal.decrease.circle",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
WORKSPACE_TAB_LABELS,
useWorkspaceTabLabels,
NAVIGATION_TAB_ICONS,
useEnabledNavigationTabs,
} from "./navigation-tabs";
Expand Down Expand Up @@ -76,6 +76,7 @@ export function WorkspaceNativeTabs(props: {
/** The sidebar column draws the tabs itself, so they leave with it. */
readonly tabsInSidebarColumn?: boolean;
}) {
const tabLabels = useWorkspaceTabLabels();
const workspace = useWorkspaceTabs()!;
const order = useEnabledNavigationTabs();
const primaryTabs = order.slice(0, 4);
Expand Down Expand Up @@ -134,7 +135,7 @@ export function WorkspaceNativeTabs(props: {
key={slot}
screenKey={slot}
title=""
tabBarItemAccessibilityLabel={settings ? "Settings" : WORKSPACE_TAB_LABELS[tab]}
tabBarItemAccessibilityLabel={settings ? "Settings" : tabLabels[tab]}
ios={{
icon: settings
? { type: "sfSymbol", name: "gearshape" }
Expand Down
Loading
Loading