diff --git a/Readme.md b/README.md similarity index 99% rename from Readme.md rename to README.md index e455ec3..769831e 100644 --- a/Readme.md +++ b/README.md @@ -301,6 +301,4 @@ ## **Features for Sprint 5 v1.1.0** * Fungsi AI (Upload File) (ok) -* Fitur Sematkan Category, otomatis paling atas (ok) - -## **Unit Testing is Coming** \ No newline at end of file +* Fitur Sematkan Category, otomatis paling atas (ok) \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index adb03e4..6e763f7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -101,9 +101,6 @@ dependencies { // PDF Parser implementation("com.tom-roush:pdfbox-android:2.0.27.0") - // FIREBASE SUDAH DIHAPUS - baris ini yang menyebabkan error - // implementation(libs.firebase.firestore.ktx) - // Testing testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") diff --git a/app/src/main/java/com/example/notesai/MainActivity.kt b/app/src/main/java/com/example/notesai/MainActivity.kt index a920e2f..2db6961 100644 --- a/app/src/main/java/com/example/notesai/MainActivity.kt +++ b/app/src/main/java/com/example/notesai/MainActivity.kt @@ -40,52 +40,95 @@ class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { - MaterialTheme( - colorScheme = darkColorScheme( - primary = AppColors.Primary, - onPrimary = Color.White, - primaryContainer = AppColors.Primary.copy(alpha = 0.3f), - onPrimaryContainer = Color.White, - secondary = AppColors.Secondary, - onSecondary = Color.White, - secondaryContainer = AppColors.Secondary.copy(alpha = 0.3f), - onSecondaryContainer = Color.White, - background = AppColors.Background, - onBackground = AppColors.OnBackground, - surface = AppColors.Surface, - onSurface = AppColors.OnSurface, - surfaceVariant = AppColors.SurfaceVariant, - onSurfaceVariant = AppColors.OnSurfaceVariant, - error = AppColors.Error, - onError = Color.White, - outline = AppColors.Border, - outlineVariant = AppColors.Divider - ), - typography = Typography( - displayLarge = MaterialTheme.typography.displayLarge.copy( - fontWeight = FontWeight.Bold - ), - headlineLarge = MaterialTheme.typography.headlineLarge.copy( - fontWeight = FontWeight.Bold - ), - titleLarge = MaterialTheme.typography.titleLarge.copy( - fontWeight = FontWeight.SemiBold - ), - bodyLarge = MaterialTheme.typography.bodyLarge.copy( - lineHeight = 24.sp - ), - bodyMedium = MaterialTheme.typography.bodyMedium.copy( - lineHeight = 20.sp - ) - ) - ) { - Surface( - modifier = Modifier.fillMaxSize(), - color = MaterialTheme.colorScheme.background - ) { - NotesApp() - } - } + NotesAppTheme() + } + } +} + +@Composable +fun NotesAppTheme(content: @Composable () -> Unit = { NotesApp() }) { + val context = LocalContext.current + val dataStoreManager = remember { DataStoreManager(context) } + var isDarkTheme by remember { mutableStateOf(true) } + + // Load theme preference + LaunchedEffect(Unit) { + dataStoreManager.themeFlow.collect { theme -> + isDarkTheme = theme == "dark" + AppColors.setTheme(isDarkTheme) + } + } + + // Create dynamic color scheme based on theme + val colorScheme = if (isDarkTheme) { + darkColorScheme( + primary = AppColors.Primary, + onPrimary = Color.White, + primaryContainer = AppColors.Primary.copy(alpha = 0.3f), + onPrimaryContainer = Color.White, + secondary = AppColors.Secondary, + onSecondary = Color.White, + secondaryContainer = AppColors.Secondary.copy(alpha = 0.3f), + onSecondaryContainer = Color.White, + background = AppColors.Background, + onBackground = AppColors.OnBackground, + surface = AppColors.Surface, + onSurface = AppColors.OnSurface, + surfaceVariant = AppColors.SurfaceVariant, + onSurfaceVariant = AppColors.OnSurfaceVariant, + error = AppColors.Error, + onError = Color.White, + outline = AppColors.Border, + outlineVariant = AppColors.Divider + ) + } else { + lightColorScheme( + primary = AppColors.Primary, + onPrimary = Color.White, + primaryContainer = AppColors.Primary.copy(alpha = 0.1f), + onPrimaryContainer = AppColors.Primary, + secondary = AppColors.Secondary, + onSecondary = Color.White, + secondaryContainer = AppColors.Secondary.copy(alpha = 0.1f), + onSecondaryContainer = AppColors.Secondary, + background = AppColors.Background, + onBackground = AppColors.OnBackground, + surface = AppColors.Surface, + onSurface = AppColors.OnSurface, + surfaceVariant = AppColors.SurfaceVariant, + onSurfaceVariant = AppColors.OnSurfaceVariant, + error = AppColors.Error, + onError = Color.White, + outline = AppColors.Border, + outlineVariant = AppColors.Divider + ) + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography( + displayLarge = MaterialTheme.typography.displayLarge.copy( + fontWeight = FontWeight.Bold + ), + headlineLarge = MaterialTheme.typography.headlineLarge.copy( + fontWeight = FontWeight.Bold + ), + titleLarge = MaterialTheme.typography.titleLarge.copy( + fontWeight = FontWeight.SemiBold + ), + bodyLarge = MaterialTheme.typography.bodyLarge.copy( + lineHeight = 24.sp + ), + bodyMedium = MaterialTheme.typography.bodyMedium.copy( + lineHeight = 20.sp + ) + ) + ) { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background + ) { + content() } } } @@ -529,6 +572,4 @@ fun NotesApp() { ) } } -} - -private fun AppColors.setTheme(darkTheme: Boolean) {} \ No newline at end of file +} \ No newline at end of file diff --git a/app/src/main/java/com/example/notesai/data/model/ChatMessage.kt b/app/src/main/java/com/example/notesai/data/model/ChatMessage.kt index 2c19659..b6fa999 100644 --- a/app/src/main/java/com/example/notesai/data/model/ChatMessage.kt +++ b/app/src/main/java/com/example/notesai/data/model/ChatMessage.kt @@ -1,8 +1,9 @@ -// File: data/model/ChatMessage.kt package com.example.notesai.data.model +import kotlinx.serialization.Serializable import java.util.UUID +@Serializable data class ChatMessage( val id: String = UUID.randomUUID().toString(), val message: String, diff --git a/app/src/main/java/com/example/notesai/data/model/SerializableModels.kt b/app/src/main/java/com/example/notesai/data/model/SerializableModels.kt index 36c7df2..976ffa4 100644 --- a/app/src/main/java/com/example/notesai/data/model/SerializableModels.kt +++ b/app/src/main/java/com/example/notesai/data/model/SerializableModels.kt @@ -11,7 +11,7 @@ data class SerializableCategory( val gradientEnd: Long, val timestamp: Long, val isDeleted: Boolean = false, - val isPinned: Boolean = false // NEW: Tambahkan ini + val isPinned: Boolean = false ) @SuppressLint("UnsafeOptInUsageError") diff --git a/app/src/main/java/com/example/notesai/presentation/dialogs/CategoryDialog.kt b/app/src/main/java/com/example/notesai/presentation/dialogs/CategoryDialog.kt index 4b213a0..019b335 100644 --- a/app/src/main/java/com/example/notesai/presentation/dialogs/CategoryDialog.kt +++ b/app/src/main/java/com/example/notesai/presentation/dialogs/CategoryDialog.kt @@ -89,11 +89,11 @@ fun CategoryDialog( Spacer(modifier = Modifier.height(8.dp)) - // Color Grid + // Color Grid - 2 rows of 4 colors Constants.CategoryColors.chunked(4).forEach { row -> Row( modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(8.dp) + horizontalArrangement = Arrangement.spacedBy(12.dp) ) { row.forEachIndexed { _, gradient -> val globalIndex = Constants.CategoryColors.indexOf(gradient) @@ -135,13 +135,13 @@ fun CategoryDialog( Icons.Default.Check, contentDescription = "Selected", tint = Color.White, - modifier = Modifier.size(24.dp) + modifier = Modifier.size(28.dp) ) } } } } - Spacer(modifier = Modifier.height(8.dp)) + Spacer(modifier = Modifier.height(12.dp)) } } }, diff --git a/app/src/main/java/com/example/notesai/presentation/screens/ai/AIHelperScreen.kt b/app/src/main/java/com/example/notesai/presentation/screens/ai/AIHelperScreen.kt index 3ea5221..7ed119a 100644 --- a/app/src/main/java/com/example/notesai/presentation/screens/ai/AIHelperScreen.kt +++ b/app/src/main/java/com/example/notesai/presentation/screens/ai/AIHelperScreen.kt @@ -64,7 +64,7 @@ fun AIHelperScreen( var showHistoryDrawer by remember { mutableStateOf(false) } var currentChatId by remember { mutableStateOf(null) } - // NEW: File Upload States + // File Upload States var uploadedFile by remember { mutableStateOf(null) } var isGeneratingSummary by remember { mutableStateOf(false) } @@ -145,19 +145,21 @@ fun AIHelperScreen( .fillMaxSize() .background(AppColors.Background) ) { - // Top Bar with History Button & Stats - Surface( - color = AppColors.Surface, - shadowElevation = 2.dp + // UPDATED: Floating Top Bar + Box( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp) ) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp) + Surface( + color = AppColors.SurfaceElevated, + shape = RoundedCornerShape(Constants.Radius.ExtraLarge.dp), + shadowElevation = Constants.Elevation.Large.dp ) { - // Top Row - Menu & New Chat Row( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 12.dp, vertical = 8.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { @@ -234,41 +236,9 @@ fun AIHelperScreen( } } } - - Spacer(modifier = Modifier.height(12.dp)) - - // Stats - Compact - val filteredNotes = if (selectedCategory != null) { - notes.filter { it.categoryId == selectedCategory!!.id && !it.isArchived } - } else { - notes.filter { !it.isArchived } - } - - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceEvenly - ) { - CompactStatItem( - icon = Icons.Default.Description, - value = filteredNotes.size.toString(), - label = "Catatan" - ) - CompactStatItem( - icon = Icons.Default.Star, - value = filteredNotes.count { it.isPinned }.toString(), - label = "Dipasang" - ) - CompactStatItem( - icon = Icons.Default.Folder, - value = categories.size.toString(), - label = "Kategori" - ) - } } } - HorizontalDivider(color = AppColors.Divider) - // Chat Area Box( modifier = Modifier @@ -276,18 +246,20 @@ fun AIHelperScreen( .fillMaxWidth() ) { if (chatMessages.isEmpty()) { - // Welcome State + // Welcome State - Optimized Layout Column( modifier = Modifier .fillMaxSize() - .padding(32.dp) + .padding(horizontal = 24.dp, vertical = 16.dp) .verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center + verticalArrangement = Arrangement.spacedBy(16.dp) ) { + Spacer(modifier = Modifier.weight(0.5f)) + Box( modifier = Modifier - .size(80.dp) + .size(64.dp) .background( color = AppColors.Primary.copy(alpha = 0.1f), shape = CircleShape @@ -297,50 +269,49 @@ fun AIHelperScreen( Icon( Icons.Default.AutoAwesome, contentDescription = null, - modifier = Modifier.size(40.dp), + modifier = Modifier.size(32.dp), tint = AppColors.Primary ) } - Spacer(modifier = Modifier.height(24.dp)) - Text( "AI Assistant", style = MaterialTheme.typography.headlineMedium, color = AppColors.OnBackground, - fontWeight = FontWeight.Bold + fontWeight = FontWeight.Bold, + fontSize = 24.sp + ) + + Text( + "Tanyakan apa saja tentang catatan Anda", + style = MaterialTheme.typography.bodyMedium, + color = AppColors.OnSurfaceVariant, + textAlign = TextAlign.Center, + fontSize = 14.sp ) Spacer(modifier = Modifier.height(8.dp)) - Text( - "Tanyakan apa saja tentang catatan Anda", - style = MaterialTheme.typography.bodyLarge, - color = AppColors.OnSurfaceVariant, - textAlign = TextAlign.Center - ) - - Spacer(modifier = Modifier.height(32.dp)) - - // Suggestion Chips + // Suggestion Chips - Lebih besar Column( - horizontalAlignment = Alignment.Start, - verticalArrangement = Arrangement.spacedBy(8.dp), - modifier = Modifier.fillMaxWidth(0.85f) + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(10.dp), + modifier = Modifier.fillMaxWidth() ) { Text( "Contoh pertanyaan:", style = MaterialTheme.typography.labelMedium, - color = AppColors.OnSurfaceTertiary + color = AppColors.OnSurfaceTertiary, + fontSize = 13.sp ) SuggestionChip("Analisis catatan saya") { prompt = it } SuggestionChip("Buat ringkasan") { prompt = it } SuggestionChip("Berikan saran organisasi") { prompt = it } } - Spacer(modifier = Modifier.height(24.dp)) + Spacer(modifier = Modifier.height(8.dp)) - // NEW: File Upload Button + // File Upload Button - PENTING: Jangan dihapus! FileUploadButton( onFileSelected = { fileResult -> uploadedFile = fileResult @@ -401,14 +372,16 @@ fun AIHelperScreen( }, modifier = Modifier.fillMaxWidth() ) + + Spacer(modifier = Modifier.weight(0.5f)) } } else { - // Chat Messages + // Chat Messages - UPDATED: Bottom padding dari 100dp ke 120dp Column( modifier = Modifier .fillMaxSize() .verticalScroll(scrollState) - .padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 100.dp) + .padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 120.dp) ) { chatMessages.forEach { message -> ChatBubble( @@ -427,7 +400,7 @@ fun AIHelperScreen( Spacer(modifier = Modifier.height(12.dp)) } - // Loading Indicator (untuk chat biasa DAN file summary) + // Loading Indicator if (isLoading || isGeneratingSummary) { Row( modifier = Modifier @@ -489,7 +462,7 @@ fun AIHelperScreen( } } - // Input Area - Minimalist + // Input Area Surface( color = AppColors.Surface, shadowElevation = 8.dp, @@ -501,7 +474,7 @@ fun AIHelperScreen( .padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { - // NEW: Upload File Button (di atas input text) + // Upload File Button (di atas input text) if (chatMessages.isNotEmpty() && !isGeneratingSummary && !isLoading) { FileUploadButton( onFileSelected = { fileResult -> @@ -533,7 +506,6 @@ fun AIHelperScreen( isUser = true ) - // Scroll to show loading delay(100) scrollState.animateScrollTo(scrollState.maxValue) diff --git a/app/src/main/java/com/example/notesai/presentation/screens/ai/components/CompactStatItem.kt b/app/src/main/java/com/example/notesai/presentation/screens/ai/components/CompactStatItem.kt deleted file mode 100644 index 9d4b67d..0000000 --- a/app/src/main/java/com/example/notesai/presentation/screens/ai/components/CompactStatItem.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.example.notesai.presentation.screens.ai.components - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.example.notesai.util.AppColors -import com.example.notesai.util.Constants - -@Composable -fun CompactStatItem( - icon: androidx.compose.ui.graphics.vector.ImageVector, - value: String, - label: String -) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(6.dp), - modifier = Modifier - .background( - color = AppColors.SurfaceVariant, - shape = RoundedCornerShape(8.dp) - ) - .padding(horizontal = 12.dp, vertical = 8.dp) - ) { - Icon( - icon, - contentDescription = null, - tint = AppColors.Primary, - modifier = Modifier.size(16.dp) - ) - Column { - Text( - value, - style = MaterialTheme.typography.titleMedium, - color = AppColors.OnBackground, - fontWeight = FontWeight.Bold, - fontSize = 16.sp - ) - Text( - label, - style = MaterialTheme.typography.bodySmall, - color = AppColors.OnSurfaceTertiary, - fontSize = 11.sp - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/notesai/presentation/screens/ai/components/StatItem.kt b/app/src/main/java/com/example/notesai/presentation/screens/ai/components/StatItem.kt deleted file mode 100644 index 9966133..0000000 --- a/app/src/main/java/com/example/notesai/presentation/screens/ai/components/StatItem.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.example.notesai.presentation.screens.ai.components - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp - -@Composable -fun StatItem(label: String, value: String, color: Color) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.padding(8.dp) - ) { - Text( - value, - style = MaterialTheme.typography.headlineMedium, - color = color, - fontWeight = FontWeight.Bold - ) - Spacer(modifier = Modifier.height(4.dp)) - Text( - label, - style = MaterialTheme.typography.bodySmall, - color = Color(0xFF94A3B8) - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/notesai/presentation/screens/main/components/CategoryCard.kt b/app/src/main/java/com/example/notesai/presentation/screens/main/components/CategoryCard.kt index cd4983c..3747d9d 100644 --- a/app/src/main/java/com/example/notesai/presentation/screens/main/components/CategoryCard.kt +++ b/app/src/main/java/com/example/notesai/presentation/screens/main/components/CategoryCard.kt @@ -32,7 +32,7 @@ fun CategoryCard( onClick: () -> Unit, onDelete: () -> Unit = {}, onEdit: (String, Long, Long) -> Unit = { _, _, _ -> }, - onPin: () -> Unit = {} // NEW: Pin callback + onPin: () -> Unit = {} ) { var showDeleteConfirm by remember { mutableStateOf(false) } var showEditDialog by remember { mutableStateOf(false) } @@ -160,7 +160,7 @@ fun CategoryCard( ) } - // NEW: Pin Indicator & Menu Button + // Pin Indicator & Menu Button Row( horizontalArrangement = Arrangement.spacedBy(4.dp), verticalAlignment = Alignment.CenterVertically @@ -198,7 +198,7 @@ fun CategoryCard( onDismissRequest = { showMenu = false }, modifier = Modifier.background(AppColors.SurfaceElevated) ) { - // NEW: Pin/Unpin Menu Item + // Pin/Unpin Menu Item DropdownMenuItem( text = { Row( @@ -382,10 +382,11 @@ fun EditCategoryDialog( Spacer(modifier = Modifier.height(12.dp)) + // 8 colors in 2 rows Constants.CategoryColors.chunked(4).forEach { row -> Row( modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(8.dp) + horizontalArrangement = Arrangement.spacedBy(12.dp) ) { row.forEachIndexed { _, gradient -> val globalIndex = Constants.CategoryColors.indexOf(gradient) @@ -416,13 +417,13 @@ fun EditCategoryDialog( Icons.Default.Check, contentDescription = null, tint = Color.White, - modifier = Modifier.size(24.dp) + modifier = Modifier.size(28.dp) ) } } } } - Spacer(modifier = Modifier.height(8.dp)) + Spacer(modifier = Modifier.height(12.dp)) } } }, diff --git a/app/src/main/java/com/example/notesai/presentation/screens/note/EditableFullScreenNoteView.kt b/app/src/main/java/com/example/notesai/presentation/screens/note/EditableFullScreenNoteView.kt index 298dacc..8eb2a6d 100644 --- a/app/src/main/java/com/example/notesai/presentation/screens/note/EditableFullScreenNoteView.kt +++ b/app/src/main/java/com/example/notesai/presentation/screens/note/EditableFullScreenNoteView.kt @@ -20,7 +20,7 @@ import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.SolidColor // ✅ ADD +import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.* import androidx.compose.ui.text.AnnotatedString @@ -57,7 +57,6 @@ fun EditableFullScreenNoteView( ) } - val focusRequester = remember { FocusRequester() } val bringIntoViewRequester = remember { BringIntoViewRequester() } val scrollState = rememberScrollState() @@ -78,7 +77,7 @@ fun EditableFullScreenNoteView( } } - // 🔥 AUTO SAVE SAAT APP BACKGROUND / KELUAR + // AUTO SAVE SAAT APP BACKGROUND / KELUAR val lifecycleOwner = LocalLifecycleOwner.current DisposableEffect(lifecycleOwner) { @@ -95,7 +94,6 @@ fun EditableFullScreenNoteView( } } - val dateFormat = remember { SimpleDateFormat("dd MMMM yyyy, HH:mm", Locale("id", "ID")) } @@ -177,21 +175,32 @@ fun EditableFullScreenNoteView( ) ) { - TextField( + // FIXED: BasicTextField untuk judul agar sejajar dengan konten + BasicTextField( value = title, onValueChange = { title = it }, textStyle = MaterialTheme.typography.headlineLarge.copy( fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.onBackground ), - placeholder = { Text("Judul") }, - modifier = Modifier.fillMaxWidth(), - colors = TextFieldDefaults.colors( - focusedContainerColor = Color.Transparent, - unfocusedContainerColor = Color.Transparent, - focusedIndicatorColor = Color.Transparent, - unfocusedIndicatorColor = Color.Transparent - ) + cursorBrush = SolidColor(MaterialTheme.colorScheme.primary), + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp), + decorationBox = { innerTextField -> + Box { + if (title.isEmpty()) { + Text( + "Judul", + style = MaterialTheme.typography.headlineLarge.copy( + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.4f) + ) + ) + } + innerTextField() + } + } ) Spacer(Modifier.height(12.dp)) @@ -204,7 +213,7 @@ fun EditableFullScreenNoteView( HorizontalDivider(Modifier.padding(vertical = 20.dp)) - // ✅ FIX UTAMA: set cursorBrush agar insertion cursor muncul + // Konten editor BasicTextField( value = editorState.value, onValueChange = { @@ -213,7 +222,7 @@ fun EditableFullScreenNoteView( bringIntoViewRequester.bringIntoView() } }, - cursorBrush = SolidColor(Color(0xFFA855F7)), // ✅ ADD THIS + cursorBrush = SolidColor(Color(0xFFA855F7)), textStyle = MaterialTheme.typography.bodyLarge.copy( color = MaterialTheme.colorScheme.onBackground, lineHeight = 28.sp @@ -285,9 +294,9 @@ fun EditableFullScreenNoteView( editorState.toggleItalic() }, onUnderline = { ensureFocus(); editorState.toggleUnderline() }, - onHeading = { ensureFocus(); editorState.setHeading(1) }, // sementara H1 + onHeading = { ensureFocus(); editorState.setHeading(1) }, onBullet = { ensureFocus(); editorState.toggleBulletList() } ) } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/example/notesai/util/AppColors.kt b/app/src/main/java/com/example/notesai/util/AppColors.kt deleted file mode 100644 index 11aeceb..0000000 --- a/app/src/main/java/com/example/notesai/util/AppColors.kt +++ /dev/null @@ -1,3 +0,0 @@ -import com.example.notesai.util.AppColors - -annotation class AppColors diff --git a/app/src/main/java/com/example/notesai/util/Constants.kt b/app/src/main/java/com/example/notesai/util/Constants.kt index 26f630d..94d67c2 100644 --- a/app/src/main/java/com/example/notesai/util/Constants.kt +++ b/app/src/main/java/com/example/notesai/util/Constants.kt @@ -1,34 +1,77 @@ package com.example.notesai.util +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.compose.ui.graphics.Color object AppColors { + // Theme state + private var isDarkTheme by mutableStateOf(true) + // Primary Colors - val Primary = Color(0xFF6C63FF) - val Secondary = Color(0xFF03DAC6) - val Accent = Color(0xFFFF6B9D) + val Primary: Color + get() = if (isDarkTheme) Color(0xFF6C63FF) else Color(0xFF5A52D5) + + val Secondary: Color + get() = if (isDarkTheme) Color(0xFF03DAC6) else Color(0xFF018786) + + val Accent: Color + get() = if (isDarkTheme) Color(0xFFFF6B9D) else Color(0xFFE91E63) // Background Colors - val Background = Color(0xFF121212) - val Surface = Color(0xFF1E1E1E) - val SurfaceVariant = Color(0xFF2A2A2A) - val SurfaceElevated = Color(0xFF252525) + val Background: Color + get() = if (isDarkTheme) Color(0xFF121212) else Color(0xFFFAFAFA) + + val Surface: Color + get() = if (isDarkTheme) Color(0xFF1E1E1E) else Color(0xFFFFFFFF) + + val SurfaceVariant: Color + get() = if (isDarkTheme) Color(0xFF2A2A2A) else Color(0xFFF5F5F5) + + val SurfaceElevated: Color + get() = if (isDarkTheme) Color(0xFF252525) else Color(0xFFFFFFFF) // Text Colors - val OnBackground = Color(0xFFE1E1E1) - val OnSurface = Color(0xFFCCCCCC) - val OnSurfaceVariant = Color(0xFF9E9E9E) - val OnSurfaceTertiary = Color(0xFF757575) + val OnBackground: Color + get() = if (isDarkTheme) Color(0xFFE1E1E1) else Color(0xFF1C1B1F) + + val OnSurface: Color + get() = if (isDarkTheme) Color(0xFFCCCCCC) else Color(0xFF1C1B1F) + + val OnSurfaceVariant: Color + get() = if (isDarkTheme) Color(0xFF9E9E9E) else Color(0xFF49454F) + + val OnSurfaceTertiary: Color + get() = if (isDarkTheme) Color(0xFF757575) else Color(0xFF79747E) // Utility Colors - val Error = Color(0xFFCF6679) - val Warning = Color(0xFFFFB74D) - val Success = Color(0xFF81C784) - val Info = Color(0xFF64B5F6) + val Error: Color + get() = if (isDarkTheme) Color(0xFFCF6679) else Color(0xFFB3261E) + + val Warning: Color + get() = if (isDarkTheme) Color(0xFFFFB74D) else Color(0xFFF57C00) + + val Success: Color + get() = if (isDarkTheme) Color(0xFF81C784) else Color(0xFF388E3C) + + val Info: Color + get() = if (isDarkTheme) Color(0xFF64B5F6) else Color(0xFF1976D2) // Border & Divider - val Border = Color(0xFF3A3A3A) - val Divider = Color(0xFF2E2E2E) + val Border: Color + get() = if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE0E0E0) + + val Divider: Color + get() = if (isDarkTheme) Color(0xFF2E2E2E) else Color(0xFFE0E0E0) + + // Function to update theme + fun setTheme(darkTheme: Boolean) { + isDarkTheme = darkTheme + } + + // Get current theme state + fun isDark(): Boolean = isDarkTheme } object Constants { @@ -69,54 +112,30 @@ object Constants { // Reference to AppColors for compatibility val AppColors = com.example.notesai.util.AppColors - // Category gradient colors + // Category gradient colors - 8 pilihan warna val CategoryColors = listOf( - // Purple gradients + // Purple gradient 0xFF6750A4L to 0xFF7E57C2L, + + // Pink gradient 0xFF9C27B0L to 0xFFE91E63L, - // Blue gradients + // Blue gradient 0xFF2196F3L to 0xFF03A9F4L, - 0xFF1976D2L to 0xFF4FC3F7L, - // Green gradients + // Green gradient 0xFF4CAF50L to 0xFF8BC34AL, - 0xFF009688L to 0xFF00BCD4L, - // Orange gradients + // Orange gradient 0xFFFF9800L to 0xFFFFB74DL, - 0xFFFF5722L to 0xFFFF7043L, - // Red gradients + // Red gradient 0xFFF44336L to 0xFFE91E63L, - 0xFFD32F2FL to 0xFFFF5252L, - // Teal gradients + // Teal gradient 0xFF009688L to 0xFF26A69AL, - 0xFF00897BL to 0xFF4DB6ACL, - // Indigo gradients - 0xFF3F51B5L to 0xFF5C6BC0L, - 0xFF303F9FL to 0xFF7986CBL, - - // Amber gradients - 0xFFFFC107L to 0xFFFFD54FL, - 0xFFFFB300L to 0xFFFFCA28L, - - // Pink gradients - 0xFFE91E63L to 0xFFF06292L, - 0xFFC2185BL to 0xFFEC407AL, - - // Cyan gradients - 0xFF00BCD4L to 0xFF26C6DAL, - 0xFF0097A7L to 0xFF00ACC1L, - - // Deep Purple gradients - 0xFF673AB7L to 0xFF9575CDL, - 0xFF512DA8L to 0xFF7E57C2L, - - // Lime gradients - 0xFFCDDC39L to 0xFFD4E157L, - 0xFFAFB42BL to 0xFFC0CA33L + // Indigo gradient + 0xFF3F51B5L to 0xFF5C6BC0L ) } \ No newline at end of file