Skip to content
Merged
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: 0 additions & 2 deletions .github/workflows/android-build.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
name: Android Build

on:
push:
pull_request:
workflow_dispatch:

jobs:
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/desktop-build.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
name: Desktop Build

on:
push:
pull_request:
workflow_dispatch:

jobs:
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/ios-build.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
name: iOS Build

on:
push:
pull_request:
workflow_dispatch:

jobs:
Expand Down
88 changes: 88 additions & 0 deletions AGENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# AGENT.md

## 🚀 Setup Commands

### Web Application
The web application is a React PWA located in the `web/` directory.

```bash
cd web
pnpm install
# Start development server
pnpm dev
```

### Mobile & Desktop Application (Kotlin Multiplatform)
The native application is built with Kotlin Multiplatform and Compose Multiplatform.

**Android:**
```bash
./gradlew :composeApp:installDebug
```

**Desktop (JVM):**
```bash
./gradlew :composeApp:run
```

**iOS:**
Open `iosApp/iosApp.xcodeproj` in Xcode or run via Android Studio configuration.

---

## 🎨 Code Style

### Kotlin Multiplatform (KMP)
- **Package Name**: `dev.therealashik.client.jules`
- **UI Framework**: Compose Multiplatform (Material 3)
- **Shared Code**: Located in `composeApp/src/commonMain/kotlin`
- **Conventions**:
- Use `expect`/`actual` for platform-specific implementations.
- Follow standard Kotlin coding conventions.
- Use "Filled" or "Solid" style icons for visual weight.

### Web Application (React)
- **Framework**: React 19 + TypeScript + Vite
- **Styling**: Tailwind CSS
- **Guidelines**:
- Refer to `web/AGENT.md` for detailed "Premium UI" and accessibility guidelines.
- Use `pnpm` for package management.

---

## 🧪 Testing Instructions

### Web Application
```bash
cd web
# Run unit tests (Vitest)
pnpm test

# Run E2E/Visual tests (Playwright)
npx playwright test
```

### Kotlin Multiplatform
```bash
# Run all tests
./gradlew allTests

# Run specific module tests
./gradlew :composeApp:testDebugUnitTest
```

---

## 🏗️ Architecture

The repository is a monorepo containing:

- **`composeApp/`**: The core Kotlin Multiplatform shared module.
- `src/commonMain`: Shared UI and business logic for Android, iOS, and Desktop.
- `src/androidMain`, `src/iosMain`, `src/jvmMain`: Platform-specific implementations.
- **`web/`**: A standalone React Progressive Web App (PWA) designed to interact with the Jules Google AI coding agent.
- **`iosApp/`**: The iOS entry point project (Swift/SwiftUI) that consumes the shared KMP framework.

### Integration
- The projects share design principles (e.g., color palettes in `web/.Jules/palette.md` and `composeApp` theme) but currently operate as separate build artifacts.
- Both clients interact with the Jules Google AI API.
66 changes: 0 additions & 66 deletions build_log.txt

This file was deleted.

37 changes: 0 additions & 37 deletions build_log_2.txt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package dev.therealashik.client.jules.utils

import android.content.ContentResolver
import android.net.Uri
import android.provider.OpenableColumns
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class AndroidPlatformFile(
private val uri: Uri,
private val contentResolver: ContentResolver
) : PlatformFile {
override val name: String
get() {
var result: String? = null
if (uri.scheme == "content") {
val cursor = contentResolver.query(uri, null, null, null, null)
try {
if (cursor != null && cursor.moveToFirst()) {
val index = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
if (index >= 0) {
result = cursor.getString(index)
}
}
} catch (e: Exception) {
// Ignore
} finally {
cursor?.close()
}
}
if (result == null) {
result = uri.path
val cut = result?.lastIndexOf('/')
if (cut != null && cut != -1) {
result = result?.substring(cut + 1)
}
}
return result ?: "unknown"
}

override suspend fun readText(): String = withContext(Dispatchers.IO) {
contentResolver.openInputStream(uri)?.use { inputStream ->
inputStream.bufferedReader().use { it.readText() }
} ?: throw Exception("Cannot read file")
}
}

@Composable
actual fun rememberFilePickerLauncher(onFilePicked: (PlatformFile) -> Unit): FilePickerLauncher {
val context = LocalContext.current
val contentResolver = context.contentResolver
val launcher = rememberLauncherForActivityResult(contract = ActivityResultContracts.GetContent()) { uri: Uri? ->
if (uri != null) {
onFilePicked(AndroidPlatformFile(uri, contentResolver))
}
}
return remember {
FilePickerLauncher {
launcher.launch("*/*")
}
}
}
Loading