diff --git a/plans/clickablebox3.md b/plans/clickablebox3.md
index 04ee699989ff..0fcef11e62f4 100644
--- a/plans/clickablebox3.md
+++ b/plans/clickablebox3.md
@@ -24,7 +24,7 @@
## ✅ Done: ClickableBox3 implemented and devices/ migrated
Committed in `3ac6c14b82`. Key points for future reference:
-- `ClickableBox3Props = Box2Props & {onClick?, onLongPress?, hitSlop?}` — `direction` required
+- `ClickableBox3Props = Box2Props & {onClick?, onLongPress?, hitSlop?}` — `direction` required; desktop mouse events (`onMouseDown/Up/Leave/Move/Over/Enter`, `onContextMenu`) are in `Box2Props` and passed through to the `
`
- `box2ClassNames()` extracted from Box2 and shared; `box2SharedProps` exported from `box.tsx`
- `devices/` pilot complete: 3 CB2 usages → CB3, inner Box2 wrappers eliminated, `mobileAddHeader` style simplified
@@ -38,19 +38,19 @@ Use `migrate-clickable-box` skill for each chunk. Run `yarn lint && yarn tsc` an
- [x] `shared/devices/` (6 total)
### Round 1 — small
-- [ ] `shared/git/` (3)
-- [ ] `shared/incoming-share/` (2)
-- [ ] `shared/signup/` (2)
-- [ ] `shared/provision/` (4)
-- [ ] `shared/people/` (2)
-- [ ] `shared/settings/` (4)
-- [ ] `shared/profile/` (10)
+- [x] `shared/git/` (3)
+- [x] `shared/incoming-share/` (2)
+- [x] `shared/signup/` (2)
+- [x] `shared/provision/` (4)
+- [x] `shared/people/` (2)
+- [x] `shared/settings/` (4)
+- [x] `shared/profile/` (10)
### Round 2 — medium
-- [ ] `shared/tracker/` (4)
-- [ ] `shared/menubar/` (3)
-- [ ] `shared/app/` (4)
-- [ ] `shared/router-v2/` (9)
+- [x] `shared/tracker/` (4)
+- [x] `shared/menubar/` (3)
+- [x] `shared/app/` (4)
+- [x] `shared/router-v2/` (9)
- [ ] `shared/teams/` (25+)
- [ ] `shared/team-building/` (9+)
diff --git a/shared/app/global-errors.tsx b/shared/app/global-errors.tsx
index d5cfc7e672ae..d1257d310d98 100644
--- a/shared/app/global-errors.tsx
+++ b/shared/app/global-errors.tsx
@@ -213,7 +213,7 @@ const GlobalError = () => {
}
return (
-
+
{summary}
@@ -239,7 +239,7 @@ const GlobalError = () => {
{details}
-
+
)
}
diff --git a/shared/app/runtime-stats.tsx b/shared/app/runtime-stats.tsx
index bad8651248a6..4214b75d8e5c 100644
--- a/shared/app/runtime-stats.tsx
+++ b/shared/app/runtime-stats.tsx
@@ -226,8 +226,7 @@ const RuntimeStatsDesktop = ({stats}: Props) => {
return (
<>
- setMoreLogs(m => !m)}>
-
+ setMoreLogs(m => !m)} direction="vertical" style={styles.container} gap="xxtiny" fullWidth={true}>
{!moreLogs &&
stats.processStats?.map((stat, i) => {
return (
@@ -303,8 +302,7 @@ const RuntimeStatsDesktop = ({stats}: Props) => {
)*/}
-
-
+
>
)
@@ -333,9 +331,9 @@ const RuntimeStatsMobile = ({stats}: Props) => {
style={showLogs ? styles.modalLogStats : styles.modalLogStatsHidden}
gap="xtiny"
>
- setShowLogs(s => !s)}>
+ setShowLogs(s => !s)} direction="vertical">
-
+
{processStat && (
diff --git a/shared/common-adapters/box.tsx b/shared/common-adapters/box.tsx
index fc9171f51534..d377163dc69a 100644
--- a/shared/common-adapters/box.tsx
+++ b/shared/common-adapters/box.tsx
@@ -24,6 +24,7 @@ export type Box2Props = {
onDrop?: (syntheticDragEvent: React.DragEvent) => void
onLayout?: (evt: LayoutEvent) => void
onMouseDown?: (syntheticEvent: React.MouseEvent) => void
+ onMouseEnter?: (syntheticEvent: React.MouseEvent) => void
onMouseMove?: (syntheticEvent: React.MouseEvent) => void
onMouseLeave?: (syntheticEvent: React.MouseEvent) => void
onMouseUp?: (syntheticEvent: React.MouseEvent) => void
@@ -318,7 +319,7 @@ export const ClickableBox3 = (p: ClickableBox3Props & {ref?: React.Ref R.remoteDispatch(RemoteGen.createOpenChatFromWidget({conversationIDKey: conv.conversationIDKey}))}
- style={styles.chatRow}
+ direction="horizontal"
+ fullWidth={true}
+ alignItems="center"
+ gap="tiny"
+ style={styles.chatRowInner}
>
-
-
-
-
-
-
- {isTeam && conv.channelname ? `${name}#${conv.channelname}` : name}
-
- {conv.hasBadge && }
-
- {!!timestamp && (
-
- {timestamp}
-
- )}
+
+
+
+
+
+ {isTeam && conv.channelname ? `${name}#${conv.channelname}` : name}
+
+ {conv.hasBadge && }
- {!!conv.snippetDecorated && (
+ {!!timestamp && (
- {conv.snippetDecorated}
+ {timestamp}
)}
+ {!!conv.snippetDecorated && (
+
+ {conv.snippetDecorated}
+
+ )}
-
+
)
}
@@ -191,17 +193,22 @@ const ChatPreview = (p: {conversationsToSend: ReadonlyArray; convL
// Inline file updates (replaces FilesContainer + files.desktop.tsx with store-connected components)
const FileUpdate = (p: {path: T.FS.Path; uploading: boolean; onClick: () => void}) => (
-
-
-
- {p.uploading && (
-
-
-
- )}
-
-
-
+
+
+ {p.uploading && (
+
+
+
+ )}
+
+
)
const defaultNumFileOptionsShown = 3
@@ -642,11 +649,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({
backgroundColor: Kb.Styles.globalColors.white,
color: Kb.Styles.globalColors.black,
},
- chatRow: Kb.Styles.platformStyles({
- isElectron: {
- ...Kb.Styles.desktopStyles.clickable,
- },
- }),
chatRowInner: Kb.Styles.padding(Kb.Styles.globalMargins.xtiny, Kb.Styles.globalMargins.xsmall),
chatRowName: {flexShrink: 1},
@@ -655,7 +657,6 @@ const styles = Kb.Styles.styleSheetCreate(() => ({
chatSnippet: {color: Kb.Styles.globalColors.black_50},
chatSnippetUnread: {color: Kb.Styles.globalColors.black},
chatTimestamp: {color: Kb.Styles.globalColors.black_50, flexShrink: 0, marginLeft: Kb.Styles.globalMargins.tiny},
- fileFullWidth: {width: '100%'},
fileIcon: {
flexShrink: 0,
...Kb.Styles.size(16),
diff --git a/shared/profile/add-to-team.tsx b/shared/profile/add-to-team.tsx
index 0448b9516d1c..207d5d2888a0 100644
--- a/shared/profile/add-to-team.tsx
+++ b/shared/profile/add-to-team.tsx
@@ -341,7 +341,7 @@ type RowProps = {
const TeamRow = (props: RowProps) => {
return (
- props.onCheck(!props.checked) : undefined}>
+ props.onCheck(!props.checked) : undefined}>
@@ -372,7 +372,7 @@ const TeamRow = (props: RowProps) => {
{!isMobile && }
-
+
)
}
diff --git a/shared/profile/edit-avatar/index.tsx b/shared/profile/edit-avatar/index.tsx
index 71c6f733bb02..47e7f1b99850 100644
--- a/shared/profile/edit-avatar/index.tsx
+++ b/shared/profile/edit-avatar/index.tsx
@@ -159,7 +159,8 @@ const DesktopEditAvatar = (_p: Props) => {
{' '}
for one.
- {
type="iconfont-camera"
/>
)}
-
+
{loading === 'loaded' ? Click to select. Scroll to zoom. : null}
@@ -311,12 +312,14 @@ const NativeAvatarUploadWrapper = (p: Props) => {
const renderImageZoomer = () => {
if (type === 'team' && !selectedImage) {
return (
-
-
+
)
}
return selectedImage ? (
diff --git a/shared/profile/generic/proofs-list.tsx b/shared/profile/generic/proofs-list.tsx
index 3a4986f101a8..4ca32f5db9c5 100644
--- a/shared/profile/generic/proofs-list.tsx
+++ b/shared/profile/generic/proofs-list.tsx
@@ -620,7 +620,10 @@ const ProviderPicker = ({
renderItem={(_: unknown, provider: Provider) => (
- onSelect(provider.key)}
style={styles.containerBox}
@@ -647,7 +650,7 @@ const ProviderPicker = ({
style={styles.iconArrow}
type="iconfont-arrow-right"
/>
-
+
)}
/>
@@ -1314,11 +1317,7 @@ const styles = Kb.Styles.styleSheetCreate(
},
}),
containerBox: {
- alignItems: 'center',
- display: 'flex',
- flexDirection: 'row',
height: isMobile ? 56 : 48,
- justifyContent: 'flex-start',
},
description: {...rightColumnStyle},
error: {
diff --git a/shared/profile/user/actions/index.tsx b/shared/profile/user/actions/index.tsx
index 22c7c9af3c6b..b7b37964f035 100644
--- a/shared/profile/user/actions/index.tsx
+++ b/shared/profile/user/actions/index.tsx
@@ -208,14 +208,12 @@ const DropdownButton = (p: DropdownProps) => {
const {showPopup, popup, popupAnchor} = Kb.usePopup2(makePopup)
return (
-
-
-
-
-
-
+
+
+
+
{popup}
-
+
)
}
diff --git a/shared/profile/user/friend.tsx b/shared/profile/user/friend.tsx
index d4366fc0cd78..72844c74b2cf 100644
--- a/shared/profile/user/friend.tsx
+++ b/shared/profile/user/friend.tsx
@@ -25,12 +25,12 @@ const Container = (ownProps: OwnProps) => {
: followsYou ? ('icon-follow-me-21' as const) : ('icon-following-21' as const)
return (
-
-
+
{!!followIconType && }
@@ -45,8 +45,7 @@ const Container = (ownProps: OwnProps) => {
{fullname}
-
-
+
)
}
diff --git a/shared/profile/user/index.tsx b/shared/profile/user/index.tsx
index d70bc05c2d7a..8c0914460892 100644
--- a/shared/profile/user/index.tsx
+++ b/shared/profile/user/index.tsx
@@ -227,24 +227,24 @@ const Tabs = (p: TabsProps) => {
const onClickFollowing = () => p.onSelectTab('following')
const onClickFollowers = () => p.onSelectTab('followers')
const tab = (tab: Tab) => (
-
-
-
- {tab === 'following'
- ? `Following${!p.loadingFollowing ? ` (${p.numFollowing || 0})` : ''}`
- : `Followers${!p.loadingFollowers ? ` (${p.numFollowers || 0})` : ''}`}
-
- {((tab === 'following' && p.loadingFollowing) || p.loadingFollowers) && (
-
- )}
-
-
+
+ {tab === 'following'
+ ? `Following${!p.loadingFollowing ? ` (${p.numFollowing || 0})` : ''}`
+ : `Followers${!p.loadingFollowers ? ` (${p.numFollowers || 0})` : ''}`}
+
+ {((tab === 'following' && p.loadingFollowing) || p.loadingFollowers) && (
+
+ )}
+
)
return (
diff --git a/shared/profile/user/teams/index.tsx b/shared/profile/user/teams/index.tsx
index bc5b8bdb9a05..46ad3d7ffc7d 100644
--- a/shared/profile/user/teams/index.tsx
+++ b/shared/profile/user/teams/index.tsx
@@ -73,14 +73,12 @@ const TeamShowcase = (props: TeamShowcaseProps) => {
const ShowcaseTeamsOffer = (p: {onEdit: () => void}) => (
-
-
-
-
- {"Feature the teams you're in"}
-
-
-
+
+
+
+ {"Feature the teams you're in"}
+
+
)
diff --git a/shared/profile/user/teams/team-row.tsx b/shared/profile/user/teams/team-row.tsx
index 4751ff7217a1..4f6a1152cdb6 100644
--- a/shared/profile/user/teams/team-row.tsx
+++ b/shared/profile/user/teams/team-row.tsx
@@ -12,19 +12,17 @@ type Props = {
}
const TeamRow = ({isOpen, loading = false, name, onClick, popup, popupAnchor}: Props) => (
-
-
- <>
- {popup}
-
- >
-
- {name}
-
- {typeof isOpen === 'boolean' && }
- {loading && }
-
-
+
+ <>
+ {popup}
+
+ >
+
+ {name}
+
+ {typeof isOpen === 'boolean' && }
+ {loading && }
+
)
const styles = Kb.Styles.styleSheetCreate(() => ({
diff --git a/shared/provision/code-page/qr-scan/scanner.tsx b/shared/provision/code-page/qr-scan/scanner.tsx
index 55a2ad9d524a..8de1aee64108 100644
--- a/shared/provision/code-page/qr-scan/scanner.tsx
+++ b/shared/provision/code-page/qr-scan/scanner.tsx
@@ -9,6 +9,7 @@ type Props = {
const QRScannerMobile = (p: Props): React.ReactElement | null => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
+ // eslint-disable-next-line @typescript-eslint/consistent-type-imports
const {CameraView, useCameraPermissions} = require('expo-camera') as typeof import('expo-camera')
const [scanned, setScanned] = React.useState(false)
const [permission, requestPermission] = useCameraPermissions()
diff --git a/shared/router-v2/header/index.desktop.tsx b/shared/router-v2/header/index.desktop.tsx
index 6275f6242fa6..8083ba381b8c 100644
--- a/shared/router-v2/header/index.desktop.tsx
+++ b/shared/router-v2/header/index.desktop.tsx
@@ -79,17 +79,19 @@ const SystemButtons = ({isMaximized}: {isMaximized: boolean}) => {
}
return (
-
-
-
+
{
style={styles.appIcon}
type={isMaximized ? 'iconfont-app-un-maximize' : 'iconfont-app-maximize'}
/>
-
-
+
-
+
)
}
@@ -192,21 +195,20 @@ function DesktopHeader(p: Props) {
>
{/* TODO have headerLeft be the back button */}
{headerLeft !== null && (
-
-
-
-
-
+
+
)}
{
const menuHeader = (
-
+
{
}
/>
-
+
{
return (
<>
-
-
-
+
+ <>
+
+ Hi {username}!
+
+
- <>
-
- Hi {username}!
-
-
- >
-
-
+ >
+
{popup}
>
)
@@ -267,37 +266,33 @@ function Tab(props: TabProps) {
}
return (
-
-
-
-
-
- {tab === Tabs.fsTab && }
-
-
- {label}
-
-
+
+
+
+ {tab === Tabs.fsTab && }
-
+
+ {label}
+
+
+
)
}
diff --git a/shared/tracker/assertion.tsx b/shared/tracker/assertion.tsx
index af8ed8898903..dd09f151610e 100644
--- a/shared/tracker/assertion.tsx
+++ b/shared/tracker/assertion.tsx
@@ -222,24 +222,29 @@ const Container = (ownProps: OwnProps) => {
)}
-
-
-
- {items ? (
- <>
-
- {popup}
- >
- ) : (
-
- )}
-
-
+
+
+ {items ? (
+ <>
+
+ {popup}
+ >
+ ) : (
+
+ )}
+
{!!metas.length && (
@@ -482,9 +487,9 @@ const AssertionSiteIcon = (p: SIProps) => {
child = {child}
}
return (
-
+
{child}
-
+
)
}
diff --git a/skill/migrate-clickable-box/SKILL.md b/skill/migrate-clickable-box/SKILL.md
index f473702a08ce..3a340d9ad9e0 100644
--- a/skill/migrate-clickable-box/SKILL.md
+++ b/skill/migrate-clickable-box/SKILL.md
@@ -11,7 +11,9 @@ See `plans/clickablebox3.md` for the full migration plan, directory checklist, a
`ClickableBox3` = `ClickableBox2` + all `Box2` layout props (direction optional). On desktop it renders a `` with the Box2 CSS class system plus `clickable-box2` cursor. On mobile it uses `Pressable` + `box2SharedProps` for layout.
-Type: `Box2Props & {onClick?, onLongPress?, onMouseOver?, hitSlop?}` — **`direction` is required.**
+Type: `Box2Props & {onClick?, onLongPress?, hitSlop?}` — **`direction` is required.**
+
+`Box2Props` includes desktop mouse events: `onMouseDown`, `onMouseUp`, `onMouseLeave`, `onMouseMove`, `onMouseOver`, `onMouseEnter`, `onContextMenu`. CB3 passes all of these through to the desktop `
`. They are **not** forwarded on mobile (Pressable doesn't support them).
`direction` is always required. For Pattern B swaps (plain clickable wrapper, no layout needed), pass the direction that matches how children are stacked — usually `"vertical"` for a single child or vertically-stacked children.
@@ -110,11 +112,12 @@ These CB1 props have no CB3 equivalent:
- `hoverColor`, `underlayColor` → add `hover_background_color_*` CSS className to CB3 instead
- `feedback={false}` → drop (Pressable doesn't have this)
- `activeOpacity` → drop
-- `onMouseEnter` / `onMouseLeave` → rare; use a wrapper div if truly needed
- `onPressIn` / `onPressOut` → not in CB3; leave as CB1 and note it
- `tooltip` → wrap with `` outside CB3
- `onLongPress={(e) => ...}` → remove the `e` param (CB3 signature is `() => void`)
+Note: `onMouseDown`, `onMouseUp`, `onMouseLeave`, `onMouseMove`, `onMouseOver`, `onMouseEnter` are **fully supported** in CB3 via `Box2Props` — these are NOT Pattern D cases.
+
## Step 3: Present Changes Before Touching Anything
For each usage show: file, line, pattern (A/B/C/D), proposed change.
@@ -133,7 +136,7 @@ From `shared/`:
```
yarn lint && yarn tsc
```
-Fix errors before reporting done.
+**Both must pass with zero errors before reporting done.** Fix any failures — including lint errors in files we touched or that were broken in a prior session. Do not skip lint or treat failures as pre-existing without verifying via `git stash`.
## Step 6: Update Checklist