Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion packages/android/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<div
id="root"
class="flex flex-col h-dvh"
style="padding-top: env(safe-area-inset-top); padding-bottom: env(safe-area-inset-bottom)"
style="height: var(--android-viewport-height, 100dvh); padding-top: env(safe-area-inset-top); padding-bottom: env(safe-area-inset-bottom)"
></div>
<script src="/src/entry-android.tsx" type="module"></script>
</body>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
android:launchMode="singleTask"
android:label="@string/main_activity_title"
android:name=".MainActivity"
android:windowSoftInputMode="adjustResize"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand Down
12 changes: 12 additions & 0 deletions packages/android/src/entry-android.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,12 @@ const App = () => {
document.documentElement.dataset.platform = "android"
void refreshVoice()

const syncViewport = () => {
const height = window.visualViewport?.height ?? window.innerHeight
document.documentElement.style.setProperty("--android-viewport-height", `${height}px`)
}
syncViewport()

const handleClick = (event: MouseEvent) => {
const link = (event.target as HTMLElement | null)?.closest("a.external-link") as HTMLAnchorElement | null
if (!link?.href) return
Expand Down Expand Up @@ -291,10 +297,16 @@ const App = () => {

document.addEventListener("click", handleClick)
window.addEventListener("focus", onFocus)
window.addEventListener("resize", syncViewport)
window.visualViewport?.addEventListener("resize", syncViewport)
window.visualViewport?.addEventListener("scroll", syncViewport)
document.addEventListener("visibilitychange", onVisible)
onCleanup(() => {
document.removeEventListener("click", handleClick)
window.removeEventListener("focus", onFocus)
window.removeEventListener("resize", syncViewport)
window.visualViewport?.removeEventListener("resize", syncViewport)
window.visualViewport?.removeEventListener("scroll", syncViewport)
document.removeEventListener("visibilitychange", onVisible)
stopListening()
stopVoiceState()
Expand Down
18 changes: 17 additions & 1 deletion packages/app/src/components/prompt-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { Select } from "@opencode-ai/ui/select"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { ModelSelectorPopover } from "@/components/dialog-select-model"
import { useProviders } from "@/hooks/use-providers"
import { getSessionContextMetrics } from "@/components/session/session-context-metrics"
import { useCommand } from "@/context/command"
import { Persist, persisted } from "@/utils/persist"
import { usePermission } from "@/context/permission"
Expand Down Expand Up @@ -134,7 +135,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
let slashPopoverRef!: HTMLDivElement

const mirror = { input: false }
const inset = 56
const inset = 44
const space = `${inset}px`

const scrollCursorIntoView = () => {
Expand Down Expand Up @@ -1127,6 +1128,11 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
})

const variants = createMemo(() => ["default", ...local.model.variant.list()])
const sessionMessages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : []))
const contextTokens = createMemo(
() => getSessionContextMetrics(sessionMessages(), providers.all()).context?.total ?? 0,
)
const contextTokenLabel = createMemo(() => contextTokens().toLocaleString(language.intl()))
const accepting = createMemo(() => {
const id = params.id
if (!id) return permission.isAutoAcceptingDirectory(sdk.directory)
Expand Down Expand Up @@ -1745,6 +1751,16 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
</TooltipKeybind>
</div>
</Show>
<Show when={params.id}>
<div
data-component="prompt-context-tokens"
class="shrink-0 whitespace-nowrap text-13-regular text-text-weak"
>
<span>Context: </span>
<span class="text-text-base">{contextTokenLabel()}</span>
<span>t</span>
</div>
</Show>
</Show>
</Show>
</div>
Expand Down
42 changes: 13 additions & 29 deletions packages/app/src/pages/session.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import { selectionFromLines, useFile, type FileSelection, type SelectedLineRange
import { createStore } from "solid-js/store"
import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
import { Select } from "@opencode-ai/ui/select"
import { Tabs } from "@opencode-ai/ui/tabs"
import { createAutoScroll } from "@opencode-ai/ui/hooks"
import { previewSelectedLines } from "@opencode-ai/ui/pierre/selection-bridge"
import { Button } from "@opencode-ai/ui/button"
Expand Down Expand Up @@ -1961,34 +1960,6 @@ export default function Page() {
{sessionSync() ?? ""}
<SessionHeader />
<div class="flex-1 min-h-0 flex flex-col md:flex-row">
<Show when={!isDesktop() && !!params.id}>
{/* UPSTREAM-DIVERGENCE: Mobile uses a compact chat switcher so the primary flow keeps more vertical space. */}
<Tabs value={store.mobileTab} class="h-auto">
<Tabs.List class="!h-9 !px-2 !py-1 !bg-background-stronger">
<Tabs.Trigger
value="session"
class="!w-1/2 !max-w-none !h-full text-13-medium"
classes={{ button: "w-full !h-full !px-2 !py-0" }}
onClick={() => setStore("mobileTab", "session")}
>
{language.t("session.tab.session")}
</Tabs.Trigger>
<Tabs.Trigger
value="changes"
class="!w-1/2 !max-w-none !h-full !border-r-0 text-13-medium"
classes={{ button: "w-full !h-full !px-2 !py-0" }}
onClick={() => setStore("mobileTab", "changes")}
>
{hasReview()
? `${reviewCount()} ${language.t(
reviewCount() === 1 ? "session.review.change.one" : "session.review.change.other",
)}`
: language.t("session.review.change.other")}
</Tabs.Trigger>
</Tabs.List>
</Tabs>
</Show>

{/* Session panel */}
<div
classList={{
Expand All @@ -2006,6 +1977,19 @@ export default function Page() {
<Show when={messagesReady()}>
<MessageTimeline
mobileChanges={mobileChanges()}
mobileTabs={
!isDesktop() && params.id
? {
value: store.mobileTab,
changesLabel: hasReview()
? `${reviewCount()} ${language.t(
reviewCount() === 1 ? "session.review.change.one" : "session.review.change.other",
)}`
: language.t("session.review.change.other"),
onChange: (value) => setStore("mobileTab", value),
}
: undefined
}
mobileFallback={reviewContent({
diffStyle: "unified",
classes: {
Expand Down
Loading
Loading