mirror of
https://github.com/stablyai/orca.git
synced 2026-11-06 12:07:58 +00:00
* feat(mobile): show dictated words as you speak and insert them at the cursor Newer desktops already send the text so far with each audio chunk reply; the phone now shows it: grey in the chat composer or command field where it will land, and on the live-input status row. The final transcript replaces the selection the field had when recording started, and is appended when the field was never focused or changed since. * refactor(mobile): pass the dictation preview by context and split its hooks out The preview reaches composers through a context instead of four layers of props, and the composer's and session's dictation logic move into their own hooks, which keeps each file under its line cap. * fix(mobile): keep dictation selections and live words to their own terminal * test(mobile): keep the composer dictation test typechecked and under the line cap * fix(mobile): keep the chat field controlled while dictation previews over it The native composer field swapped its value for rich-text children while showing heard words, so Android could duplicate or drop typed characters and selection offsets could drift from the draft. Draw the preview as a hidden- from-accessibility overlay shared with the web field, and only hide the field's own text. * refactor(mobile): build the dictation route context beside its destination Keeps the session dictation hook under the line cap after rebasing onto main. * fix(mobile): land dictation where its preview showed after switching chat or terminal The final words now go to the target captured at start: the chat it began in (its own draft when another chat is showing) rather than whichever surface is visible. The command field preview only uses the saved selection in the terminal it was read from, so it shows the words appended in another terminal, as they will land. * fix(mobile): send terminal dictation to the chat that replaced its terminal Routing only by the captured target dropped the words when a chat tab replaced the terminal mid-dictation, since no terminal draft was left to take them.
246 lines
5.6 KiB
TypeScript
246 lines
5.6 KiB
TypeScript
import { StyleSheet } from 'react-native'
|
|
|
|
import { colors, spacing, radii, typography } from '../theme/mobile-theme'
|
|
import { TEXT_INPUT_FONT_SIZE } from '../platform/text-input-font-size'
|
|
import { themedStyleSheet } from '../theme/themed-styles'
|
|
|
|
export const mobileSessionCommandInputStyles = themedStyleSheet(() => ({
|
|
createWarningBanner: {
|
|
flexDirection: 'row',
|
|
alignItems: 'flex-start',
|
|
gap: spacing.sm,
|
|
backgroundColor: colors.bgPanel,
|
|
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
borderBottomColor: colors.borderSubtle,
|
|
paddingHorizontal: spacing.md,
|
|
paddingVertical: spacing.sm
|
|
},
|
|
createWarningText: {
|
|
flex: 1,
|
|
color: colors.textPrimary,
|
|
fontSize: 12,
|
|
lineHeight: 16
|
|
},
|
|
createWarningDismiss: {
|
|
width: 24,
|
|
height: 24,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
marginTop: -4
|
|
},
|
|
emptyState: {
|
|
flex: 1,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
padding: spacing.xl
|
|
},
|
|
emptyText: {
|
|
color: colors.textSecondary,
|
|
fontSize: typography.bodySize,
|
|
marginBottom: spacing.lg
|
|
},
|
|
createError: {
|
|
color: colors.statusRed,
|
|
fontSize: 13,
|
|
marginBottom: spacing.sm
|
|
},
|
|
emptyActions: {
|
|
flexDirection: 'row',
|
|
flexWrap: 'wrap',
|
|
justifyContent: 'center',
|
|
gap: spacing.sm
|
|
},
|
|
createButton: {
|
|
backgroundColor: colors.bgRaised,
|
|
borderWidth: 1,
|
|
borderColor: colors.borderSubtle,
|
|
paddingHorizontal: spacing.xl,
|
|
paddingVertical: spacing.sm + 2,
|
|
borderRadius: radii.button
|
|
},
|
|
createButtonDisabled: {
|
|
opacity: 0.5
|
|
},
|
|
createButtonText: {
|
|
color: colors.textPrimary,
|
|
fontSize: typography.bodySize,
|
|
fontWeight: '600'
|
|
},
|
|
commandDock: {
|
|
zIndex: 20,
|
|
backgroundColor: colors.bgPanel
|
|
},
|
|
accessoryBar: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
borderTopWidth: 1,
|
|
borderTopColor: colors.borderSubtle,
|
|
backgroundColor: colors.bgPanel
|
|
},
|
|
accessoryScroll: {
|
|
flex: 1,
|
|
minWidth: 0
|
|
},
|
|
accessoryContent: {
|
|
paddingHorizontal: spacing.sm,
|
|
paddingVertical: spacing.xs,
|
|
gap: spacing.xs
|
|
},
|
|
accessoryKey: {
|
|
backgroundColor: colors.bgRaised,
|
|
paddingHorizontal: spacing.sm + 2,
|
|
paddingVertical: spacing.xs,
|
|
borderRadius: radii.button,
|
|
minWidth: 36,
|
|
alignItems: 'center'
|
|
},
|
|
accessoryKeyPressed: {
|
|
backgroundColor: colors.borderSubtle
|
|
},
|
|
accessoryKeyActive: {
|
|
backgroundColor: colors.textPrimary
|
|
},
|
|
customAccessoryKey: {
|
|
borderWidth: 1,
|
|
borderColor: colors.borderSubtle
|
|
},
|
|
accessoryKeyDisabled: {
|
|
opacity: 0.35
|
|
},
|
|
accessoryKeyText: {
|
|
color: colors.textSecondary,
|
|
fontSize: 12,
|
|
fontFamily: typography.monoFamily
|
|
},
|
|
accessoryKeyTextActive: {
|
|
color: colors.bgBase,
|
|
fontWeight: '700'
|
|
},
|
|
accessoryKeyTextDisabled: {
|
|
color: colors.textMuted
|
|
},
|
|
keyboardDismissKey: {
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
alignSelf: 'stretch',
|
|
marginLeft: spacing.sm,
|
|
marginVertical: spacing.xs,
|
|
backgroundColor: colors.bgRaised,
|
|
paddingHorizontal: spacing.sm + 2,
|
|
paddingVertical: 0,
|
|
borderRadius: radii.button,
|
|
minWidth: 36
|
|
},
|
|
// The Duo side rail column: full-width keys with room for a thumb.
|
|
accessoryRail: {
|
|
flex: 1,
|
|
minHeight: 0
|
|
},
|
|
accessoryScrollVertical: {
|
|
flex: 1
|
|
},
|
|
// Keys span the 84pt rail less 4pt each side, so the widest label (Shift+Tab) fits.
|
|
accessoryContentVertical: {
|
|
paddingHorizontal: spacing.xs,
|
|
paddingVertical: spacing.xs,
|
|
gap: spacing.sm
|
|
},
|
|
accessoryKeyVertical: {
|
|
minHeight: 44,
|
|
justifyContent: 'center',
|
|
paddingHorizontal: 2,
|
|
borderRadius: radii.composerItem
|
|
},
|
|
keyboardDismissKeyVertical: {
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
marginHorizontal: spacing.xs,
|
|
marginBottom: spacing.sm,
|
|
minHeight: 44,
|
|
backgroundColor: colors.bgRaised,
|
|
borderRadius: radii.composerItem
|
|
},
|
|
keyboardDismissGlyph: {
|
|
alignItems: 'center',
|
|
height: 18,
|
|
justifyContent: 'flex-start',
|
|
position: 'relative',
|
|
width: 18
|
|
},
|
|
keyboardDismissChevron: {
|
|
bottom: -2,
|
|
position: 'absolute'
|
|
},
|
|
inputBar: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
minHeight: 46,
|
|
paddingVertical: spacing.xs + 2,
|
|
paddingHorizontal: spacing.md,
|
|
borderTopWidth: 1,
|
|
borderTopColor: colors.borderSubtle,
|
|
backgroundColor: colors.bgPanel
|
|
},
|
|
textInput: {
|
|
flex: 1,
|
|
height: 34,
|
|
backgroundColor: colors.bgRaised,
|
|
color: colors.textPrimary,
|
|
borderRadius: radii.input,
|
|
paddingHorizontal: spacing.md,
|
|
paddingVertical: 0,
|
|
fontSize: TEXT_INPUT_FONT_SIZE,
|
|
fontFamily: typography.monoFamily,
|
|
marginRight: spacing.sm
|
|
},
|
|
commandField: {
|
|
flex: 1,
|
|
flexDirection: 'row',
|
|
marginRight: spacing.sm
|
|
},
|
|
commandFieldInput: {
|
|
marginRight: 0
|
|
},
|
|
// Dictation's preview draws the draft instead while it is hidden.
|
|
commandFieldInputHidden: {
|
|
color: 'transparent'
|
|
},
|
|
liveInputField: {
|
|
flex: 1,
|
|
marginRight: spacing.sm
|
|
},
|
|
liveInputCapture: {
|
|
minWidth: 0,
|
|
marginRight: 0
|
|
},
|
|
liveInputCaptureDisabled: {
|
|
opacity: 0.45
|
|
},
|
|
sendButton: {
|
|
backgroundColor: colors.bgRaised,
|
|
width: 34,
|
|
height: 34,
|
|
borderRadius: 17,
|
|
alignItems: 'center',
|
|
justifyContent: 'center'
|
|
},
|
|
dictationButton: {
|
|
backgroundColor: colors.bgRaised,
|
|
width: 34,
|
|
height: 34,
|
|
borderRadius: 17,
|
|
borderWidth: 1,
|
|
borderColor: 'transparent',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
marginRight: spacing.sm
|
|
},
|
|
dictationButtonActive: {
|
|
backgroundColor: colors.bgPanel,
|
|
borderColor: colors.textSecondary
|
|
},
|
|
sendButtonDisabled: {
|
|
opacity: 0.35
|
|
}
|
|
}))
|