mobile bluesky app made with flutter lazurite.stormlightlabs.org/
mobile bluesky flutter
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

initial commit

Owais Jamil 3621003a

+6769
+47
.gitignore
··· 1 + # Miscellaneous 2 + *.class 3 + *.log 4 + *.pyc 5 + *.swp 6 + .DS_Store 7 + .atom/ 8 + .build/ 9 + .buildlog/ 10 + .history 11 + .svn/ 12 + .swiftpm/ 13 + migrate_working_dir/ 14 + 15 + # IntelliJ related 16 + *.iml 17 + *.ipr 18 + *.iws 19 + .idea/ 20 + 21 + # The .vscode folder contains launch configuration and tasks you configure in 22 + # VS Code which you may wish to be included in version control, so this line 23 + # is commented out by default. 24 + #.vscode/ 25 + 26 + # Flutter/Dart/Pub related 27 + **/doc/api/ 28 + **/ios/Flutter/.last_build_id 29 + .dart_tool/ 30 + .flutter-plugins-dependencies 31 + .pub-cache/ 32 + .pub/ 33 + /build/ 34 + /coverage/ 35 + 36 + # Symbolication related 37 + app.*.symbols 38 + 39 + # Obfuscation related 40 + app.*.map.json 41 + 42 + # Android Studio will place build artifacts here 43 + /android/app/debug 44 + /android/app/profile 45 + /android/app/release 46 + 47 + .sandbox/
+45
.metadata
··· 1 + # This file tracks properties of this Flutter project. 2 + # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 + # 4 + # This file should be version controlled and should not be manually edited. 5 + 6 + version: 7 + revision: "nixpkgs000000000000000000000000000000000" 8 + channel: "stable" 9 + 10 + project_type: app 11 + 12 + # Tracks metadata for the flutter migrate command 13 + migration: 14 + platforms: 15 + - platform: root 16 + create_revision: nixpkgs000000000000000000000000000000000 17 + base_revision: nixpkgs000000000000000000000000000000000 18 + - platform: android 19 + create_revision: nixpkgs000000000000000000000000000000000 20 + base_revision: nixpkgs000000000000000000000000000000000 21 + - platform: ios 22 + create_revision: nixpkgs000000000000000000000000000000000 23 + base_revision: nixpkgs000000000000000000000000000000000 24 + - platform: linux 25 + create_revision: nixpkgs000000000000000000000000000000000 26 + base_revision: nixpkgs000000000000000000000000000000000 27 + - platform: macos 28 + create_revision: nixpkgs000000000000000000000000000000000 29 + base_revision: nixpkgs000000000000000000000000000000000 30 + - platform: web 31 + create_revision: nixpkgs000000000000000000000000000000000 32 + base_revision: nixpkgs000000000000000000000000000000000 33 + - platform: windows 34 + create_revision: nixpkgs000000000000000000000000000000000 35 + base_revision: nixpkgs000000000000000000000000000000000 36 + 37 + # User provided section 38 + 39 + # List of Local paths (relative to this file) that should be 40 + # ignored by the migrate tool. 41 + # 42 + # Files that are not part of the templates will be ignored by default. 43 + unmanaged_files: 44 + - 'lib/main.dart' 45 + - 'ios/Runner.xcodeproj/project.pbxproj'
+4
.prettierrc
··· 1 + { 2 + "bracketSameLine": true, 3 + "printWidth": 120 4 + }
+3
README.md
··· 1 + # Lazurite 2 + 3 + Cross-platform mobile BlueSky client.
+20
analysis_options.yaml
··· 1 + include: package:flutter_lints/flutter.yaml 2 + 3 + analyzer: 4 + plugins: 5 + - custom_lint 6 + errors: 7 + invalid_annotation_target: ignore 8 + 9 + formatter: 10 + page_width: 120 11 + 12 + linter: 13 + rules: 14 + - prefer_single_quotes 15 + - sort_constructors_first 16 + - unawaited_futures 17 + - avoid_print 18 + - prefer_const_constructors 19 + - prefer_const_declarations 20 + - prefer_final_locals
+14
android/.gitignore
··· 1 + gradle-wrapper.jar 2 + /.gradle 3 + /captures/ 4 + /gradlew 5 + /gradlew.bat 6 + /local.properties 7 + GeneratedPluginRegistrant.java 8 + .cxx/ 9 + 10 + # Remember to never publicly share your keystore. 11 + # See https://flutter.dev/to/reference-keystore 12 + key.properties 13 + **/*.keystore 14 + **/*.jks
+44
android/app/build.gradle.kts
··· 1 + plugins { 2 + id("com.android.application") 3 + id("kotlin-android") 4 + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. 5 + id("dev.flutter.flutter-gradle-plugin") 6 + } 7 + 8 + android { 9 + namespace = "com.example.lazurite" 10 + compileSdk = flutter.compileSdkVersion 11 + ndkVersion = flutter.ndkVersion 12 + 13 + compileOptions { 14 + sourceCompatibility = JavaVersion.VERSION_17 15 + targetCompatibility = JavaVersion.VERSION_17 16 + } 17 + 18 + kotlinOptions { 19 + jvmTarget = JavaVersion.VERSION_17.toString() 20 + } 21 + 22 + defaultConfig { 23 + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 24 + applicationId = "com.example.lazurite" 25 + // You can update the following values to match your application needs. 26 + // For more information, see: https://flutter.dev/to/review-gradle-config. 27 + minSdk = flutter.minSdkVersion 28 + targetSdk = flutter.targetSdkVersion 29 + versionCode = flutter.versionCode 30 + versionName = flutter.versionName 31 + } 32 + 33 + buildTypes { 34 + release { 35 + // TODO: Add your own signing config for the release build. 36 + // Signing with the debug keys for now, so `flutter run --release` works. 37 + signingConfig = signingConfigs.getByName("debug") 38 + } 39 + } 40 + } 41 + 42 + flutter { 43 + source = "../.." 44 + }
+7
android/app/src/debug/AndroidManifest.xml
··· 1 + <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 2 + <!-- The INTERNET permission is required for development. Specifically, 3 + the Flutter tool needs it to communicate with the running application 4 + to allow setting breakpoints, to provide hot reload, etc. 5 + --> 6 + <uses-permission android:name="android.permission.INTERNET"/> 7 + </manifest>
+45
android/app/src/main/AndroidManifest.xml
··· 1 + <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 2 + <application 3 + android:label="lazurite" 4 + android:name="${applicationName}" 5 + android:icon="@mipmap/ic_launcher"> 6 + <activity 7 + android:name=".MainActivity" 8 + android:exported="true" 9 + android:launchMode="singleTop" 10 + android:taskAffinity="" 11 + android:theme="@style/LaunchTheme" 12 + android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" 13 + android:hardwareAccelerated="true" 14 + android:windowSoftInputMode="adjustResize"> 15 + <!-- Specifies an Android theme to apply to this Activity as soon as 16 + the Android process has started. This theme is visible to the user 17 + while the Flutter UI initializes. After that, this theme continues 18 + to determine the Window background behind the Flutter UI. --> 19 + <meta-data 20 + android:name="io.flutter.embedding.android.NormalTheme" 21 + android:resource="@style/NormalTheme" 22 + /> 23 + <intent-filter> 24 + <action android:name="android.intent.action.MAIN"/> 25 + <category android:name="android.intent.category.LAUNCHER"/> 26 + </intent-filter> 27 + </activity> 28 + <!-- Don't delete the meta-data below. 29 + This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> 30 + <meta-data 31 + android:name="flutterEmbedding" 32 + android:value="2" /> 33 + </application> 34 + <!-- Required to query activities that can process text, see: 35 + https://developer.android.com/training/package-visibility and 36 + https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT. 37 + 38 + In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. --> 39 + <queries> 40 + <intent> 41 + <action android:name="android.intent.action.PROCESS_TEXT"/> 42 + <data android:mimeType="text/plain"/> 43 + </intent> 44 + </queries> 45 + </manifest>
+5
android/app/src/main/kotlin/com/example/lazurite/MainActivity.kt
··· 1 + package com.example.lazurite 2 + 3 + import io.flutter.embedding.android.FlutterActivity 4 + 5 + class MainActivity : FlutterActivity()
+12
android/app/src/main/res/drawable-v21/launch_background.xml
··· 1 + <?xml version="1.0" encoding="utf-8"?> 2 + <!-- Modify this file to customize your launch splash screen --> 3 + <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> 4 + <item android:drawable="?android:colorBackground" /> 5 + 6 + <!-- You can insert your own image assets here --> 7 + <!-- <item> 8 + <bitmap 9 + android:gravity="center" 10 + android:src="@mipmap/launch_image" /> 11 + </item> --> 12 + </layer-list>
+12
android/app/src/main/res/drawable/launch_background.xml
··· 1 + <?xml version="1.0" encoding="utf-8"?> 2 + <!-- Modify this file to customize your launch splash screen --> 3 + <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> 4 + <item android:drawable="@android:color/white" /> 5 + 6 + <!-- You can insert your own image assets here --> 7 + <!-- <item> 8 + <bitmap 9 + android:gravity="center" 10 + android:src="@mipmap/launch_image" /> 11 + </item> --> 12 + </layer-list>
android/app/src/main/res/mipmap-hdpi/ic_launcher.png

This is a binary file and will not be displayed.

android/app/src/main/res/mipmap-mdpi/ic_launcher.png

This is a binary file and will not be displayed.

android/app/src/main/res/mipmap-xhdpi/ic_launcher.png

This is a binary file and will not be displayed.

android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png

This is a binary file and will not be displayed.

android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png

This is a binary file and will not be displayed.

+18
android/app/src/main/res/values-night/styles.xml
··· 1 + <?xml version="1.0" encoding="utf-8"?> 2 + <resources> 3 + <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on --> 4 + <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> 5 + <!-- Show a splash screen on the activity. Automatically removed when 6 + the Flutter engine draws its first frame --> 7 + <item name="android:windowBackground">@drawable/launch_background</item> 8 + </style> 9 + <!-- Theme applied to the Android Window as soon as the process has started. 10 + This theme determines the color of the Android Window while your 11 + Flutter UI initializes, as well as behind your Flutter UI while its 12 + running. 13 + 14 + This Theme is only used starting with V2 of Flutter's Android embedding. --> 15 + <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar"> 16 + <item name="android:windowBackground">?android:colorBackground</item> 17 + </style> 18 + </resources>
+18
android/app/src/main/res/values/styles.xml
··· 1 + <?xml version="1.0" encoding="utf-8"?> 2 + <resources> 3 + <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off --> 4 + <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar"> 5 + <!-- Show a splash screen on the activity. Automatically removed when 6 + the Flutter engine draws its first frame --> 7 + <item name="android:windowBackground">@drawable/launch_background</item> 8 + </style> 9 + <!-- Theme applied to the Android Window as soon as the process has started. 10 + This theme determines the color of the Android Window while your 11 + Flutter UI initializes, as well as behind your Flutter UI while its 12 + running. 13 + 14 + This Theme is only used starting with V2 of Flutter's Android embedding. --> 15 + <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar"> 16 + <item name="android:windowBackground">?android:colorBackground</item> 17 + </style> 18 + </resources>
+7
android/app/src/profile/AndroidManifest.xml
··· 1 + <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 2 + <!-- The INTERNET permission is required for development. Specifically, 3 + the Flutter tool needs it to communicate with the running application 4 + to allow setting breakpoints, to provide hot reload, etc. 5 + --> 6 + <uses-permission android:name="android.permission.INTERNET"/> 7 + </manifest>
+24
android/build.gradle.kts
··· 1 + allprojects { 2 + repositories { 3 + google() 4 + mavenCentral() 5 + } 6 + } 7 + 8 + val newBuildDir: Directory = 9 + rootProject.layout.buildDirectory 10 + .dir("../../build") 11 + .get() 12 + rootProject.layout.buildDirectory.value(newBuildDir) 13 + 14 + subprojects { 15 + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) 16 + project.layout.buildDirectory.value(newSubprojectBuildDir) 17 + } 18 + subprojects { 19 + project.evaluationDependsOn(":app") 20 + } 21 + 22 + tasks.register<Delete>("clean") { 23 + delete(rootProject.layout.buildDirectory) 24 + }
+2
android/gradle.properties
··· 1 + org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError 2 + android.useAndroidX=true
+5
android/gradle/wrapper/gradle-wrapper.properties
··· 1 + distributionBase=GRADLE_USER_HOME 2 + distributionPath=wrapper/dists 3 + zipStoreBase=GRADLE_USER_HOME 4 + zipStorePath=wrapper/dists 5 + distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip
+26
android/settings.gradle.kts
··· 1 + pluginManagement { 2 + val flutterSdkPath = 3 + run { 4 + val properties = java.util.Properties() 5 + file("local.properties").inputStream().use { properties.load(it) } 6 + val flutterSdkPath = properties.getProperty("flutter.sdk") 7 + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } 8 + flutterSdkPath 9 + } 10 + 11 + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 12 + 13 + repositories { 14 + google() 15 + mavenCentral() 16 + gradlePluginPortal() 17 + } 18 + } 19 + 20 + plugins { 21 + id("dev.flutter.flutter-plugin-loader") version "1.0.0" 22 + id("com.android.application") version "8.11.1" apply false 23 + id("org.jetbrains.kotlin.android") version "2.2.20" apply false 24 + } 25 + 26 + include(":app")
+1
docs/README.md
··· 1 + # Lazurite Project Documentation
+516
docs/designs/devtools.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> 6 + <title>Dev Tools - Lazurite</title> 7 + <link rel="preconnect" href="https://fonts.googleapis.com"> 8 + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 9 + <link href="https://fonts.googleapis.com/css2?family=Lora:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet"> 10 + <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/geist@1.2.2/dist/fonts/geist-sans/style.css"> 11 + <link rel="stylesheet" href="styles.css"> 12 + <style> 13 + .devtools-container { 14 + padding-bottom: 88px; 15 + } 16 + 17 + .devtools-search { 18 + padding: 16px; 19 + border-bottom: 1px solid var(--border); 20 + } 21 + 22 + .devtools-search-row { 23 + display: flex; 24 + gap: 8px; 25 + } 26 + 27 + .devtools-search-row .input { 28 + flex: 1; 29 + font-family: 'JetBrains Mono', monospace; 30 + font-size: 13px; 31 + } 32 + 33 + .devtools-search-btn { 34 + padding: 12px 16px; 35 + border-radius: 8px; 36 + border: none; 37 + background-color: var(--accent-primary); 38 + color: white; 39 + font-weight: 600; 40 + font-size: 14px; 41 + cursor: pointer; 42 + transition: background-color 0.2s ease; 43 + white-space: nowrap; 44 + } 45 + 46 + .devtools-search-btn:hover { 47 + background-color: var(--accent-primary-hover); 48 + } 49 + 50 + .devtools-tabs { 51 + display: flex; 52 + border-bottom: 1px solid var(--border); 53 + background-color: var(--bg); 54 + } 55 + 56 + .devtools-tab { 57 + flex: 1; 58 + padding: 12px; 59 + text-align: center; 60 + font-weight: 600; 61 + font-size: 13px; 62 + color: var(--text-secondary); 63 + cursor: pointer; 64 + border-bottom: 2px solid transparent; 65 + transition: all 0.2s ease; 66 + background: none; 67 + border-top: none; 68 + border-left: none; 69 + border-right: none; 70 + } 71 + 72 + .devtools-tab:hover { 73 + background-color: var(--surface); 74 + color: var(--text-primary); 75 + } 76 + 77 + .devtools-tab.active { 78 + color: var(--text-primary); 79 + border-bottom-color: var(--accent-primary); 80 + } 81 + 82 + /* Repo Overview */ 83 + .repo-header { 84 + padding: 16px; 85 + border-bottom: 1px solid var(--border); 86 + background-color: var(--surface); 87 + } 88 + 89 + .repo-identity { 90 + display: flex; 91 + align-items: center; 92 + gap: 12px; 93 + margin-bottom: 12px; 94 + } 95 + 96 + .repo-avatar { 97 + width: 40px; 98 + height: 40px; 99 + border-radius: 50%; 100 + background-color: var(--surface-variant); 101 + display: flex; 102 + align-items: center; 103 + justify-content: center; 104 + font-weight: 600; 105 + font-size: 14px; 106 + color: var(--text-secondary); 107 + flex-shrink: 0; 108 + } 109 + 110 + .repo-names { 111 + flex: 1; 112 + min-width: 0; 113 + } 114 + 115 + .repo-handle { 116 + font-weight: 600; 117 + font-size: 15px; 118 + color: var(--text-primary); 119 + } 120 + 121 + .repo-did { 122 + font-family: 'JetBrains Mono', monospace; 123 + font-size: 11px; 124 + color: var(--text-muted); 125 + overflow: hidden; 126 + text-overflow: ellipsis; 127 + white-space: nowrap; 128 + } 129 + 130 + .repo-stats { 131 + display: flex; 132 + gap: 16px; 133 + } 134 + 135 + .repo-stat { 136 + font-size: 13px; 137 + color: var(--text-secondary); 138 + } 139 + 140 + .repo-stat strong { 141 + color: var(--text-primary); 142 + font-weight: 600; 143 + } 144 + 145 + /* Collection List */ 146 + .collection-item { 147 + display: flex; 148 + align-items: center; 149 + justify-content: space-between; 150 + padding: 14px 16px; 151 + border-bottom: 1px solid var(--border); 152 + cursor: pointer; 153 + transition: background-color 0.2s ease; 154 + } 155 + 156 + .collection-item:hover { 157 + background-color: var(--surface); 158 + } 159 + 160 + .collection-item-left { 161 + display: flex; 162 + align-items: center; 163 + gap: 12px; 164 + min-width: 0; 165 + flex: 1; 166 + } 167 + 168 + .collection-icon { 169 + width: 32px; 170 + height: 32px; 171 + border-radius: 6px; 172 + background-color: var(--surface); 173 + border: 1px solid var(--border); 174 + display: flex; 175 + align-items: center; 176 + justify-content: center; 177 + flex-shrink: 0; 178 + } 179 + 180 + .collection-icon svg { 181 + width: 16px; 182 + height: 16px; 183 + color: var(--text-secondary); 184 + } 185 + 186 + .collection-name { 187 + font-family: 'JetBrains Mono', monospace; 188 + font-size: 13px; 189 + font-weight: 500; 190 + color: var(--text-primary); 191 + overflow: hidden; 192 + text-overflow: ellipsis; 193 + white-space: nowrap; 194 + } 195 + 196 + .collection-count { 197 + font-size: 12px; 198 + color: var(--text-muted); 199 + background-color: var(--surface); 200 + padding: 2px 8px; 201 + border-radius: 9999px; 202 + flex-shrink: 0; 203 + } 204 + 205 + .collection-chevron { 206 + color: var(--text-muted); 207 + flex-shrink: 0; 208 + margin-left: 8px; 209 + } 210 + 211 + .collection-chevron svg { 212 + width: 16px; 213 + height: 16px; 214 + } 215 + 216 + /* Record Inspector */ 217 + .record-header { 218 + padding: 12px 16px; 219 + background-color: var(--surface); 220 + border-bottom: 1px solid var(--border); 221 + display: flex; 222 + align-items: center; 223 + justify-content: space-between; 224 + } 225 + 226 + .record-breadcrumb { 227 + font-family: 'JetBrains Mono', monospace; 228 + font-size: 12px; 229 + color: var(--text-secondary); 230 + overflow: hidden; 231 + text-overflow: ellipsis; 232 + white-space: nowrap; 233 + } 234 + 235 + .record-breadcrumb span { 236 + color: var(--accent-primary); 237 + } 238 + 239 + .record-copy-btn { 240 + padding: 4px 10px; 241 + border-radius: 4px; 242 + border: 1px solid var(--border); 243 + background-color: var(--bg); 244 + color: var(--text-secondary); 245 + font-size: 11px; 246 + font-weight: 500; 247 + cursor: pointer; 248 + transition: all 0.2s ease; 249 + } 250 + 251 + .record-copy-btn:hover { 252 + background-color: var(--surface-variant); 253 + color: var(--text-primary); 254 + } 255 + 256 + .json-viewer { 257 + padding: 16px; 258 + font-family: 'JetBrains Mono', monospace; 259 + font-size: 12px; 260 + line-height: 1.8; 261 + overflow-x: auto; 262 + } 263 + 264 + .json-key { color: var(--accent-primary); } 265 + .json-string { color: var(--accent-success); } 266 + .json-number { color: var(--accent-secondary); } 267 + .json-bool { color: var(--accent-warning); } 268 + .json-null { color: var(--text-muted); } 269 + .json-brace { color: var(--text-secondary); } 270 + 271 + /* Section Label */ 272 + .section-label { 273 + padding: 8px 16px; 274 + font-size: 12px; 275 + font-weight: 600; 276 + color: var(--text-muted); 277 + text-transform: uppercase; 278 + letter-spacing: 0.5px; 279 + background-color: var(--surface); 280 + border-bottom: 1px solid var(--border); 281 + } 282 + </style> 283 + </head> 284 + <body> 285 + <div class="mobile-container"> 286 + 287 + <!-- Header --> 288 + <header class="header"> 289 + <button class="header-action">← Back</button> 290 + <h1 class="header-title">PDS Explorer</h1> 291 + <span class="debug-badge">Debug</span> 292 + </header> 293 + 294 + <!-- Search / AT-URI Input --> 295 + <div class="devtools-search"> 296 + <div class="devtools-search-row"> 297 + <input class="input" type="text" placeholder="Handle, DID, or at:// URI" value="alice.bsky.social"> 298 + <button class="devtools-search-btn">Resolve</button> 299 + </div> 300 + </div> 301 + 302 + <!-- Tabs --> 303 + <div class="devtools-tabs"> 304 + <button class="devtools-tab active">Repo</button> 305 + <button class="devtools-tab">Records</button> 306 + <button class="devtools-tab">JSON</button> 307 + </div> 308 + 309 + <div class="devtools-container"> 310 + 311 + <!-- Repo Overview --> 312 + <div class="repo-header"> 313 + <div class="repo-identity"> 314 + <div class="repo-avatar">AS</div> 315 + <div class="repo-names"> 316 + <div class="repo-handle">alice.bsky.social</div> 317 + <div class="repo-did">did:plc:z72i7hdynmk6r22z27h6tvur</div> 318 + </div> 319 + </div> 320 + <div class="repo-stats"> 321 + <div class="repo-stat"><strong>12</strong> collections</div> 322 + <div class="repo-stat"><strong>1,847</strong> records</div> 323 + </div> 324 + </div> 325 + 326 + <!-- Collections --> 327 + <div class="section-label">Collections</div> 328 + 329 + <div class="collection-item"> 330 + <div class="collection-item-left"> 331 + <div class="collection-icon"> 332 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 333 + <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/> 334 + </svg> 335 + </div> 336 + <div class="collection-name">app.bsky.feed.post</div> 337 + </div> 338 + <span class="collection-count">482</span> 339 + <div class="collection-chevron"> 340 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 341 + <polyline points="9 18 15 12 9 6"/> 342 + </svg> 343 + </div> 344 + </div> 345 + 346 + <div class="collection-item"> 347 + <div class="collection-item-left"> 348 + <div class="collection-icon"> 349 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 350 + <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/> 351 + </svg> 352 + </div> 353 + <div class="collection-name">app.bsky.feed.like</div> 354 + </div> 355 + <span class="collection-count">1,203</span> 356 + <div class="collection-chevron"> 357 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 358 + <polyline points="9 18 15 12 9 6"/> 359 + </svg> 360 + </div> 361 + </div> 362 + 363 + <div class="collection-item"> 364 + <div class="collection-item-left"> 365 + <div class="collection-icon"> 366 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 367 + <polyline points="17 1 21 5 17 9"/> 368 + <path d="M3 11V9a4 4 0 0 1 4-4h14"/> 369 + <polyline points="7 23 3 19 7 15"/> 370 + <path d="M21 13v2a4 4 0 0 1-4 4H3"/> 371 + </svg> 372 + </div> 373 + <div class="collection-name">app.bsky.feed.repost</div> 374 + </div> 375 + <span class="collection-count">89</span> 376 + <div class="collection-chevron"> 377 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 378 + <polyline points="9 18 15 12 9 6"/> 379 + </svg> 380 + </div> 381 + </div> 382 + 383 + <div class="collection-item"> 384 + <div class="collection-item-left"> 385 + <div class="collection-icon"> 386 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 387 + <path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/> 388 + <circle cx="8.5" cy="7" r="4"/> 389 + <line x1="20" y1="8" x2="20" y2="14"/> 390 + <line x1="23" y1="11" x2="17" y2="11"/> 391 + </svg> 392 + </div> 393 + <div class="collection-name">app.bsky.graph.follow</div> 394 + </div> 395 + <span class="collection-count">256</span> 396 + <div class="collection-chevron"> 397 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 398 + <polyline points="9 18 15 12 9 6"/> 399 + </svg> 400 + </div> 401 + </div> 402 + 403 + <div class="collection-item"> 404 + <div class="collection-item-left"> 405 + <div class="collection-icon"> 406 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 407 + <circle cx="12" cy="12" r="10"/> 408 + <line x1="2" y1="12" x2="22" y2="12"/> 409 + <path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/> 410 + </svg> 411 + </div> 412 + <div class="collection-name">app.bsky.feed.generator</div> 413 + </div> 414 + <span class="collection-count">2</span> 415 + <div class="collection-chevron"> 416 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 417 + <polyline points="9 18 15 12 9 6"/> 418 + </svg> 419 + </div> 420 + </div> 421 + 422 + <div class="collection-item"> 423 + <div class="collection-item-left"> 424 + <div class="collection-icon"> 425 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 426 + <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/> 427 + <circle cx="12" cy="7" r="4"/> 428 + </svg> 429 + </div> 430 + <div class="collection-name">app.bsky.actor.profile</div> 431 + </div> 432 + <span class="collection-count">1</span> 433 + <div class="collection-chevron"> 434 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 435 + <polyline points="9 18 15 12 9 6"/> 436 + </svg> 437 + </div> 438 + </div> 439 + 440 + <!-- Record Inspector Preview (shown when tapping a record) --> 441 + <div class="section-label">Record Preview</div> 442 + 443 + <div class="record-header"> 444 + <div class="record-breadcrumb"> 445 + <span>app.bsky.feed.post</span> / 3lbr7yz2gfk2j 446 + </div> 447 + <button class="record-copy-btn">Copy JSON</button> 448 + </div> 449 + 450 + <div class="json-viewer"> 451 + <span class="json-brace">{</span><br> 452 + &nbsp;&nbsp;<span class="json-key">"$type"</span>: <span class="json-string">"app.bsky.feed.post"</span>,<br> 453 + &nbsp;&nbsp;<span class="json-key">"text"</span>: <span class="json-string">"Just launched my new project! 🚀"</span>,<br> 454 + &nbsp;&nbsp;<span class="json-key">"createdAt"</span>: <span class="json-string">"2024-03-15T14:30:00.000Z"</span>,<br> 455 + &nbsp;&nbsp;<span class="json-key">"langs"</span>: <span class="json-brace">[</span><span class="json-string">"en"</span><span class="json-brace">]</span>,<br> 456 + &nbsp;&nbsp;<span class="json-key">"facets"</span>: <span class="json-brace">[</span><br> 457 + &nbsp;&nbsp;&nbsp;&nbsp;<span class="json-brace">{</span><br> 458 + &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="json-key">"index"</span>: <span class="json-brace">{</span><br> 459 + &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="json-key">"byteStart"</span>: <span class="json-number">0</span>,<br> 460 + &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="json-key">"byteEnd"</span>: <span class="json-number">34</span><br> 461 + &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="json-brace">}</span>,<br> 462 + &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="json-key">"features"</span>: <span class="json-brace">[</span><span class="json-brace">{</span><br> 463 + &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="json-key">"$type"</span>: <span class="json-string">"...facet#tag"</span>,<br> 464 + &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="json-key">"tag"</span>: <span class="json-string">"buildinpublic"</span><br> 465 + &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="json-brace">}</span><span class="json-brace">]</span><br> 466 + &nbsp;&nbsp;&nbsp;&nbsp;<span class="json-brace">}</span><br> 467 + &nbsp;&nbsp;<span class="json-brace">]</span><br> 468 + <span class="json-brace">}</span> 469 + </div> 470 + 471 + </div> 472 + 473 + <!-- Bottom Navigation --> 474 + <nav class="nav-bar"> 475 + <a href="home.html" class="nav-item"> 476 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 477 + <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/> 478 + <polyline points="9 22 9 12 15 12 15 22"/> 479 + </svg> 480 + <span>Home</span> 481 + </a> 482 + 483 + <a href="search.html" class="nav-item"> 484 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 485 + <circle cx="11" cy="11" r="8"/> 486 + <line x1="21" y1="21" x2="16.65" y2="16.65"/> 487 + </svg> 488 + <span>Search</span> 489 + </a> 490 + 491 + <a href="profile.html" class="nav-item"> 492 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 493 + <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/> 494 + <circle cx="12" cy="7" r="4"/> 495 + </svg> 496 + <span>Profile</span> 497 + </a> 498 + 499 + <a href="settings.html" class="nav-item"> 500 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 501 + <circle cx="12" cy="12" r="3"/> 502 + <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/> 503 + </svg> 504 + <span>Settings</span> 505 + </a> 506 + </nav> 507 + 508 + </div> 509 + 510 + <script> 511 + if (localStorage.getItem('theme') === 'dark') { 512 + document.documentElement.setAttribute('data-theme', 'dark'); 513 + } 514 + </script> 515 + </body> 516 + </html>
+579
docs/designs/feeds.html
··· 1 + <!doctype html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8" /> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> 6 + <title>Feeds - Lazurite</title> 7 + <link rel="preconnect" href="https://fonts.googleapis.com" /> 8 + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> 9 + <link href="https://fonts.googleapis.com/css2?family=Lora:wght@400;500;600;700&display=swap" rel="stylesheet" /> 10 + <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/geist@1.2.2/dist/fonts/geist-sans/style.css" /> 11 + <link rel="stylesheet" href="styles.css" /> 12 + <style> 13 + .feeds-container { 14 + padding-bottom: 88px; 15 + } 16 + 17 + .feeds-section-header { 18 + display: flex; 19 + align-items: center; 20 + justify-content: space-between; 21 + padding: 12px 16px; 22 + background-color: var(--surface); 23 + border-bottom: 1px solid var(--border); 24 + } 25 + 26 + .feeds-section-title { 27 + font-size: 13px; 28 + font-weight: 600; 29 + color: var(--text-muted); 30 + text-transform: uppercase; 31 + letter-spacing: 0.5px; 32 + } 33 + 34 + .feeds-section-action { 35 + background: none; 36 + border: none; 37 + color: var(--accent-primary); 38 + font-size: 13px; 39 + font-weight: 600; 40 + cursor: pointer; 41 + } 42 + 43 + /* Feed Item */ 44 + .feed-item { 45 + display: flex; 46 + align-items: center; 47 + gap: 12px; 48 + padding: 14px 16px; 49 + border-bottom: 1px solid var(--border); 50 + transition: background-color 0.2s ease; 51 + } 52 + 53 + .feed-item-drag { 54 + width: 20px; 55 + display: flex; 56 + flex-direction: column; 57 + align-items: center; 58 + gap: 2px; 59 + cursor: grab; 60 + flex-shrink: 0; 61 + color: var(--text-muted); 62 + } 63 + 64 + .feed-item-drag svg { 65 + width: 16px; 66 + height: 16px; 67 + } 68 + 69 + .feed-item-icon { 70 + width: 40px; 71 + height: 40px; 72 + border-radius: 10px; 73 + display: flex; 74 + align-items: center; 75 + justify-content: center; 76 + font-size: 18px; 77 + flex-shrink: 0; 78 + } 79 + 80 + .feed-item-icon-blue { 81 + background: linear-gradient(135deg, #0066ff 0%, #0ea5e9 100%); 82 + color: white; 83 + } 84 + 85 + .feed-item-icon-purple { 86 + background: linear-gradient(135deg, #8b5cf6 0%, #be95ff 100%); 87 + color: white; 88 + } 89 + 90 + .feed-item-icon-green { 91 + background: linear-gradient(135deg, #22c55e 0%, #42be65 100%); 92 + color: white; 93 + } 94 + 95 + .feed-item-icon-orange { 96 + background: linear-gradient(135deg, #f59e0b 0%, #fb923c 100%); 97 + color: white; 98 + } 99 + 100 + .feed-item-icon-pink { 101 + background: linear-gradient(135deg, #ee5396 0%, #ff7eb6 100%); 102 + color: white; 103 + } 104 + 105 + .feed-item-icon-teal { 106 + background: linear-gradient(135deg, #08bdba 0%, #3ddbd9 100%); 107 + color: white; 108 + } 109 + 110 + .feed-item-info { 111 + flex: 1; 112 + min-width: 0; 113 + } 114 + 115 + .feed-item-name { 116 + font-weight: 600; 117 + font-size: 15px; 118 + color: var(--text-primary); 119 + } 120 + 121 + .feed-item-creator { 122 + font-size: 13px; 123 + color: var(--text-secondary); 124 + } 125 + 126 + .feed-item-desc { 127 + font-size: 13px; 128 + color: var(--text-muted); 129 + margin-top: 2px; 130 + display: -webkit-box; 131 + line-clamp: 1; 132 + -webkit-line-clamp: 1; 133 + -webkit-box-orient: vertical; 134 + overflow: hidden; 135 + } 136 + 137 + .feed-item-actions { 138 + display: flex; 139 + align-items: center; 140 + gap: 8px; 141 + flex-shrink: 0; 142 + } 143 + 144 + .feed-pin-btn { 145 + width: 32px; 146 + height: 32px; 147 + border-radius: 50%; 148 + border: 1.5px solid var(--border); 149 + background: none; 150 + cursor: pointer; 151 + display: flex; 152 + align-items: center; 153 + justify-content: center; 154 + transition: all 0.2s ease; 155 + } 156 + 157 + .feed-pin-btn svg { 158 + width: 16px; 159 + height: 16px; 160 + color: var(--text-muted); 161 + } 162 + 163 + .feed-pin-btn:hover { 164 + border-color: var(--accent-primary); 165 + } 166 + 167 + .feed-pin-btn:hover svg { 168 + color: var(--accent-primary); 169 + } 170 + 171 + .feed-pin-btn.pinned { 172 + background-color: var(--accent-primary); 173 + border-color: var(--accent-primary); 174 + } 175 + 176 + .feed-pin-btn.pinned svg { 177 + color: white; 178 + } 179 + 180 + .feed-remove-btn { 181 + width: 32px; 182 + height: 32px; 183 + border-radius: 50%; 184 + border: none; 185 + background: none; 186 + cursor: pointer; 187 + display: flex; 188 + align-items: center; 189 + justify-content: center; 190 + transition: all 0.2s ease; 191 + } 192 + 193 + .feed-remove-btn svg { 194 + width: 16px; 195 + height: 16px; 196 + color: var(--text-muted); 197 + } 198 + 199 + .feed-remove-btn:hover svg { 200 + color: var(--accent-error); 201 + } 202 + 203 + /* Discover Feed Card */ 204 + .discover-grid { 205 + padding: 16px; 206 + display: flex; 207 + flex-direction: column; 208 + gap: 12px; 209 + } 210 + 211 + .discover-card { 212 + display: flex; 213 + align-items: center; 214 + gap: 12px; 215 + padding: 12px; 216 + background-color: var(--surface); 217 + border-radius: 12px; 218 + border: 1px solid var(--border); 219 + cursor: pointer; 220 + transition: all 0.2s ease; 221 + } 222 + 223 + .discover-card:hover { 224 + border-color: var(--accent-primary); 225 + background-color: var(--surface-variant); 226 + } 227 + 228 + .discover-card-info { 229 + flex: 1; 230 + min-width: 0; 231 + } 232 + 233 + .discover-card-name { 234 + font-weight: 600; 235 + font-size: 15px; 236 + color: var(--text-primary); 237 + } 238 + 239 + .discover-card-creator { 240 + font-size: 12px; 241 + color: var(--text-secondary); 242 + } 243 + 244 + .discover-card-desc { 245 + font-size: 13px; 246 + color: var(--text-muted); 247 + margin-top: 4px; 248 + display: -webkit-box; 249 + line-clamp: 2; 250 + -webkit-line-clamp: 2; 251 + -webkit-box-orient: vertical; 252 + overflow: hidden; 253 + } 254 + 255 + .discover-card-likes { 256 + font-size: 12px; 257 + color: var(--text-muted); 258 + margin-top: 4px; 259 + } 260 + 261 + .add-feed-btn { 262 + padding: 6px 14px; 263 + border-radius: 9999px; 264 + border: 1.5px solid var(--accent-primary); 265 + background: none; 266 + color: var(--accent-primary); 267 + font-size: 13px; 268 + font-weight: 600; 269 + cursor: pointer; 270 + transition: all 0.2s ease; 271 + flex-shrink: 0; 272 + white-space: nowrap; 273 + } 274 + 275 + .add-feed-btn:hover { 276 + background-color: var(--accent-primary); 277 + color: white; 278 + } 279 + </style> 280 + </head> 281 + <body> 282 + <div class="mobile-container"> 283 + <!-- Header --> 284 + <header class="header"> 285 + <button class="header-action">← Back</button> 286 + <h1 class="header-title">My Feeds</h1> 287 + <button class="header-action">Done</button> 288 + </header> 289 + 290 + <div class="feeds-container"> 291 + <!-- Pinned Feeds --> 292 + <div class="feeds-section-header"> 293 + <span class="feeds-section-title">Pinned Feeds</span> 294 + <button class="feeds-section-action">Reorder</button> 295 + </div> 296 + 297 + <div class="feed-item"> 298 + <div class="feed-item-drag"> 299 + <svg 300 + viewBox="0 0 24 24" 301 + fill="none" 302 + stroke="currentColor" 303 + stroke-width="2" 304 + stroke-linecap="round" 305 + stroke-linejoin="round"> 306 + <line x1="3" y1="9" x2="21" y2="9" /> 307 + <line x1="3" y1="15" x2="21" y2="15" /> 308 + </svg> 309 + </div> 310 + <div class="feed-item-icon feed-item-icon-blue"> 311 + <svg 312 + viewBox="0 0 24 24" 313 + fill="none" 314 + stroke="currentColor" 315 + stroke-width="2" 316 + stroke-linecap="round" 317 + stroke-linejoin="round" 318 + width="20" 319 + height="20"> 320 + <path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" /> 321 + <circle cx="8.5" cy="7" r="4" /> 322 + <polyline points="17 11 19 13 23 9" /> 323 + </svg> 324 + </div> 325 + <div class="feed-item-info"> 326 + <div class="feed-item-name">Following</div> 327 + <div class="feed-item-creator">Timeline</div> 328 + </div> 329 + <div class="feed-item-actions"> 330 + <button class="feed-pin-btn pinned" title="Unpin"> 331 + <svg viewBox="0 0 24 24" fill="currentColor" stroke="none"> 332 + <path 333 + d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" /> 334 + </svg> 335 + </button> 336 + </div> 337 + </div> 338 + 339 + <div class="feed-item"> 340 + <div class="feed-item-drag"> 341 + <svg 342 + viewBox="0 0 24 24" 343 + fill="none" 344 + stroke="currentColor" 345 + stroke-width="2" 346 + stroke-linecap="round" 347 + stroke-linejoin="round"> 348 + <line x1="3" y1="9" x2="21" y2="9" /> 349 + <line x1="3" y1="15" x2="21" y2="15" /> 350 + </svg> 351 + </div> 352 + <div class="feed-item-icon feed-item-icon-orange">🔥</div> 353 + <div class="feed-item-info"> 354 + <div class="feed-item-name">What's Hot</div> 355 + <div class="feed-item-creator">by Bluesky</div> 356 + </div> 357 + <div class="feed-item-actions"> 358 + <button class="feed-pin-btn pinned" title="Unpin"> 359 + <svg viewBox="0 0 24 24" fill="currentColor" stroke="none"> 360 + <path 361 + d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" /> 362 + </svg> 363 + </button> 364 + </div> 365 + </div> 366 + 367 + <div class="feed-item"> 368 + <div class="feed-item-drag"> 369 + <svg 370 + viewBox="0 0 24 24" 371 + fill="none" 372 + stroke="currentColor" 373 + stroke-width="2" 374 + stroke-linecap="round" 375 + stroke-linejoin="round"> 376 + <line x1="3" y1="9" x2="21" y2="9" /> 377 + <line x1="3" y1="15" x2="21" y2="15" /> 378 + </svg> 379 + </div> 380 + <div class="feed-item-icon feed-item-icon-purple">🧪</div> 381 + <div class="feed-item-info"> 382 + <div class="feed-item-name">Science</div> 383 + <div class="feed-item-creator">by bossett.social</div> 384 + </div> 385 + <div class="feed-item-actions"> 386 + <button class="feed-pin-btn pinned" title="Unpin"> 387 + <svg viewBox="0 0 24 24" fill="currentColor" stroke="none"> 388 + <path 389 + d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" /> 390 + </svg> 391 + </button> 392 + </div> 393 + </div> 394 + 395 + <!-- Saved (Unpinned) Feeds --> 396 + <div class="feeds-section-header"> 397 + <span class="feeds-section-title">Saved Feeds</span> 398 + </div> 399 + 400 + <div class="feed-item"> 401 + <div style="width: 20px"></div> 402 + <div class="feed-item-icon feed-item-icon-green">📰</div> 403 + <div class="feed-item-info"> 404 + <div class="feed-item-name">News</div> 405 + <div class="feed-item-creator">by skyfeed.xyz</div> 406 + </div> 407 + <div class="feed-item-actions"> 408 + <button class="feed-pin-btn" title="Pin"> 409 + <svg 410 + viewBox="0 0 24 24" 411 + fill="none" 412 + stroke="currentColor" 413 + stroke-width="2" 414 + stroke-linecap="round" 415 + stroke-linejoin="round"> 416 + <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" /> 417 + <circle cx="12" cy="10" r="3" /> 418 + </svg> 419 + </button> 420 + <button class="feed-remove-btn" title="Remove"> 421 + <svg 422 + viewBox="0 0 24 24" 423 + fill="none" 424 + stroke="currentColor" 425 + stroke-width="2" 426 + stroke-linecap="round" 427 + stroke-linejoin="round"> 428 + <line x1="18" y1="6" x2="6" y2="18" /> 429 + <line x1="6" y1="6" x2="18" y2="18" /> 430 + </svg> 431 + </button> 432 + </div> 433 + </div> 434 + 435 + <div class="feed-item"> 436 + <div style="width: 20px"></div> 437 + <div class="feed-item-icon feed-item-icon-pink">🎨</div> 438 + <div class="feed-item-info"> 439 + <div class="feed-item-name">Art</div> 440 + <div class="feed-item-creator">by flicknow.xyz</div> 441 + </div> 442 + <div class="feed-item-actions"> 443 + <button class="feed-pin-btn" title="Pin"> 444 + <svg 445 + viewBox="0 0 24 24" 446 + fill="none" 447 + stroke="currentColor" 448 + stroke-width="2" 449 + stroke-linecap="round" 450 + stroke-linejoin="round"> 451 + <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" /> 452 + <circle cx="12" cy="10" r="3" /> 453 + </svg> 454 + </button> 455 + <button class="feed-remove-btn" title="Remove"> 456 + <svg 457 + viewBox="0 0 24 24" 458 + fill="none" 459 + stroke="currentColor" 460 + stroke-width="2" 461 + stroke-linecap="round" 462 + stroke-linejoin="round"> 463 + <line x1="18" y1="6" x2="6" y2="18" /> 464 + <line x1="6" y1="6" x2="18" y2="18" /> 465 + </svg> 466 + </button> 467 + </div> 468 + </div> 469 + 470 + <!-- Discover --> 471 + <div class="feeds-section-header"> 472 + <span class="feeds-section-title">Discover Feeds</span> 473 + <button class="feeds-section-action">See All</button> 474 + </div> 475 + 476 + <div class="discover-grid"> 477 + <div class="discover-card"> 478 + <div class="feed-item-icon feed-item-icon-teal">🌐</div> 479 + <div class="discover-card-info"> 480 + <div class="discover-card-name">Popular With Friends</div> 481 + <div class="discover-card-creator">by Bluesky</div> 482 + <div class="discover-card-desc">Posts liked by people you follow</div> 483 + <div class="discover-card-likes">❤️ 15.2K likes</div> 484 + </div> 485 + <button class="add-feed-btn">+ Add</button> 486 + </div> 487 + 488 + <div class="discover-card"> 489 + <div class="feed-item-icon feed-item-icon-blue">💻</div> 490 + <div class="discover-card-info"> 491 + <div class="discover-card-name">Tech & Dev</div> 492 + <div class="discover-card-creator">by devfeed.app</div> 493 + <div class="discover-card-desc">A curated mix of tech, programming, and dev culture posts</div> 494 + <div class="discover-card-likes">❤️ 8.3K likes</div> 495 + </div> 496 + <button class="add-feed-btn">+ Add</button> 497 + </div> 498 + 499 + <div class="discover-card"> 500 + <div class="feed-item-icon feed-item-icon-green">📸</div> 501 + <div class="discover-card-info"> 502 + <div class="discover-card-name">Photography</div> 503 + <div class="discover-card-creator">by photo.bsky.social</div> 504 + <div class="discover-card-desc">Beautiful photos from across the Bluesky network</div> 505 + <div class="discover-card-likes">❤️ 6.1K likes</div> 506 + </div> 507 + <button class="add-feed-btn">+ Add</button> 508 + </div> 509 + </div> 510 + </div> 511 + 512 + <!-- Bottom Navigation --> 513 + <nav class="nav-bar"> 514 + <a href="home.html" class="nav-item"> 515 + <svg 516 + viewBox="0 0 24 24" 517 + fill="none" 518 + stroke="currentColor" 519 + stroke-width="2" 520 + stroke-linecap="round" 521 + stroke-linejoin="round"> 522 + <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" /> 523 + <polyline points="9 22 9 12 15 12 15 22" /> 524 + </svg> 525 + <span>Home</span> 526 + </a> 527 + 528 + <a href="search.html" class="nav-item"> 529 + <svg 530 + viewBox="0 0 24 24" 531 + fill="none" 532 + stroke="currentColor" 533 + stroke-width="2" 534 + stroke-linecap="round" 535 + stroke-linejoin="round"> 536 + <circle cx="11" cy="11" r="8" /> 537 + <line x1="21" y1="21" x2="16.65" y2="16.65" /> 538 + </svg> 539 + <span>Search</span> 540 + </a> 541 + 542 + <a href="profile.html" class="nav-item"> 543 + <svg 544 + viewBox="0 0 24 24" 545 + fill="none" 546 + stroke="currentColor" 547 + stroke-width="2" 548 + stroke-linecap="round" 549 + stroke-linejoin="round"> 550 + <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" /> 551 + <circle cx="12" cy="7" r="4" /> 552 + </svg> 553 + <span>Profile</span> 554 + </a> 555 + 556 + <a href="settings.html" class="nav-item"> 557 + <svg 558 + viewBox="0 0 24 24" 559 + fill="none" 560 + stroke="currentColor" 561 + stroke-width="2" 562 + stroke-linecap="round" 563 + stroke-linejoin="round"> 564 + <circle cx="12" cy="12" r="3" /> 565 + <path 566 + d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" /> 567 + </svg> 568 + <span>Settings</span> 569 + </a> 570 + </nav> 571 + </div> 572 + 573 + <script> 574 + if (localStorage.getItem("theme") === "dark") { 575 + document.documentElement.setAttribute("data-theme", "dark"); 576 + } 577 + </script> 578 + </body> 579 + </html>
+495
docs/designs/home.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> 6 + <title>Home - Lazurite</title> 7 + <link rel="preconnect" href="https://fonts.googleapis.com"> 8 + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 9 + <link href="https://fonts.googleapis.com/css2?family=Lora:wght@400;500;600;700&display=swap" rel="stylesheet"> 10 + <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/geist@1.2.2/dist/fonts/geist-sans/style.css"> 11 + <link rel="stylesheet" href="styles.css"> 12 + <style> 13 + .feed-container { 14 + padding-bottom: 88px; 15 + } 16 + 17 + .compose-box { 18 + padding: 16px; 19 + border-bottom: 1px solid var(--border); 20 + background-color: var(--bg); 21 + } 22 + 23 + .compose-inner { 24 + display: flex; 25 + gap: 12px; 26 + } 27 + 28 + .compose-input-wrapper { 29 + flex: 1; 30 + } 31 + 32 + .compose-input { 33 + width: 100%; 34 + border: none; 35 + background: transparent; 36 + color: var(--text-primary); 37 + font-size: 16px; 38 + resize: none; 39 + outline: none; 40 + font-family: inherit; 41 + min-height: 24px; 42 + } 43 + 44 + .compose-input::placeholder { 45 + color: var(--text-muted); 46 + } 47 + 48 + .compose-actions { 49 + display: flex; 50 + justify-content: space-between; 51 + align-items: center; 52 + margin-top: 12px; 53 + padding-left: 60px; 54 + } 55 + 56 + .compose-tools { 57 + display: flex; 58 + gap: 8px; 59 + } 60 + 61 + .compose-tool { 62 + width: 36px; 63 + height: 36px; 64 + border-radius: 50%; 65 + border: none; 66 + background: transparent; 67 + color: var(--accent-primary); 68 + cursor: pointer; 69 + display: flex; 70 + align-items: center; 71 + justify-content: center; 72 + transition: background-color 0.2s ease; 73 + } 74 + 75 + .compose-tool:hover { 76 + background-color: rgba(0, 102, 255, 0.1); 77 + } 78 + 79 + .compose-tool svg { 80 + width: 20px; 81 + height: 20px; 82 + } 83 + 84 + .compose-submit { 85 + padding: 8px 20px; 86 + border-radius: 9999px; 87 + border: none; 88 + background-color: var(--accent-primary); 89 + color: white; 90 + font-weight: 600; 91 + font-size: 14px; 92 + cursor: pointer; 93 + transition: background-color 0.2s ease; 94 + } 95 + 96 + .compose-submit:hover { 97 + background-color: var(--accent-primary-hover); 98 + } 99 + 100 + .feed-tabs { 101 + display: flex; 102 + border-bottom: 1px solid var(--border); 103 + background-color: var(--bg); 104 + } 105 + 106 + .feed-tab { 107 + flex: 1; 108 + padding: 16px; 109 + text-align: center; 110 + font-weight: 600; 111 + font-size: 15px; 112 + color: var(--text-secondary); 113 + cursor: pointer; 114 + border-bottom: 2px solid transparent; 115 + transition: all 0.2s ease; 116 + background: none; 117 + border-top: none; 118 + border-left: none; 119 + border-right: none; 120 + } 121 + 122 + .feed-tab:hover { 123 + background-color: var(--surface); 124 + color: var(--text-primary); 125 + } 126 + 127 + .feed-tab.active { 128 + color: var(--text-primary); 129 + border-bottom-color: var(--accent-primary); 130 + } 131 + 132 + .post-facet-mention { 133 + color: var(--accent-primary); 134 + text-decoration: none; 135 + font-weight: 500; 136 + } 137 + 138 + .post-facet-mention:hover { 139 + text-decoration: underline; 140 + } 141 + 142 + .post-facet-hashtag { 143 + color: var(--accent-secondary); 144 + text-decoration: none; 145 + font-weight: 500; 146 + } 147 + 148 + .post-facet-hashtag:hover { 149 + text-decoration: underline; 150 + } 151 + 152 + .post-facet-link { 153 + color: var(--accent-primary); 154 + text-decoration: underline; 155 + } 156 + 157 + .post-embed { 158 + margin-top: 12px; 159 + border: 1px solid var(--border); 160 + border-radius: 12px; 161 + overflow: hidden; 162 + } 163 + 164 + .post-embed-image { 165 + width: 100%; 166 + height: 200px; 167 + background: linear-gradient(135deg, var(--surface) 0%, var(--surface-variant) 100%); 168 + display: flex; 169 + align-items: center; 170 + justify-content: center; 171 + color: var(--text-muted); 172 + } 173 + 174 + .post-embed-content { 175 + padding: 12px; 176 + } 177 + 178 + .post-embed-title { 179 + font-weight: 600; 180 + color: var(--text-primary); 181 + font-size: 14px; 182 + margin-bottom: 4px; 183 + } 184 + 185 + .post-embed-url { 186 + color: var(--text-muted); 187 + font-size: 12px; 188 + } 189 + </style> 190 + </head> 191 + <body> 192 + <div class="mobile-container"> 193 + 194 + <!-- Header --> 195 + <header class="header"> 196 + <h1 class="header-title">Home</h1> 197 + <button class="header-action">Settings</button> 198 + </header> 199 + 200 + <div class="feed-container"> 201 + 202 + <!-- Compose Box --> 203 + <div class="compose-box"> 204 + <div class="compose-inner"> 205 + <div class="avatar avatar-sm">JD</div> 206 + <div class="compose-input-wrapper"> 207 + <textarea class="compose-input" placeholder="What's on your mind?" rows="2"></textarea> 208 + </div> 209 + </div> 210 + <div class="compose-actions"> 211 + <div class="compose-tools"> 212 + <button class="compose-tool" title="Add image"> 213 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 214 + <rect x="3" y="3" width="18" height="18" rx="2" ry="2"/> 215 + <circle cx="8.5" cy="8.5" r="1.5"/> 216 + <polyline points="21 15 16 10 5 21"/> 217 + </svg> 218 + </button> 219 + <button class="compose-tool" title="Add GIF"> 220 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 221 + <text x="4" y="16" font-family="inherit" font-size="12" font-weight="bold" fill="currentColor">GIF</text> 222 + </svg> 223 + </button> 224 + <button class="compose-tool" title="Add poll"> 225 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 226 + <line x1="18" y1="20" x2="18" y2="10"/> 227 + <line x1="12" y1="20" x2="12" y2="4"/> 228 + <line x1="6" y1="20" x2="6" y2="14"/> 229 + </svg> 230 + </button> 231 + <button class="compose-tool" title="Emoji"> 232 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 233 + <circle cx="12" cy="12" r="10"/> 234 + <path d="M8 14s1.5 2 4 2 4-2 4-2"/> 235 + <line x1="9" y1="9" x2="9.01" y2="9"/> 236 + <line x1="15" y1="9" x2="15.01" y2="9"/> 237 + </svg> 238 + </button> 239 + </div> 240 + <button class="compose-submit">Post</button> 241 + </div> 242 + </div> 243 + 244 + <!-- Feed Tabs --> 245 + <div class="feed-tabs"> 246 + <button class="feed-tab active">Following</button> 247 + <button class="feed-tab">Discover</button> 248 + </div> 249 + 250 + <!-- Post 1 --> 251 + <article class="post-card"> 252 + <div class="post-header"> 253 + <div class="avatar">AS</div> 254 + <div class="post-author"> 255 + <div class="post-author-name">Alice Smith</div> 256 + <div class="post-author-handle">@alice.bsky.social · <span class="post-timestamp">2h</span></div> 257 + </div> 258 + </div> 259 + 260 + <div class="post-content"> 261 + Just launched my new project! 🚀 Check out the demo at <a href="#" class="post-facet-link">example.com</a>. Special thanks to <a href="#" class="post-facet-mention">@bob.bsky.social</a> for the help! <a href="#" class="post-facet-hashtag">#buildinpublic</a> <a href="#" class="post-facet-hashtag">#dev</a> 262 + </div> 263 + 264 + <div class="post-embed"> 265 + <div class="post-embed-image">[Project Screenshot]</div> 266 + <div class="post-embed-content"> 267 + <div class="post-embed-title">My Awesome Project</div> 268 + <div class="post-embed-url">example.com</div> 269 + </div> 270 + </div> 271 + 272 + <div class="post-actions"> 273 + <button class="post-action"> 274 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 275 + <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/> 276 + </svg> 277 + 12 278 + </button> 279 + <button class="post-action"> 280 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 281 + <polyline points="17 1 21 5 17 9"/> 282 + <path d="M3 11V9a4 4 0 0 1 4-4h14"/> 283 + <polyline points="7 23 3 19 7 15"/> 284 + <path d="M21 13v2a4 4 0 0 1-4 4H3"/> 285 + </svg> 286 + 5 287 + </button> 288 + <button class="post-action"> 289 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 290 + <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/> 291 + </svg> 292 + 48 293 + </button> 294 + <button class="post-action"> 295 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 296 + <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/> 297 + <polyline points="16 6 12 2 8 6"/> 298 + <line x1="12" y1="2" x2="12" y2="15"/> 299 + </svg> 300 + </button> 301 + </div> 302 + </article> 303 + 304 + <!-- Post 2 --> 305 + <article class="post-card"> 306 + <div class="post-header"> 307 + <div class="avatar">BJ</div> 308 + <div class="post-author"> 309 + <div class="post-author-name">Bob Johnson</div> 310 + <div class="post-author-handle">@bob.bsky.social · <span class="post-timestamp">4h</span></div> 311 + </div> 312 + </div> 313 + 314 + <div class="post-content"> 315 + Working on some exciting features for the next release! Here's a sneak peek of what's coming: improved search, better notifications, and dark mode support. <a href="#" class="post-facet-hashtag">#bluesky</a> <a href="#" class="post-facet-hashtag">#atproto</a> 316 + </div> 317 + 318 + <div class="post-actions"> 319 + <button class="post-action"> 320 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 321 + <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/> 322 + </svg> 323 + 8 324 + </button> 325 + <button class="post-action"> 326 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 327 + <polyline points="17 1 21 5 17 9"/> 328 + <path d="M3 11V9a4 4 0 0 1 4-4h14"/> 329 + <polyline points="7 23 3 19 7 15"/> 330 + <path d="M21 13v2a4 4 0 0 1-4 4H3"/> 331 + </svg> 332 + 24 333 + </button> 334 + <button class="post-action"> 335 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 336 + <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/> 337 + </svg> 338 + 156 339 + </button> 340 + <button class="post-action"> 341 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 342 + <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/> 343 + <polyline points="16 6 12 2 8 6"/> 344 + <line x1="12" y1="2" x2="12" y2="15"/> 345 + </svg> 346 + </button> 347 + </div> 348 + </article> 349 + 350 + <!-- Post 3 - Rich Facets --> 351 + <article class="post-card"> 352 + <div class="post-header"> 353 + <div class="avatar">CW</div> 354 + <div class="post-author"> 355 + <div class="post-author-name">Carol White</div> 356 + <div class="post-author-handle">@carol.dev · <span class="post-timestamp">6h</span></div> 357 + </div> 358 + </div> 359 + 360 + <div class="post-content"> 361 + The <a href="#" class="post-facet-mention">@atproto</a> team has been doing amazing work! Read their latest blog post about federation: <a href="#" class="post-facet-link">atproto.com/blog/federation</a> 362 + 363 + <br><br> 364 + 365 + Key highlights: 366 + • Self-hosting guides 367 + • PDS (Personal Data Server) setup 368 + • Relay infrastructure 369 + 370 + <br><br> 371 + 372 + <a href="#" class="post-facet-hashtag">#atprotocol</a> <a href="#" class="post-facet-hashtag">#decentralized</a> <a href="#" class="post-facet-hashtag">#socialweb</a> 373 + </div> 374 + 375 + <div class="post-actions"> 376 + <button class="post-action"> 377 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 378 + <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/> 379 + </svg> 380 + 34 381 + </button> 382 + <button class="post-action"> 383 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 384 + <polyline points="17 1 21 5 17 9"/> 385 + <path d="M3 11V9a4 4 0 0 1 4-4h14"/> 386 + <polyline points="7 23 3 19 7 15"/> 387 + <path d="M21 13v2a4 4 0 0 1-4 4H3"/> 388 + </svg> 389 + 12 390 + </button> 391 + <button class="post-action"> 392 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 393 + <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/> 394 + </svg> 395 + 289 396 + </button> 397 + <button class="post-action"> 398 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 399 + <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/> 400 + <polyline points="16 6 12 2 8 6"/> 401 + <line x1="12" y1="2" x2="12" y2="15"/> 402 + </svg> 403 + </button> 404 + </div> 405 + </article> 406 + 407 + <!-- Post 4 --> 408 + <article class="post-card"> 409 + <div class="post-header"> 410 + <div class="avatar">DM</div> 411 + <div class="post-author"> 412 + <div class="post-author-name">David Miller</div> 413 + <div class="post-author-handle">@davidm.bsky.social · <span class="post-timestamp">8h</span></div> 414 + </div> 415 + </div> 416 + 417 + <div class="post-content"> 418 + Beautiful sunset from my hike today! 🌅 <a href="#" class="post-facet-hashtag">#nature</a> <a href="#" class="post-facet-hashtag">#photography</a> <a href="#" class="post-facet-hashtag">#hiking</a> 419 + </div> 420 + 421 + <div class="post-embed"> 422 + <div class="post-embed-image">[Sunset Photo]</div> 423 + </div> 424 + 425 + <div class="post-actions"> 426 + <button class="post-action"> 427 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 428 + <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/> 429 + </svg> 430 + 6 431 + </button> 432 + <button class="post-action"> 433 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 434 + <polyline points="17 1 21 5 17 9"/> 435 + <path d="M3 11V9a4 4 0 0 1 4-4h14"/> 436 + <polyline points="7 23 3 19 7 15"/> 437 + <path d="M21 13v2a4 4 0 0 1-4 4H3"/> 438 + </svg> 439 + 18 440 + </button> 441 + <button class="post-action"> 442 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 443 + <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/> 444 + </svg> 445 + 423 446 + </button> 447 + <button class="post-action"> 448 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 449 + <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/> 450 + <polyline points="16 6 12 2 8 6"/> 451 + <line x1="12" y1="2" x2="12" y2="15"/> 452 + </svg> 453 + </button> 454 + </div> 455 + </article> 456 + 457 + </div> 458 + 459 + <!-- Bottom Navigation --> 460 + <nav class="nav-bar"> 461 + <a href="home.html" class="nav-item active"> 462 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 463 + <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/> 464 + <polyline points="9 22 9 12 15 12 15 22"/> 465 + </svg> 466 + <span>Home</span> 467 + </a> 468 + 469 + <a href="profile.html" class="nav-item"> 470 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 471 + <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/> 472 + <circle cx="12" cy="7" r="4"/> 473 + </svg> 474 + <span>Profile</span> 475 + </a> 476 + 477 + <a href="settings.html" class="nav-item"> 478 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 479 + <circle cx="12" cy="12" r="3"/> 480 + <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/> 481 + </svg> 482 + <span>Settings</span> 483 + </a> 484 + </nav> 485 + 486 + </div> 487 + 488 + <script> 489 + // Theme toggle functionality for demo 490 + if (localStorage.getItem('theme') === 'dark') { 491 + document.documentElement.setAttribute('data-theme', 'dark'); 492 + } 493 + </script> 494 + </body> 495 + </html>
+210
docs/designs/login.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> 6 + <title>Login - Lazurite</title> 7 + <link rel="preconnect" href="https://fonts.googleapis.com"> 8 + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 9 + <link href="https://fonts.googleapis.com/css2?family=Lora:wght@400;500;600;700&display=swap" rel="stylesheet"> 10 + <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/geist@1.2.2/dist/fonts/geist-sans/style.css"> 11 + <link rel="stylesheet" href="styles.css"> 12 + <style> 13 + .login-screen { 14 + min-height: 100vh; 15 + display: flex; 16 + flex-direction: column; 17 + justify-content: center; 18 + padding: 24px; 19 + background: linear-gradient(180deg, var(--bg) 0%, var(--surface) 100%); 20 + } 21 + 22 + .login-header { 23 + text-align: center; 24 + margin-bottom: 48px; 25 + } 26 + 27 + .logo { 28 + width: 80px; 29 + height: 80px; 30 + background: linear-gradient(135deg, var(--accent-primary) 0%, var(--accent-secondary) 100%); 31 + border-radius: 20px; 32 + margin: 0 auto 24px; 33 + display: flex; 34 + align-items: center; 35 + justify-content: center; 36 + box-shadow: 0 8px 32px rgba(0, 102, 255, 0.25); 37 + } 38 + 39 + .logo svg { 40 + width: 44px; 41 + height: 44px; 42 + color: white; 43 + } 44 + 45 + .app-name { 46 + font-size: 32px; 47 + font-weight: 700; 48 + color: var(--text-primary); 49 + margin-bottom: 8px; 50 + letter-spacing: -0.5px; 51 + } 52 + 53 + .app-tagline { 54 + color: var(--text-secondary); 55 + font-size: 15px; 56 + } 57 + 58 + .login-methods { 59 + display: flex; 60 + flex-direction: column; 61 + gap: 16px; 62 + margin-bottom: 32px; 63 + } 64 + 65 + .divider-with-text { 66 + display: flex; 67 + align-items: center; 68 + gap: 16px; 69 + margin: 24px 0; 70 + color: var(--text-muted); 71 + font-size: 13px; 72 + font-weight: 500; 73 + text-transform: uppercase; 74 + } 75 + 76 + .divider-with-text::before, 77 + .divider-with-text::after { 78 + content: ''; 79 + flex: 1; 80 + height: 1px; 81 + background-color: var(--border); 82 + } 83 + 84 + .debug-section { 85 + background-color: var(--surface); 86 + border-radius: 12px; 87 + padding: 20px; 88 + border: 1.5px dashed var(--border); 89 + } 90 + 91 + .debug-header { 92 + display: flex; 93 + align-items: center; 94 + gap: 8px; 95 + margin-bottom: 16px; 96 + } 97 + 98 + .debug-title { 99 + font-weight: 600; 100 + color: var(--text-primary); 101 + font-size: 15px; 102 + } 103 + 104 + .debug-form { 105 + display: flex; 106 + flex-direction: column; 107 + gap: 12px; 108 + } 109 + 110 + .input-group { 111 + display: flex; 112 + flex-direction: column; 113 + gap: 6px; 114 + } 115 + 116 + .input-label { 117 + font-size: 13px; 118 + font-weight: 500; 119 + color: var(--text-secondary); 120 + text-transform: uppercase; 121 + } 122 + 123 + .help-text { 124 + font-size: 12px; 125 + color: var(--text-muted); 126 + margin-top: 8px; 127 + text-align: center; 128 + } 129 + 130 + .login-footer { 131 + text-align: center; 132 + margin-top: auto; 133 + padding-top: 32px; 134 + } 135 + 136 + .terms-text { 137 + font-size: 12px; 138 + color: var(--text-muted); 139 + line-height: 1.6; 140 + } 141 + </style> 142 + </head> 143 + <body> 144 + <div class="mobile-container"> 145 + <div class="login-screen"> 146 + <!-- Logo & Header --> 147 + <div class="login-header"> 148 + <div class="logo"> 149 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 150 + <path d="M12 2L2 7l10 5 10-5-10-5z"/> 151 + <path d="M2 17l10 5 10-5"/> 152 + <path d="M2 12l10 5 10-5"/> 153 + </svg> 154 + </div> 155 + <h1 class="app-name">Lazurite</h1> 156 + <p class="app-tagline">Connect with BlueSky</p> 157 + </div> 158 + 159 + <!-- Login Methods --> 160 + <div class="login-methods"> 161 + <!-- OAuth 2.0 Login --> 162 + <button class="oauth-btn"> 163 + <svg viewBox="0 0 24 24" fill="currentColor"> 164 + <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/> 165 + </svg> 166 + Continue with BlueSky OAuth 167 + </button> 168 + 169 + <div class="divider-with-text">Or</div> 170 + 171 + <!-- Debug Mode - App Password --> 172 + <div class="debug-section"> 173 + <div class="debug-header"> 174 + <span class="debug-badge">Debug Mode</span> 175 + <span class="debug-title">App Password Login</span> 176 + </div> 177 + 178 + <form class="debug-form"> 179 + <div class="input-group"> 180 + <label class="input-label">Handle or DID</label> 181 + <input type="text" class="input" placeholder="@username.bsky.social"> 182 + </div> 183 + 184 + <div class="input-group"> 185 + <label class="input-label">App Password</label> 186 + <input type="password" class="input" placeholder="xxxx-xxxx-xxxx-xxxx"> 187 + </div> 188 + 189 + <button type="submit" class="btn btn-primary"> 190 + Sign In 191 + </button> 192 + </form> 193 + 194 + <p class="help-text"> 195 + App passwords can be generated in BlueSky Settings → App Passwords 196 + </p> 197 + </div> 198 + </div> 199 + 200 + <!-- Footer --> 201 + <div class="login-footer"> 202 + <p class="terms-text"> 203 + By continuing, you agree to BlueSky's<br> 204 + <a href="#" class="link">Terms of Service</a> and <a href="#" class="link">Privacy Policy</a> 205 + </p> 206 + </div> 207 + </div> 208 + </div> 209 + </body> 210 + </html>
+452
docs/designs/profile.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> 6 + <title>Profile - Lazurite</title> 7 + <link rel="preconnect" href="https://fonts.googleapis.com"> 8 + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 9 + <link href="https://fonts.googleapis.com/css2?family=Lora:wght@400;500;600;700&display=swap" rel="stylesheet"> 10 + <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/geist@1.2.2/dist/fonts/geist-sans/style.css"> 11 + <link rel="stylesheet" href="styles.css"> 12 + <style> 13 + .profile-container { 14 + padding-bottom: 88px; 15 + } 16 + 17 + .profile-header { 18 + background: linear-gradient(180deg, var(--surface) 0%, var(--bg) 100%); 19 + padding: 0 16px 16px; 20 + } 21 + 22 + .profile-cover { 23 + height: 120px; 24 + background: linear-gradient(135deg, var(--accent-primary) 0%, var(--accent-secondary) 100%); 25 + margin: 0 -16px 16px; 26 + border-radius: 0 0 16px 16px; 27 + } 28 + 29 + .profile-info { 30 + position: relative; 31 + } 32 + 33 + .profile-avatar { 34 + position: absolute; 35 + top: -50px; 36 + left: 0; 37 + width: 100px; 38 + height: 100px; 39 + border-radius: 50%; 40 + background-color: var(--surface); 41 + border: 4px solid var(--bg); 42 + display: flex; 43 + align-items: center; 44 + justify-content: center; 45 + font-size: 36px; 46 + font-weight: 700; 47 + color: var(--text-primary); 48 + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); 49 + } 50 + 51 + .profile-actions { 52 + display: flex; 53 + justify-content: flex-end; 54 + gap: 12px; 55 + margin-bottom: 16px; 56 + padding-top: 8px; 57 + } 58 + 59 + .profile-btn { 60 + padding: 8px 20px; 61 + border-radius: 9999px; 62 + font-size: 14px; 63 + font-weight: 600; 64 + cursor: pointer; 65 + transition: all 0.2s ease; 66 + border: 1.5px solid var(--border); 67 + background-color: var(--surface); 68 + color: var(--text-primary); 69 + } 70 + 71 + .profile-btn:hover { 72 + background-color: var(--surface-variant); 73 + } 74 + 75 + .profile-btn-primary { 76 + background-color: var(--text-primary); 77 + color: var(--bg); 78 + border-color: var(--text-primary); 79 + } 80 + 81 + .profile-btn-primary:hover { 82 + opacity: 0.9; 83 + } 84 + 85 + .profile-name { 86 + font-size: 22px; 87 + font-weight: 700; 88 + color: var(--text-primary); 89 + margin-top: 56px; 90 + margin-bottom: 4px; 91 + } 92 + 93 + .profile-handle { 94 + color: var(--text-secondary); 95 + font-size: 15px; 96 + margin-bottom: 12px; 97 + } 98 + 99 + .profile-bio { 100 + color: var(--text-primary); 101 + font-size: 15px; 102 + line-height: 1.5; 103 + margin-bottom: 16px; 104 + } 105 + 106 + .profile-meta { 107 + display: flex; 108 + flex-wrap: wrap; 109 + gap: 16px; 110 + margin-bottom: 16px; 111 + } 112 + 113 + .profile-meta-item { 114 + display: flex; 115 + align-items: center; 116 + gap: 6px; 117 + color: var(--text-secondary); 118 + font-size: 14px; 119 + } 120 + 121 + .profile-meta-item svg { 122 + width: 16px; 123 + height: 16px; 124 + color: var(--text-muted); 125 + } 126 + 127 + .profile-stats { 128 + display: flex; 129 + gap: 24px; 130 + } 131 + 132 + .profile-stat { 133 + color: var(--text-secondary); 134 + font-size: 14px; 135 + } 136 + 137 + .profile-stat strong { 138 + color: var(--text-primary); 139 + font-weight: 700; 140 + } 141 + 142 + .profile-tabs { 143 + display: flex; 144 + border-bottom: 1px solid var(--border); 145 + background-color: var(--bg); 146 + } 147 + 148 + .profile-tab { 149 + flex: 1; 150 + padding: 16px; 151 + text-align: center; 152 + font-weight: 600; 153 + font-size: 15px; 154 + color: var(--text-secondary); 155 + cursor: pointer; 156 + border-bottom: 2px solid transparent; 157 + transition: all 0.2s ease; 158 + background: none; 159 + border-top: none; 160 + border-left: none; 161 + border-right: none; 162 + } 163 + 164 + .profile-tab:hover { 165 + background-color: var(--surface); 166 + color: var(--text-primary); 167 + } 168 + 169 + .profile-tab.active { 170 + color: var(--text-primary); 171 + border-bottom-color: var(--accent-primary); 172 + } 173 + 174 + .posts-empty { 175 + padding: 48px 24px; 176 + text-align: center; 177 + } 178 + 179 + .posts-empty-icon { 180 + width: 64px; 181 + height: 64px; 182 + color: var(--text-muted); 183 + margin-bottom: 16px; 184 + } 185 + 186 + .posts-empty-title { 187 + font-size: 18px; 188 + font-weight: 600; 189 + color: var(--text-primary); 190 + margin-bottom: 8px; 191 + } 192 + 193 + .posts-empty-text { 194 + color: var(--text-secondary); 195 + font-size: 14px; 196 + } 197 + </style> 198 + </head> 199 + <body> 200 + <div class="mobile-container"> 201 + 202 + <!-- Header --> 203 + <header class="header"> 204 + <button class="header-action">← Back</button> 205 + <h1 class="header-title">John Doe</h1> 206 + <button class="header-action">⋯</button> 207 + </header> 208 + 209 + <div class="profile-container"> 210 + 211 + <!-- Profile Header --> 212 + <div class="profile-header"> 213 + <div class="profile-cover"></div> 214 + 215 + <div class="profile-info"> 216 + <div class="profile-avatar">JD</div> 217 + 218 + <div class="profile-actions"> 219 + <button class="profile-btn">Edit Profile</button> 220 + </div> 221 + 222 + <h2 class="profile-name">John Doe</h2> 223 + <div class="profile-handle">@johndoe.bsky.social</div> 224 + 225 + <p class="profile-bio"> 226 + Building things on the web 🚀 • Open source enthusiast • Coffee addict ☕ • 227 + <a href="#" class="link">johndoe.dev</a> 228 + </p> 229 + 230 + <div class="profile-meta"> 231 + <div class="profile-meta-item"> 232 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 233 + <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/> 234 + <circle cx="12" cy="10" r="3"/> 235 + </svg> 236 + San Francisco, CA 237 + </div> 238 + 239 + <div class="profile-meta-item"> 240 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 241 + <rect x="3" y="4" width="18" height="18" rx="2" ry="2"/> 242 + <line x1="16" y1="2" x2="16" y2="6"/> 243 + <line x1="8" y1="2" x2="8" y2="6"/> 244 + <line x1="3" y1="10" x2="21" y2="10"/> 245 + </svg> 246 + Joined March 2023 247 + </div> 248 + </div> 249 + 250 + <div class="profile-stats"> 251 + <div class="profile-stat"><strong>256</strong> Following</div> 252 + <div class="profile-stat"><strong>1.2K</strong> Followers</div> 253 + <div class="profile-stat"><strong>482</strong> Posts</div> 254 + </div> 255 + </div> 256 + </div> 257 + 258 + <!-- Profile Tabs --> 259 + <div class="profile-tabs"> 260 + <button class="profile-tab active">Posts</button> 261 + <button class="profile-tab">Replies</button> 262 + <button class="profile-tab">Media</button> 263 + <button class="profile-tab">Likes</button> 264 + </div> 265 + 266 + <!-- User Posts --> 267 + 268 + <!-- Post 1 --> 269 + <article class="post-card"> 270 + <div class="post-header"> 271 + <div class="avatar">JD</div> 272 + <div class="post-author"> 273 + <div class="post-author-name">John Doe</div> 274 + <div class="post-author-handle">@johndoe.bsky.social · <span class="post-timestamp">2h</span></div> 275 + </div> 276 + </div> 277 + 278 + <div class="post-content"> 279 + Finally shipped the new feature I've been working on! 🎉 Check it out and let me know what you think. Always open to feedback! <a href="#" class="post-facet-hashtag">#buildinpublic</a> <a href="#" class="post-facet-hashtag">#indiehacker</a> 280 + </div> 281 + 282 + <div class="post-actions"> 283 + <button class="post-action"> 284 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 285 + <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/> 286 + </svg> 287 + 8 288 + </button> 289 + <button class="post-action"> 290 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 291 + <polyline points="17 1 21 5 17 9"/> 292 + <path d="M3 11V9a4 4 0 0 1 4-4h14"/> 293 + <polyline points="7 23 3 19 7 15"/> 294 + <path d="M21 13v2a4 4 0 0 1-4 4H3"/> 295 + </svg> 296 + 3 297 + </button> 298 + <button class="post-action"> 299 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 300 + <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/> 301 + </svg> 302 + 42 303 + </button> 304 + <button class="post-action"> 305 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 306 + <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/> 307 + <polyline points="16 6 12 2 8 6"/> 308 + <line x1="12" y1="2" x2="12" y2="15"/> 309 + </svg> 310 + </button> 311 + </div> 312 + </article> 313 + 314 + <!-- Post 2 --> 315 + <article class="post-card"> 316 + <div class="post-header"> 317 + <div class="avatar">JD</div> 318 + <div class="post-author"> 319 + <div class="post-author-name">John Doe</div> 320 + <div class="post-author-handle">@johndoe.bsky.social · <span class="post-timestamp">1d</span></div> 321 + </div> 322 + </div> 323 + 324 + <div class="post-content"> 325 + Really enjoying the <a href="#" class="post-facet-mention">@bluesky</a> community so far. Great conversations happening here! Looking forward to seeing how the platform evolves. <a href="#" class="post-facet-hashtag">#bluesky</a> <a href="#" class="post-facet-hashtag">#socialmedia</a> 326 + </div> 327 + 328 + <div class="post-actions"> 329 + <button class="post-action"> 330 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 331 + <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/> 332 + </svg> 333 + 15 334 + </button> 335 + <button class="post-action"> 336 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 337 + <polyline points="17 1 21 5 17 9"/> 338 + <path d="M3 11V9a4 4 0 0 1 4-4h14"/> 339 + <polyline points="7 23 3 19 7 15"/> 340 + <path d="M21 13v2a4 4 0 0 1-4 4H3"/> 341 + </svg> 342 + 7 343 + </button> 344 + <button class="post-action"> 345 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 346 + <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/> 347 + </svg> 348 + 89 349 + </button> 350 + <button class="post-action"> 351 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 352 + <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/> 353 + <polyline points="16 6 12 2 8 6"/> 354 + <line x1="12" y1="2" x2="12" y2="15"/> 355 + </svg> 356 + </button> 357 + </div> 358 + </article> 359 + 360 + <!-- Post 3 - With Embedded Link --> 361 + <article class="post-card"> 362 + <div class="post-header"> 363 + <div class="avatar">JD</div> 364 + <div class="post-author"> 365 + <div class="post-author-name">John Doe</div> 366 + <div class="post-author-handle">@johndoe.bsky.social · <span class="post-timestamp">3d</span></div> 367 + </div> 368 + </div> 369 + 370 + <div class="post-content"> 371 + Great read on the future of decentralized social networks by <a href="#" class="post-facet-mention">@protocol</a> <a href="#" class="post-facet-hashtag">#atprotocol</a> <a href="#" class="post-facet-hashtag">#web3</a> 372 + </div> 373 + 374 + <div class="post-embed"> 375 + <div class="post-embed-image">[Article Preview Image]</div> 376 + <div class="post-embed-content"> 377 + <div class="post-embed-title">The Future of Decentralized Social Networks</div> 378 + <div class="post-embed-url">protocol.com/blog/future-decentralized-social</div> 379 + </div> 380 + </div> 381 + 382 + <div class="post-actions"> 383 + <button class="post-action"> 384 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 385 + <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/> 386 + </svg> 387 + 22 388 + </button> 389 + <button class="post-action"> 390 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 391 + <polyline points="17 1 21 5 17 9"/> 392 + <path d="M3 11V9a4 4 0 0 1 4-4h14"/> 393 + <polyline points="7 23 3 19 7 15"/> 394 + <path d="M21 13v2a4 4 0 0 1-4 4H3"/> 395 + </svg> 396 + 14 397 + </button> 398 + <button class="post-action"> 399 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 400 + <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/> 401 + </svg> 402 + 156 403 + </button> 404 + <button class="post-action"> 405 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 406 + <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/> 407 + <polyline points="16 6 12 2 8 6"/> 408 + <line x1="12" y1="2" x2="12" y2="15"/> 409 + </svg> 410 + </button> 411 + </div> 412 + </article> 413 + 414 + </div> 415 + 416 + <!-- Bottom Navigation --> 417 + <nav class="nav-bar"> 418 + <a href="home.html" class="nav-item"> 419 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 420 + <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/> 421 + <polyline points="9 22 9 12 15 12 15 22"/> 422 + </svg> 423 + <span>Home</span> 424 + </a> 425 + 426 + <a href="profile.html" class="nav-item active"> 427 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 428 + <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/> 429 + <circle cx="12" cy="7" r="4"/> 430 + </svg> 431 + <span>Profile</span> 432 + </a> 433 + 434 + <a href="settings.html" class="nav-item"> 435 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 436 + <circle cx="12" cy="12" r="3"/> 437 + <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/> 438 + </svg> 439 + <span>Settings</span> 440 + </a> 441 + </nav> 442 + 443 + </div> 444 + 445 + <script> 446 + // Theme toggle functionality for demo 447 + if (localStorage.getItem('theme') === 'dark') { 448 + document.documentElement.setAttribute('data-theme', 'dark'); 449 + } 450 + </script> 451 + </body> 452 + </html>
+742
docs/designs/search.html
··· 1 + <!doctype html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8" /> 5 + <meta 6 + name="viewport" 7 + content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" 8 + /> 9 + <title>Search - Lazurite</title> 10 + <link rel="preconnect" href="https://fonts.googleapis.com" /> 11 + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> 12 + <link 13 + href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Condensed:wght@400;500;600&family=IBM+Plex+Sans:wght@400;500;600;700&display=swap" 14 + rel="stylesheet" 15 + /> 16 + <link rel="stylesheet" href="styles.css" /> 17 + <style> 18 + .search-container { 19 + padding-bottom: 88px; 20 + } 21 + 22 + .search-bar { 23 + padding: 12px 16px; 24 + border-bottom: 1px solid var(--border); 25 + display: flex; 26 + align-items: center; 27 + gap: 12px; 28 + } 29 + 30 + .search-input-wrapper { 31 + flex: 1; 32 + position: relative; 33 + } 34 + 35 + .search-input-wrapper svg { 36 + position: absolute; 37 + left: 12px; 38 + top: 50%; 39 + transform: translateY(-50%); 40 + width: 18px; 41 + height: 18px; 42 + color: var(--text-muted); 43 + } 44 + 45 + .search-input { 46 + width: 100%; 47 + padding: 10px 12px 10px 38px; 48 + border: 1px solid var(--border); 49 + border-radius: 9999px; 50 + background-color: var(--surface); 51 + color: var(--text-primary); 52 + font-size: 15px; 53 + transition: border-color 0.2s ease; 54 + } 55 + 56 + .search-input:focus { 57 + outline: none; 58 + border-color: var(--accent-primary); 59 + } 60 + 61 + .search-input::placeholder { 62 + color: var(--text-muted); 63 + } 64 + 65 + .search-cancel { 66 + background: none; 67 + border: none; 68 + color: var(--accent-primary); 69 + font-size: 15px; 70 + font-weight: 500; 71 + cursor: pointer; 72 + white-space: nowrap; 73 + } 74 + 75 + /* Tabs */ 76 + .search-tabs { 77 + display: flex; 78 + border-bottom: 1px solid var(--border); 79 + background-color: var(--bg); 80 + } 81 + 82 + .search-tab { 83 + flex: 1; 84 + padding: 14px; 85 + text-align: center; 86 + font-weight: 600; 87 + font-size: 15px; 88 + color: var(--text-secondary); 89 + cursor: pointer; 90 + border-bottom: 2px solid transparent; 91 + transition: all 0.2s ease; 92 + background: none; 93 + border-top: none; 94 + border-left: none; 95 + border-right: none; 96 + } 97 + 98 + .search-tab:hover { 99 + background-color: var(--surface); 100 + color: var(--text-primary); 101 + } 102 + 103 + .search-tab.active { 104 + color: var(--text-primary); 105 + border-bottom-color: var(--accent-primary); 106 + } 107 + 108 + /* Sort Toggle */ 109 + .search-sort { 110 + display: flex; 111 + align-items: center; 112 + gap: 8px; 113 + padding: 10px 16px; 114 + border-bottom: 1px solid var(--border); 115 + } 116 + 117 + .search-sort-label { 118 + font-size: 13px; 119 + color: var(--text-secondary); 120 + } 121 + 122 + .sort-toggle { 123 + display: flex; 124 + background-color: var(--surface); 125 + border-radius: 8px; 126 + border: 1px solid var(--border); 127 + overflow: hidden; 128 + } 129 + 130 + .sort-option { 131 + padding: 6px 14px; 132 + font-size: 13px; 133 + font-weight: 500; 134 + border: none; 135 + background: none; 136 + color: var(--text-secondary); 137 + cursor: pointer; 138 + transition: all 0.2s ease; 139 + } 140 + 141 + .sort-option.active { 142 + background-color: var(--accent-primary); 143 + color: white; 144 + } 145 + 146 + /* Search History */ 147 + .search-history-header { 148 + display: flex; 149 + align-items: center; 150 + justify-content: space-between; 151 + padding: 12px 16px; 152 + } 153 + 154 + .search-history-title { 155 + font-size: 14px; 156 + font-weight: 600; 157 + color: var(--text-primary); 158 + } 159 + 160 + .search-history-clear { 161 + background: none; 162 + border: none; 163 + color: var(--accent-primary); 164 + font-size: 13px; 165 + font-weight: 500; 166 + cursor: pointer; 167 + } 168 + 169 + .history-item { 170 + display: flex; 171 + align-items: center; 172 + justify-content: space-between; 173 + padding: 12px 16px; 174 + cursor: pointer; 175 + transition: background-color 0.2s ease; 176 + } 177 + 178 + .history-item:hover { 179 + background-color: var(--surface); 180 + } 181 + 182 + .history-item-left { 183 + display: flex; 184 + align-items: center; 185 + gap: 12px; 186 + } 187 + 188 + .history-icon { 189 + width: 20px; 190 + height: 20px; 191 + color: var(--text-muted); 192 + } 193 + 194 + .history-query { 195 + font-size: 15px; 196 + color: var(--text-primary); 197 + } 198 + 199 + .history-meta { 200 + font-size: 12px; 201 + color: var(--text-muted); 202 + } 203 + 204 + .history-delete { 205 + width: 20px; 206 + height: 20px; 207 + color: var(--text-muted); 208 + background: none; 209 + border: none; 210 + cursor: pointer; 211 + transition: color 0.2s ease; 212 + } 213 + 214 + .history-delete:hover { 215 + color: var(--accent-error); 216 + } 217 + 218 + /* Actor Results */ 219 + .actor-result { 220 + display: flex; 221 + align-items: center; 222 + gap: 12px; 223 + padding: 12px 16px; 224 + border-bottom: 1px solid var(--border); 225 + cursor: pointer; 226 + transition: background-color 0.2s ease; 227 + } 228 + 229 + .actor-result:hover { 230 + background-color: var(--surface); 231 + } 232 + 233 + .actor-result-info { 234 + flex: 1; 235 + min-width: 0; 236 + } 237 + 238 + .actor-result-name { 239 + font-weight: 600; 240 + font-size: 15px; 241 + color: var(--text-primary); 242 + } 243 + 244 + .actor-result-handle { 245 + font-size: 14px; 246 + color: var(--text-secondary); 247 + } 248 + 249 + .actor-result-bio { 250 + font-size: 13px; 251 + color: var(--text-muted); 252 + margin-top: 2px; 253 + display: -webkit-box; 254 + line-clamp: 1; 255 + -webkit-line-clamp: 1; 256 + -webkit-box-orient: vertical; 257 + overflow: hidden; 258 + } 259 + 260 + .follow-btn { 261 + padding: 6px 16px; 262 + border-radius: 9999px; 263 + border: 1.5px solid var(--accent-primary); 264 + background: none; 265 + color: var(--accent-primary); 266 + font-size: 13px; 267 + font-weight: 600; 268 + cursor: pointer; 269 + transition: all 0.2s ease; 270 + flex-shrink: 0; 271 + } 272 + 273 + .follow-btn:hover { 274 + background-color: var(--accent-primary); 275 + color: white; 276 + } 277 + 278 + /* Typeahead */ 279 + .typeahead-section { 280 + border-bottom: 1px solid var(--border); 281 + } 282 + 283 + .typeahead-label { 284 + padding: 8px 16px; 285 + font-size: 12px; 286 + font-weight: 600; 287 + color: var(--text-muted); 288 + text-transform: uppercase; 289 + letter-spacing: 0.5px; 290 + } 291 + 292 + .post-facet-mention { 293 + color: var(--accent-primary); 294 + text-decoration: none; 295 + font-weight: 500; 296 + } 297 + 298 + .post-facet-hashtag { 299 + color: var(--accent-secondary); 300 + text-decoration: none; 301 + font-weight: 500; 302 + } 303 + </style> 304 + </head> 305 + <body> 306 + <div class="mobile-container"> 307 + <!-- Search Bar (replaces standard header) --> 308 + <div class="search-bar"> 309 + <div class="search-input-wrapper"> 310 + <svg 311 + viewBox="0 0 24 24" 312 + fill="none" 313 + stroke="currentColor" 314 + stroke-width="2" 315 + stroke-linecap="round" 316 + stroke-linejoin="round" 317 + > 318 + <circle cx="11" cy="11" r="8" /> 319 + <line x1="21" y1="21" x2="16.65" y2="16.65" /> 320 + </svg> 321 + <input 322 + class="search-input" 323 + type="text" 324 + placeholder="Search posts or people" 325 + value="atproto" 326 + /> 327 + </div> 328 + <button class="search-cancel">Cancel</button> 329 + </div> 330 + 331 + <!-- Tabs --> 332 + <div class="search-tabs"> 333 + <button class="search-tab active">Posts</button> 334 + <button class="search-tab">People</button> 335 + </div> 336 + 337 + <!-- Sort --> 338 + <div class="search-sort"> 339 + <span class="search-sort-label">Sort by</span> 340 + <div class="sort-toggle"> 341 + <button class="sort-option active">Top</button> 342 + <button class="sort-option">Latest</button> 343 + </div> 344 + </div> 345 + 346 + <div class="search-container"> 347 + <!-- Post Results --> 348 + <article class="post-card"> 349 + <div class="post-header"> 350 + <div class="avatar">PB</div> 351 + <div class="post-author"> 352 + <div class="post-author-name">Paul Frazee</div> 353 + <div class="post-author-handle"> 354 + @pfrazee.com · <span class="post-timestamp">3h</span> 355 + </div> 356 + </div> 357 + </div> 358 + <div class="post-content"> 359 + We just shipped a major update to the 360 + <a href="#" class="post-facet-mention">@atproto</a> federation code. 361 + Self-hosting your own PDS is now easier than ever. 362 + <a href="#" class="post-facet-hashtag">#atproto</a> 363 + <a href="#" class="post-facet-hashtag">#decentralized</a> 364 + </div> 365 + <div class="post-actions"> 366 + <button class="post-action"> 367 + <svg 368 + viewBox="0 0 24 24" 369 + fill="none" 370 + stroke="currentColor" 371 + stroke-width="2" 372 + stroke-linecap="round" 373 + stroke-linejoin="round" 374 + > 375 + <path 376 + d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" 377 + /> 378 + </svg> 379 + 67 380 + </button> 381 + <button class="post-action"> 382 + <svg 383 + viewBox="0 0 24 24" 384 + fill="none" 385 + stroke="currentColor" 386 + stroke-width="2" 387 + stroke-linecap="round" 388 + stroke-linejoin="round" 389 + > 390 + <polyline points="17 1 21 5 17 9" /> 391 + <path d="M3 11V9a4 4 0 0 1 4-4h14" /> 392 + <polyline points="7 23 3 19 7 15" /> 393 + <path d="M21 13v2a4 4 0 0 1-4 4H3" /> 394 + </svg> 395 + 34 396 + </button> 397 + <button class="post-action"> 398 + <svg 399 + viewBox="0 0 24 24" 400 + fill="none" 401 + stroke="currentColor" 402 + stroke-width="2" 403 + stroke-linecap="round" 404 + stroke-linejoin="round" 405 + > 406 + <path 407 + d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" 408 + /> 409 + </svg> 410 + 512 411 + </button> 412 + <button class="post-action"> 413 + <svg 414 + viewBox="0 0 24 24" 415 + fill="none" 416 + stroke="currentColor" 417 + stroke-width="2" 418 + stroke-linecap="round" 419 + stroke-linejoin="round" 420 + > 421 + <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8" /> 422 + <polyline points="16 6 12 2 8 6" /> 423 + <line x1="12" y1="2" x2="12" y2="15" /> 424 + </svg> 425 + </button> 426 + </div> 427 + </article> 428 + 429 + <article class="post-card"> 430 + <div class="post-header"> 431 + <div class="avatar">JK</div> 432 + <div class="post-author"> 433 + <div class="post-author-name">Jake Gold</div> 434 + <div class="post-author-handle"> 435 + @jake.bsky.social · <span class="post-timestamp">6h</span> 436 + </div> 437 + </div> 438 + </div> 439 + <div class="post-content"> 440 + The <a href="#" class="post-facet-hashtag">#atproto</a> ecosystem is 441 + growing fast. More and more third-party apps popping up every week. 442 + The open protocol approach is really paying off 🌐 443 + </div> 444 + <div class="post-actions"> 445 + <button class="post-action"> 446 + <svg 447 + viewBox="0 0 24 24" 448 + fill="none" 449 + stroke="currentColor" 450 + stroke-width="2" 451 + stroke-linecap="round" 452 + stroke-linejoin="round" 453 + > 454 + <path 455 + d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" 456 + /> 457 + </svg> 458 + 23 459 + </button> 460 + <button class="post-action"> 461 + <svg 462 + viewBox="0 0 24 24" 463 + fill="none" 464 + stroke="currentColor" 465 + stroke-width="2" 466 + stroke-linecap="round" 467 + stroke-linejoin="round" 468 + > 469 + <polyline points="17 1 21 5 17 9" /> 470 + <path d="M3 11V9a4 4 0 0 1 4-4h14" /> 471 + <polyline points="7 23 3 19 7 15" /> 472 + <path d="M21 13v2a4 4 0 0 1-4 4H3" /> 473 + </svg> 474 + 11 475 + </button> 476 + <button class="post-action"> 477 + <svg 478 + viewBox="0 0 24 24" 479 + fill="none" 480 + stroke="currentColor" 481 + stroke-width="2" 482 + stroke-linecap="round" 483 + stroke-linejoin="round" 484 + > 485 + <path 486 + d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" 487 + /> 488 + </svg> 489 + 178 490 + </button> 491 + <button class="post-action"> 492 + <svg 493 + viewBox="0 0 24 24" 494 + fill="none" 495 + stroke="currentColor" 496 + stroke-width="2" 497 + stroke-linecap="round" 498 + stroke-linejoin="round" 499 + > 500 + <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8" /> 501 + <polyline points="16 6 12 2 8 6" /> 502 + <line x1="12" y1="2" x2="12" y2="15" /> 503 + </svg> 504 + </button> 505 + </div> 506 + </article> 507 + 508 + <!-- People Results (shown when People tab is active — rendered inline for preview) --> 509 + <div class="typeahead-section" style="display: none"> 510 + <div class="typeahead-label">People</div> 511 + 512 + <div class="actor-result"> 513 + <div class="avatar">AT</div> 514 + <div class="actor-result-info"> 515 + <div class="actor-result-name">AT Protocol</div> 516 + <div class="actor-result-handle">@atproto.com</div> 517 + <div class="actor-result-bio"> 518 + The AT Protocol – social networking technology created by 519 + Bluesky 520 + </div> 521 + </div> 522 + <button class="follow-btn">Follow</button> 523 + </div> 524 + 525 + <div class="actor-result"> 526 + <div class="avatar">BS</div> 527 + <div class="actor-result-info"> 528 + <div class="actor-result-name">Bluesky</div> 529 + <div class="actor-result-handle">@bsky.app</div> 530 + <div class="actor-result-bio"> 531 + Building a social internet. Join us at bsky.app 532 + </div> 533 + </div> 534 + <button class="follow-btn">Follow</button> 535 + </div> 536 + </div> 537 + 538 + <!-- Search History (shown when search input is empty) --> 539 + <div style="display: none"> 540 + <div class="search-history-header"> 541 + <span class="search-history-title">Recent Searches</span> 542 + <button class="search-history-clear">Clear All</button> 543 + </div> 544 + 545 + <div class="history-item"> 546 + <div class="history-item-left"> 547 + <svg 548 + class="history-icon" 549 + viewBox="0 0 24 24" 550 + fill="none" 551 + stroke="currentColor" 552 + stroke-width="2" 553 + stroke-linecap="round" 554 + stroke-linejoin="round" 555 + > 556 + <polyline points="12 8 12 12 14 14" /> 557 + <circle cx="12" cy="12" r="10" /> 558 + </svg> 559 + <div> 560 + <div class="history-query">flutter bloc</div> 561 + <div class="history-meta">Posts · 2 hours ago</div> 562 + </div> 563 + </div> 564 + <button class="history-delete"> 565 + <svg 566 + viewBox="0 0 24 24" 567 + fill="none" 568 + stroke="currentColor" 569 + stroke-width="2" 570 + stroke-linecap="round" 571 + stroke-linejoin="round" 572 + > 573 + <line x1="18" y1="6" x2="6" y2="18" /> 574 + <line x1="6" y1="6" x2="18" y2="18" /> 575 + </svg> 576 + </button> 577 + </div> 578 + 579 + <div class="history-item"> 580 + <div class="history-item-left"> 581 + <svg 582 + class="history-icon" 583 + viewBox="0 0 24 24" 584 + fill="none" 585 + stroke="currentColor" 586 + stroke-width="2" 587 + stroke-linecap="round" 588 + stroke-linejoin="round" 589 + > 590 + <polyline points="12 8 12 12 14 14" /> 591 + <circle cx="12" cy="12" r="10" /> 592 + </svg> 593 + <div> 594 + <div class="history-query">@pfrazee.com</div> 595 + <div class="history-meta">People · Yesterday</div> 596 + </div> 597 + </div> 598 + <button class="history-delete"> 599 + <svg 600 + viewBox="0 0 24 24" 601 + fill="none" 602 + stroke="currentColor" 603 + stroke-width="2" 604 + stroke-linecap="round" 605 + stroke-linejoin="round" 606 + > 607 + <line x1="18" y1="6" x2="6" y2="18" /> 608 + <line x1="6" y1="6" x2="18" y2="18" /> 609 + </svg> 610 + </button> 611 + </div> 612 + 613 + <div class="history-item"> 614 + <div class="history-item-left"> 615 + <svg 616 + class="history-icon" 617 + viewBox="0 0 24 24" 618 + fill="none" 619 + stroke="currentColor" 620 + stroke-width="2" 621 + stroke-linecap="round" 622 + stroke-linejoin="round" 623 + > 624 + <polyline points="12 8 12 12 14 14" /> 625 + <circle cx="12" cy="12" r="10" /> 626 + </svg> 627 + <div> 628 + <div class="history-query">#decentralized</div> 629 + <div class="history-meta">Posts · 3 days ago</div> 630 + </div> 631 + </div> 632 + <button class="history-delete"> 633 + <svg 634 + viewBox="0 0 24 24" 635 + fill="none" 636 + stroke="currentColor" 637 + stroke-width="2" 638 + stroke-linecap="round" 639 + stroke-linejoin="round" 640 + > 641 + <line x1="18" y1="6" x2="6" y2="18" /> 642 + <line x1="6" y1="6" x2="18" y2="18" /> 643 + </svg> 644 + </button> 645 + </div> 646 + </div> 647 + </div> 648 + 649 + <!-- Bottom Navigation --> 650 + <nav class="nav-bar"> 651 + <a href="home.html" class="nav-item"> 652 + <svg 653 + viewBox="0 0 24 24" 654 + fill="none" 655 + stroke="currentColor" 656 + stroke-width="2" 657 + stroke-linecap="round" 658 + stroke-linejoin="round" 659 + > 660 + <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" /> 661 + <polyline points="9 22 9 12 15 12 15 22" /> 662 + </svg> 663 + <span>Home</span> 664 + </a> 665 + 666 + <a href="search.html" class="nav-item active"> 667 + <svg 668 + viewBox="0 0 24 24" 669 + fill="none" 670 + stroke="currentColor" 671 + stroke-width="2" 672 + stroke-linecap="round" 673 + stroke-linejoin="round" 674 + > 675 + <circle cx="11" cy="11" r="8" /> 676 + <line x1="21" y1="21" x2="16.65" y2="16.65" /> 677 + </svg> 678 + <span>Search</span> 679 + </a> 680 + 681 + <a href="profile.html" class="nav-item"> 682 + <svg 683 + viewBox="0 0 24 24" 684 + fill="none" 685 + stroke="currentColor" 686 + stroke-width="2" 687 + stroke-linecap="round" 688 + stroke-linejoin="round" 689 + > 690 + <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" /> 691 + <circle cx="12" cy="7" r="4" /> 692 + </svg> 693 + <span>Profile</span> 694 + </a> 695 + 696 + <a href="settings.html" class="nav-item"> 697 + <svg 698 + viewBox="0 0 24 24" 699 + fill="none" 700 + stroke="currentColor" 701 + stroke-width="2" 702 + stroke-linecap="round" 703 + stroke-linejoin="round" 704 + > 705 + <circle cx="12" cy="12" r="3" /> 706 + <path 707 + d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" 708 + /> 709 + </svg> 710 + <span>Settings</span> 711 + </a> 712 + </nav> 713 + </div> 714 + 715 + <script> 716 + if (localStorage.getItem("theme") === "dark") { 717 + document.documentElement.setAttribute("data-theme", "dark"); 718 + } 719 + 720 + // Tab switching demo 721 + document.querySelectorAll(".search-tab").forEach((tab) => { 722 + tab.addEventListener("click", () => { 723 + document 724 + .querySelectorAll(".search-tab") 725 + .forEach((t) => t.classList.remove("active")); 726 + tab.classList.add("active"); 727 + 728 + const isPeople = tab.textContent === "People"; 729 + document 730 + .querySelectorAll(".post-card") 731 + .forEach((el) => (el.style.display = isPeople ? "none" : "block")); 732 + document.querySelector(".search-sort").style.display = isPeople 733 + ? "none" 734 + : "flex"; 735 + document.querySelector(".typeahead-section").style.display = isPeople 736 + ? "block" 737 + : "none"; 738 + }); 739 + }); 740 + </script> 741 + </body> 742 + </html>
+528
docs/designs/settings.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> 6 + <title>Settings - Lazurite</title> 7 + <link rel="preconnect" href="https://fonts.googleapis.com"> 8 + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 9 + <link href="https://fonts.googleapis.com/css2?family=Lora:wght@400;500;600;700&display=swap" rel="stylesheet"> 10 + <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/geist@1.2.2/dist/fonts/geist-sans/style.css"> 11 + <link rel="stylesheet" href="styles.css"> 12 + <style> 13 + .settings-container { 14 + padding-bottom: 88px; 15 + } 16 + 17 + .settings-section { 18 + margin-bottom: 24px; 19 + } 20 + 21 + .settings-section-title { 22 + padding: 16px; 23 + font-size: 13px; 24 + font-weight: 600; 25 + color: var(--text-muted); 26 + text-transform: uppercase; 27 + letter-spacing: 0.5px; 28 + } 29 + 30 + .settings-group { 31 + background-color: var(--surface); 32 + border-top: 1px solid var(--border); 33 + border-bottom: 1px solid var(--border); 34 + } 35 + 36 + .theme-selector { 37 + padding: 16px; 38 + display: flex; 39 + gap: 16px; 40 + justify-content: center; 41 + } 42 + 43 + .theme-option { 44 + display: flex; 45 + flex-direction: column; 46 + align-items: center; 47 + gap: 8px; 48 + cursor: pointer; 49 + padding: 12px; 50 + border-radius: 12px; 51 + transition: background-color 0.2s ease; 52 + } 53 + 54 + .theme-option:hover { 55 + background-color: var(--surface-variant); 56 + } 57 + 58 + .theme-option.active { 59 + background-color: var(--surface-variant); 60 + } 61 + 62 + .theme-option-preview { 63 + width: 80px; 64 + height: 80px; 65 + border-radius: 12px; 66 + border: 2px solid var(--border); 67 + overflow: hidden; 68 + position: relative; 69 + transition: all 0.2s ease; 70 + } 71 + 72 + .theme-option:hover .theme-option-preview, 73 + .theme-option.active .theme-option-preview { 74 + border-color: var(--accent-primary); 75 + transform: scale(1.02); 76 + } 77 + 78 + .theme-preview-light { 79 + background: linear-gradient(135deg, #ffffff 50%, #f4f4f4 50%); 80 + } 81 + 82 + .theme-preview-dark { 83 + background: linear-gradient(135deg, #161616 50%, #262626 50%); 84 + } 85 + 86 + .theme-option-check { 87 + position: absolute; 88 + bottom: 4px; 89 + right: 4px; 90 + width: 20px; 91 + height: 20px; 92 + background-color: var(--accent-primary); 93 + border-radius: 50%; 94 + display: flex; 95 + align-items: center; 96 + justify-content: center; 97 + opacity: 0; 98 + transition: opacity 0.2s ease; 99 + } 100 + 101 + .theme-option.active .theme-option-check { 102 + opacity: 1; 103 + } 104 + 105 + .theme-option-check svg { 106 + width: 12px; 107 + height: 12px; 108 + color: white; 109 + } 110 + 111 + .theme-option-label { 112 + font-size: 14px; 113 + font-weight: 500; 114 + color: var(--text-primary); 115 + } 116 + 117 + .auto-theme-toggle { 118 + padding: 16px; 119 + border-top: 1px solid var(--border); 120 + display: flex; 121 + align-items: center; 122 + justify-content: space-between; 123 + } 124 + 125 + .auto-theme-info { 126 + display: flex; 127 + flex-direction: column; 128 + gap: 4px; 129 + } 130 + 131 + .auto-theme-title { 132 + font-size: 15px; 133 + font-weight: 500; 134 + color: var(--text-primary); 135 + } 136 + 137 + .auto-theme-subtitle { 138 + font-size: 13px; 139 + color: var(--text-secondary); 140 + } 141 + 142 + .settings-item-danger { 143 + color: var(--accent-error); 144 + } 145 + 146 + .settings-item-danger .settings-item-title { 147 + color: var(--accent-error); 148 + } 149 + 150 + .app-version { 151 + text-align: center; 152 + padding: 24px; 153 + color: var(--text-muted); 154 + font-size: 13px; 155 + } 156 + 157 + .user-card { 158 + display: flex; 159 + align-items: center; 160 + gap: 12px; 161 + padding: 16px; 162 + background-color: var(--surface); 163 + border-top: 1px solid var(--border); 164 + border-bottom: 1px solid var(--border); 165 + } 166 + 167 + .user-card-avatar { 168 + width: 48px; 169 + height: 48px; 170 + border-radius: 50%; 171 + background-color: var(--surface-variant); 172 + display: flex; 173 + align-items: center; 174 + justify-content: center; 175 + font-size: 18px; 176 + font-weight: 600; 177 + color: var(--text-primary); 178 + } 179 + 180 + .user-card-info { 181 + flex: 1; 182 + } 183 + 184 + .user-card-name { 185 + font-weight: 600; 186 + color: var(--text-primary); 187 + font-size: 15px; 188 + } 189 + 190 + .user-card-handle { 191 + color: var(--text-secondary); 192 + font-size: 14px; 193 + } 194 + 195 + .user-card-arrow { 196 + color: var(--text-muted); 197 + } 198 + 199 + .user-card-arrow svg { 200 + width: 20px; 201 + height: 20px; 202 + } 203 + </style> 204 + </head> 205 + <body> 206 + <div class="mobile-container"> 207 + 208 + <!-- Header --> 209 + <header class="header"> 210 + <h1 class="header-title">Settings</h1> 211 + <button class="header-action" onclick="logout()">Log Out</button> 212 + </header> 213 + 214 + <div class="settings-container"> 215 + 216 + <!-- User Card --> 217 + <a href="profile.html" class="user-card"> 218 + <div class="user-card-avatar">JD</div> 219 + <div class="user-card-info"> 220 + <div class="user-card-name">John Doe</div> 221 + <div class="user-card-handle">@johndoe.bsky.social</div> 222 + </div> 223 + <div class="user-card-arrow"> 224 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 225 + <polyline points="9 18 15 12 9 6"/> 226 + </svg> 227 + </div> 228 + </a> 229 + 230 + <!-- Appearance Section --> 231 + <div class="settings-section"> 232 + <div class="settings-section-title">Appearance</div> 233 + 234 + <div class="settings-group"> 235 + <!-- Theme Selector --> 236 + <div class="theme-selector"> 237 + <div class="theme-option active" data-theme="light" onclick="setTheme('light')"> 238 + <div class="theme-option-preview theme-preview-light"> 239 + <div class="theme-option-check"> 240 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"> 241 + <polyline points="20 6 9 17 4 12"/> 242 + </svg> 243 + </div> 244 + </div> 245 + <span class="theme-option-label">Light</span> 246 + </div> 247 + 248 + <div class="theme-option" data-theme="dark" onclick="setTheme('dark')"> 249 + <div class="theme-option-preview theme-preview-dark"> 250 + <div class="theme-option-check"> 251 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"> 252 + <polyline points="20 6 9 17 4 12"/> 253 + </svg> 254 + </div> 255 + </div> 256 + <span class="theme-option-label">Dark</span> 257 + </div> 258 + </div> 259 + 260 + <!-- Auto Theme Toggle --> 261 + <div class="auto-theme-toggle"> 262 + <div class="auto-theme-info"> 263 + <div class="auto-theme-title">Auto</div> 264 + <div class="auto-theme-subtitle">Follow system theme</div> 265 + </div> 266 + <div class="toggle" id="auto-theme-toggle" onclick="toggleAutoTheme()"> 267 + <div class="toggle-thumb"></div> 268 + </div> 269 + </div> 270 + </div> 271 + </div> 272 + 273 + <!-- Account Section --> 274 + <div class="settings-section"> 275 + <div class="settings-section-title">Account</div> 276 + 277 + <div class="settings-group"> 278 + <div class="settings-item"> 279 + <div class="settings-item-left"> 280 + <svg class="settings-item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 281 + <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/> 282 + <circle cx="12" cy="7" r="4"/> 283 + </svg> 284 + <div class="settings-item-content"> 285 + <div class="settings-item-title">Edit Profile</div> 286 + <div class="settings-item-subtitle">Name, bio, avatar</div> 287 + </div> 288 + </div> 289 + <div class="settings-item-right"> 290 + <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 291 + <polyline points="9 18 15 12 9 6"/> 292 + </svg> 293 + </div> 294 + </div> 295 + 296 + <div class="settings-item"> 297 + <div class="settings-item-left"> 298 + <svg class="settings-item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 299 + <rect x="3" y="11" width="18" height="11" rx="2" ry="2"/> 300 + <path d="M7 11V7a5 5 0 0 1 10 0v4"/> 301 + </svg> 302 + <div class="settings-item-content"> 303 + <div class="settings-item-title">Privacy</div> 304 + <div class="settings-item-subtitle">Visibility settings</div> 305 + </div> 306 + </div> 307 + <div class="settings-item-right"> 308 + <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 309 + <polyline points="9 18 15 12 9 6"/> 310 + </svg> 311 + </div> 312 + </div> 313 + </div> 314 + </div> 315 + 316 + <!-- Notifications Section --> 317 + <div class="settings-section"> 318 + <div class="settings-section-title">Notifications</div> 319 + 320 + <div class="settings-group"> 321 + <div class="settings-item"> 322 + <div class="settings-item-left"> 323 + <svg class="settings-item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 324 + <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/> 325 + <path d="M13.73 21a2 2 0 0 1-3.46 0"/> 326 + </svg> 327 + <div class="settings-item-content"> 328 + <div class="settings-item-title">Push Notifications</div> 329 + </div> 330 + </div> 331 + <div class="settings-item-right"> 332 + <div class="toggle active"> 333 + <div class="toggle-thumb"></div> 334 + </div> 335 + </div> 336 + </div> 337 + 338 + <div class="settings-item"> 339 + <div class="settings-item-left"> 340 + <svg class="settings-item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 341 + <path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/> 342 + <polyline points="22,6 12,13 2,6"/> 343 + </svg> 344 + <div class="settings-item-content"> 345 + <div class="settings-item-title">Email Notifications</div> 346 + </div> 347 + </div> 348 + <div class="settings-item-right"> 349 + <div class="toggle"> 350 + <div class="toggle-thumb"></div> 351 + </div> 352 + </div> 353 + </div> 354 + </div> 355 + </div> 356 + 357 + <!-- About Section --> 358 + <div class="settings-section"> 359 + <div class="settings-section-title">About</div> 360 + 361 + <div class="settings-group"> 362 + <div class="settings-item"> 363 + <div class="settings-item-left"> 364 + <svg class="settings-item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 365 + <circle cx="12" cy="12" r="10"/> 366 + <line x1="12" y1="16" x2="12" y2="12"/> 367 + <line x1="12" y1="8" x2="12.01" y2="8"/> 368 + </svg> 369 + <div class="settings-item-content"> 370 + <div class="settings-item-title">Help & Support</div> 371 + </div> 372 + </div> 373 + <div class="settings-item-right"> 374 + <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 375 + <polyline points="9 18 15 12 9 6"/> 376 + </svg> 377 + </div> 378 + </div> 379 + 380 + <div class="settings-item"> 381 + <div class="settings-item-left"> 382 + <svg class="settings-item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 383 + <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/> 384 + </svg> 385 + <div class="settings-item-content"> 386 + <div class="settings-item-title">Privacy Policy</div> 387 + </div> 388 + </div> 389 + <div class="settings-item-right"> 390 + <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 391 + <polyline points="9 18 15 12 9 6"/> 392 + </svg> 393 + </div> 394 + </div> 395 + </div> 396 + </div> 397 + 398 + <!-- Danger Zone --> 399 + <div class="settings-section"> 400 + <div class="settings-section-title">Danger Zone</div> 401 + 402 + <div class="settings-group"> 403 + <div class="settings-item settings-item-danger" onclick="logout()"> 404 + <div class="settings-item-left"> 405 + <svg class="settings-item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color: var(--accent-error);"> 406 + <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/> 407 + <polyline points="16 17 21 12 16 7"/> 408 + <line x1="21" y1="12" x2="9" y2="12"/> 409 + </svg> 410 + <div class="settings-item-content"> 411 + <div class="settings-item-title">Log Out</div> 412 + </div> 413 + </div> 414 + </div> 415 + </div> 416 + </div> 417 + 418 + <!-- App Version --> 419 + <div class="app-version"> 420 + Lazurite v1.0.0 (Phase 1) 421 + </div> 422 + 423 + </div> 424 + 425 + <!-- Bottom Navigation --> 426 + <nav class="nav-bar"> 427 + <a href="home.html" class="nav-item"> 428 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 429 + <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/> 430 + <polyline points="9 22 9 12 15 12 15 22"/> 431 + </svg> 432 + <span>Home</span> 433 + </a> 434 + 435 + <a href="profile.html" class="nav-item"> 436 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 437 + <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/> 438 + <circle cx="12" cy="7" r="4"/> 439 + </svg> 440 + <span>Profile</span> 441 + </a> 442 + 443 + <a href="settings.html" class="nav-item active"> 444 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 445 + <circle cx="12" cy="12" r="3"/> 446 + <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/> 447 + </svg> 448 + <span>Settings</span> 449 + </a> 450 + </nav> 451 + 452 + </div> 453 + 454 + <script> 455 + // Theme management 456 + function setTheme(theme) { 457 + // Update UI 458 + document.querySelectorAll('.theme-option').forEach(el => { 459 + el.classList.remove('active'); 460 + }); 461 + document.querySelector(`[data-theme="${theme}"]`).classList.add('active'); 462 + 463 + // Apply theme 464 + if (theme === 'dark') { 465 + document.documentElement.setAttribute('data-theme', 'dark'); 466 + localStorage.setItem('theme', 'dark'); 467 + } else { 468 + document.documentElement.removeAttribute('data-theme'); 469 + localStorage.setItem('theme', 'light'); 470 + } 471 + 472 + // Disable auto theme 473 + document.getElementById('auto-theme-toggle').classList.remove('active'); 474 + localStorage.removeItem('auto-theme'); 475 + } 476 + 477 + function toggleAutoTheme() { 478 + const toggle = document.getElementById('auto-theme-toggle'); 479 + toggle.classList.toggle('active'); 480 + 481 + if (toggle.classList.contains('active')) { 482 + localStorage.setItem('auto-theme', 'true'); 483 + // Check system preference 484 + if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { 485 + document.documentElement.setAttribute('data-theme', 'dark'); 486 + localStorage.setItem('theme', 'dark'); 487 + } else { 488 + document.documentElement.removeAttribute('data-theme'); 489 + localStorage.setItem('theme', 'light'); 490 + } 491 + } else { 492 + localStorage.removeItem('auto-theme'); 493 + } 494 + } 495 + 496 + function logout() { 497 + if (confirm('Are you sure you want to log out?')) { 498 + localStorage.removeItem('theme'); 499 + localStorage.removeItem('auto-theme'); 500 + window.location.href = 'login.html'; 501 + } 502 + } 503 + 504 + // Initialize theme on load 505 + (function() { 506 + const savedTheme = localStorage.getItem('theme'); 507 + const autoTheme = localStorage.getItem('auto-theme'); 508 + 509 + if (autoTheme === 'true') { 510 + document.getElementById('auto-theme-toggle').classList.add('active'); 511 + } 512 + 513 + if (savedTheme === 'dark') { 514 + document.documentElement.setAttribute('data-theme', 'dark'); 515 + document.querySelectorAll('.theme-option').forEach(el => { 516 + el.classList.remove('active'); 517 + }); 518 + document.querySelector('[data-theme="dark"]').classList.add('active'); 519 + } else { 520 + document.querySelectorAll('.theme-option').forEach(el => { 521 + el.classList.remove('active'); 522 + }); 523 + document.querySelector('[data-theme="light"]').classList.add('active'); 524 + } 525 + })(); 526 + </script> 527 + </body> 528 + </html>
+571
docs/designs/styles.css
··· 1 + /* Oxocarbon Theme Variables */ 2 + :root { 3 + /* Oxocarbon Light Palette */ 4 + --oxo-light-bg: #ffffff; 5 + --oxo-light-surface: #f4f4f4; 6 + --oxo-light-surface-variant: #e0e0e0; 7 + --oxo-light-border: #d1d1d1; 8 + --oxo-light-text-primary: #161616; 9 + --oxo-light-text-secondary: #525252; 10 + --oxo-light-text-muted: #8d8d8d; 11 + 12 + /* Oxocarbon Dark Palette */ 13 + --oxo-dark-bg: #161616; 14 + --oxo-dark-surface: #262626; 15 + --oxo-dark-surface-variant: #393939; 16 + --oxo-dark-border: #525252; 17 + --oxo-dark-text-primary: #f4f4f4; 18 + --oxo-dark-text-secondary: #c6c6c6; 19 + --oxo-dark-text-muted: #a8a8a8; 20 + 21 + /* Accent Colors (BlueSky inspired) */ 22 + --accent-primary: #0066ff; 23 + --accent-primary-hover: #0052cc; 24 + --accent-secondary: #0ea5e9; 25 + --accent-success: #22c55e; 26 + --accent-error: #ef4444; 27 + --accent-warning: #f59e0b; 28 + 29 + /* Default to light theme */ 30 + --bg: var(--oxo-light-bg); 31 + --surface: var(--oxo-light-surface); 32 + --surface-variant: var(--oxo-light-surface-variant); 33 + --border: var(--oxo-light-border); 34 + --text-primary: var(--oxo-light-text-primary); 35 + --text-secondary: var(--oxo-light-text-secondary); 36 + --text-muted: var(--oxo-light-text-muted); 37 + } 38 + 39 + /* Dark theme */ 40 + [data-theme="dark"] { 41 + --bg: var(--oxo-dark-bg); 42 + --surface: var(--oxo-dark-surface); 43 + --surface-variant: var(--oxo-dark-surface-variant); 44 + --border: var(--oxo-dark-border); 45 + --text-primary: var(--oxo-dark-text-primary); 46 + --text-secondary: var(--oxo-dark-text-secondary); 47 + --text-muted: var(--oxo-dark-text-muted); 48 + } 49 + 50 + /* Base Reset */ 51 + *, *::before, *::after { 52 + box-sizing: border-box; 53 + margin: 0; 54 + padding: 0; 55 + } 56 + 57 + html { 58 + font-size: 16px; 59 + -webkit-font-smoothing: antialiased; 60 + -moz-osx-font-smoothing: grayscale; 61 + } 62 + 63 + /* Font Variables */ 64 + :root { 65 + --font-heading: 'Lora', Georgia, 'Times New Roman', serif; 66 + --font-body: 'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; 67 + --font-mono: 'JetBrains Mono', 'IBM Plex Mono', 'Fira Code', monospace; 68 + } 69 + 70 + body { 71 + font-family: var(--font-body); 72 + background-color: var(--bg); 73 + color: var(--text-primary); 74 + line-height: 1.5; 75 + min-height: 100vh; 76 + } 77 + 78 + /* Typography */ 79 + h1, h2, h3, h4, h5, h6 { 80 + font-family: var(--font-heading); 81 + font-weight: 600; 82 + color: var(--text-primary); 83 + } 84 + 85 + p { 86 + color: var(--text-secondary); 87 + } 88 + 89 + /* Mobile Container */ 90 + .mobile-container { 91 + max-width: 414px; 92 + margin: 0 auto; 93 + min-height: 100vh; 94 + background-color: var(--bg); 95 + position: relative; 96 + border-left: 1px solid var(--border); 97 + border-right: 1px solid var(--border); 98 + } 99 + 100 + @media (max-width: 414px) { 101 + .mobile-container { 102 + border: none; 103 + } 104 + } 105 + 106 + /* Navigation Bar */ 107 + .nav-bar { 108 + position: fixed; 109 + bottom: 0; 110 + left: 50%; 111 + transform: translateX(-50%); 112 + width: 100%; 113 + max-width: 414px; 114 + background-color: var(--surface); 115 + border-top: 1px solid var(--border); 116 + display: flex; 117 + justify-content: space-around; 118 + padding: 12px 0 24px; 119 + z-index: 100; 120 + } 121 + 122 + .nav-item { 123 + display: flex; 124 + flex-direction: column; 125 + align-items: center; 126 + gap: 4px; 127 + padding: 8px 16px; 128 + color: var(--text-secondary); 129 + text-decoration: none; 130 + transition: color 0.2s ease; 131 + } 132 + 133 + .nav-item:hover, 134 + .nav-item.active { 135 + color: var(--accent-primary); 136 + } 137 + 138 + .nav-item svg { 139 + width: 24px; 140 + height: 24px; 141 + } 142 + 143 + .nav-item span { 144 + font-size: 12px; 145 + font-weight: 500; 146 + } 147 + 148 + /* Header */ 149 + .header { 150 + position: sticky; 151 + top: 0; 152 + background-color: var(--bg); 153 + border-bottom: 1px solid var(--border); 154 + padding: 12px 16px; 155 + z-index: 50; 156 + display: flex; 157 + align-items: center; 158 + justify-content: space-between; 159 + } 160 + 161 + .header-title { 162 + font-size: 18px; 163 + font-weight: 600; 164 + } 165 + 166 + .header-action { 167 + background: none; 168 + border: none; 169 + color: var(--accent-primary); 170 + font-size: 14px; 171 + font-weight: 600; 172 + cursor: pointer; 173 + padding: 8px 12px; 174 + } 175 + 176 + /* Buttons */ 177 + .btn { 178 + display: inline-flex; 179 + align-items: center; 180 + justify-content: center; 181 + gap: 8px; 182 + padding: 12px 24px; 183 + border-radius: 9999px; 184 + font-size: 15px; 185 + font-weight: 600; 186 + cursor: pointer; 187 + transition: all 0.2s ease; 188 + border: none; 189 + width: 100%; 190 + } 191 + 192 + .btn-primary { 193 + background-color: var(--accent-primary); 194 + color: white; 195 + } 196 + 197 + .btn-primary:hover { 198 + background-color: var(--accent-primary-hover); 199 + } 200 + 201 + .btn-secondary { 202 + background-color: var(--surface); 203 + color: var(--text-primary); 204 + border: 1px solid var(--border); 205 + } 206 + 207 + .btn-secondary:hover { 208 + background-color: var(--surface-variant); 209 + } 210 + 211 + .btn-outline { 212 + background-color: transparent; 213 + color: var(--accent-primary); 214 + border: 1.5px solid var(--accent-primary); 215 + } 216 + 217 + .btn-outline:hover { 218 + background-color: rgba(0, 102, 255, 0.1); 219 + } 220 + 221 + /* Cards */ 222 + .card { 223 + background-color: var(--surface); 224 + border-radius: 12px; 225 + padding: 16px; 226 + border: 1px solid var(--border); 227 + } 228 + 229 + /* Inputs */ 230 + .input { 231 + width: 100%; 232 + padding: 12px 16px; 233 + border: 1px solid var(--border); 234 + border-radius: 8px; 235 + background-color: var(--surface); 236 + color: var(--text-primary); 237 + font-size: 15px; 238 + transition: border-color 0.2s ease; 239 + } 240 + 241 + .input:focus { 242 + outline: none; 243 + border-color: var(--accent-primary); 244 + } 245 + 246 + .input::placeholder { 247 + color: var(--text-muted); 248 + } 249 + 250 + /* Divider */ 251 + .divider { 252 + height: 1px; 253 + background-color: var(--border); 254 + margin: 16px 0; 255 + } 256 + 257 + /* Avatar */ 258 + .avatar { 259 + width: 48px; 260 + height: 48px; 261 + border-radius: 50%; 262 + background-color: var(--surface-variant); 263 + display: flex; 264 + align-items: center; 265 + justify-content: center; 266 + font-weight: 600; 267 + color: var(--text-secondary); 268 + flex-shrink: 0; 269 + } 270 + 271 + .avatar-sm { 272 + width: 40px; 273 + height: 40px; 274 + font-size: 14px; 275 + } 276 + 277 + .avatar-lg { 278 + width: 80px; 279 + height: 80px; 280 + font-size: 28px; 281 + } 282 + 283 + /* Post Card */ 284 + .post-card { 285 + padding: 16px; 286 + border-bottom: 1px solid var(--border); 287 + background-color: var(--bg); 288 + } 289 + 290 + .post-header { 291 + display: flex; 292 + gap: 12px; 293 + margin-bottom: 12px; 294 + } 295 + 296 + .post-author { 297 + flex: 1; 298 + } 299 + 300 + .post-author-name { 301 + font-weight: 600; 302 + color: var(--text-primary); 303 + font-size: 15px; 304 + } 305 + 306 + .post-author-handle { 307 + color: var(--text-secondary); 308 + font-size: 14px; 309 + } 310 + 311 + .post-timestamp { 312 + color: var(--text-muted); 313 + font-size: 13px; 314 + } 315 + 316 + .post-content { 317 + color: var(--text-primary); 318 + font-size: 15px; 319 + line-height: 1.5; 320 + margin-bottom: 12px; 321 + } 322 + 323 + .post-facet { 324 + color: var(--accent-primary); 325 + text-decoration: none; 326 + } 327 + 328 + .post-facet:hover { 329 + text-decoration: underline; 330 + } 331 + 332 + .post-actions { 333 + display: flex; 334 + gap: 24px; 335 + } 336 + 337 + .post-action { 338 + display: flex; 339 + align-items: center; 340 + gap: 6px; 341 + color: var(--text-secondary); 342 + font-size: 13px; 343 + background: none; 344 + border: none; 345 + cursor: pointer; 346 + transition: color 0.2s ease; 347 + } 348 + 349 + .post-action:hover { 350 + color: var(--accent-primary); 351 + } 352 + 353 + .post-action svg { 354 + width: 20px; 355 + height: 20px; 356 + } 357 + 358 + /* Settings Items */ 359 + .settings-item { 360 + display: flex; 361 + align-items: center; 362 + justify-content: space-between; 363 + padding: 16px; 364 + border-bottom: 1px solid var(--border); 365 + cursor: pointer; 366 + transition: background-color 0.2s ease; 367 + } 368 + 369 + .settings-item:hover { 370 + background-color: var(--surface); 371 + } 372 + 373 + .settings-item-left { 374 + display: flex; 375 + align-items: center; 376 + gap: 12px; 377 + } 378 + 379 + .settings-item-icon { 380 + width: 24px; 381 + height: 24px; 382 + color: var(--text-secondary); 383 + } 384 + 385 + .settings-item-content { 386 + display: flex; 387 + flex-direction: column; 388 + } 389 + 390 + .settings-item-title { 391 + font-weight: 500; 392 + color: var(--text-primary); 393 + font-size: 15px; 394 + } 395 + 396 + .settings-item-subtitle { 397 + color: var(--text-secondary); 398 + font-size: 13px; 399 + } 400 + 401 + .settings-item-right { 402 + display: flex; 403 + align-items: center; 404 + gap: 8px; 405 + } 406 + 407 + /* Toggle Switch */ 408 + .toggle { 409 + width: 48px; 410 + height: 28px; 411 + background-color: var(--surface-variant); 412 + border-radius: 14px; 413 + position: relative; 414 + cursor: pointer; 415 + transition: background-color 0.3s ease; 416 + } 417 + 418 + .toggle.active { 419 + background-color: var(--accent-primary); 420 + } 421 + 422 + .toggle-thumb { 423 + position: absolute; 424 + top: 2px; 425 + left: 2px; 426 + width: 24px; 427 + height: 24px; 428 + background-color: white; 429 + border-radius: 50%; 430 + transition: transform 0.3s ease; 431 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 432 + } 433 + 434 + .toggle.active .toggle-thumb { 435 + transform: translateX(20px); 436 + } 437 + 438 + /* Theme Preview */ 439 + .theme-preview { 440 + width: 40px; 441 + height: 40px; 442 + border-radius: 8px; 443 + border: 2px solid var(--border); 444 + cursor: pointer; 445 + transition: all 0.2s ease; 446 + } 447 + 448 + .theme-preview:hover, 449 + .theme-preview.active { 450 + border-color: var(--accent-primary); 451 + transform: scale(1.05); 452 + } 453 + 454 + .theme-preview-light { 455 + background: linear-gradient(135deg, #ffffff 50%, #f4f4f4 50%); 456 + } 457 + 458 + .theme-preview-dark { 459 + background: linear-gradient(135deg, #161616 50%, #262626 50%); 460 + } 461 + 462 + /* Scrollbar Styling */ 463 + ::-webkit-scrollbar { 464 + width: 6px; 465 + } 466 + 467 + ::-webkit-scrollbar-track { 468 + background: var(--surface); 469 + } 470 + 471 + ::-webkit-scrollbar-thumb { 472 + background: var(--surface-variant); 473 + border-radius: 3px; 474 + } 475 + 476 + ::-webkit-scrollbar-thumb:hover { 477 + background: var(--border); 478 + } 479 + 480 + /* Safe Area for Mobile */ 481 + .safe-area-bottom { 482 + padding-bottom: 88px; 483 + } 484 + 485 + /* Screen Content Padding */ 486 + .screen-content { 487 + padding: 16px; 488 + } 489 + 490 + /* Debug Badge */ 491 + .debug-badge { 492 + display: inline-flex; 493 + align-items: center; 494 + gap: 4px; 495 + padding: 4px 8px; 496 + background-color: var(--accent-warning); 497 + color: #161616; 498 + font-size: 11px; 499 + font-weight: 600; 500 + border-radius: 4px; 501 + text-transform: uppercase; 502 + } 503 + 504 + /* OAuth Provider Button */ 505 + .oauth-btn { 506 + display: flex; 507 + align-items: center; 508 + justify-content: center; 509 + gap: 12px; 510 + padding: 14px 24px; 511 + border-radius: 9999px; 512 + border: 1.5px solid var(--border); 513 + background-color: var(--surface); 514 + color: var(--text-primary); 515 + font-size: 15px; 516 + font-weight: 600; 517 + cursor: pointer; 518 + transition: all 0.2s ease; 519 + width: 100%; 520 + } 521 + 522 + .oauth-btn:hover { 523 + background-color: var(--surface-variant); 524 + border-color: var(--text-secondary); 525 + } 526 + 527 + .oauth-btn svg { 528 + width: 20px; 529 + height: 20px; 530 + } 531 + 532 + /* Link */ 533 + .link { 534 + color: var(--accent-primary); 535 + text-decoration: none; 536 + font-weight: 500; 537 + } 538 + 539 + .link:hover { 540 + text-decoration: underline; 541 + } 542 + 543 + /* Empty State */ 544 + .empty-state { 545 + display: flex; 546 + flex-direction: column; 547 + align-items: center; 548 + justify-content: center; 549 + padding: 48px 24px; 550 + text-align: center; 551 + } 552 + 553 + .empty-state-icon { 554 + width: 64px; 555 + height: 64px; 556 + color: var(--text-muted); 557 + margin-bottom: 16px; 558 + } 559 + 560 + .empty-state-title { 561 + font-size: 18px; 562 + font-weight: 600; 563 + color: var(--text-primary); 564 + margin-bottom: 8px; 565 + } 566 + 567 + .empty-state-text { 568 + color: var(--text-secondary); 569 + font-size: 14px; 570 + max-width: 280px; 571 + }
+208
docs/specs/phase-1.md
··· 1 + # Phase 1 2 + 3 + BLoC for state management via `flutter_bloc`. Each feature gets its own 4 + Bloc/Cubit—keep them small and focused. Use `BlocProvider` for DI, 5 + `BlocBuilder` / `BlocSelector` for granular rebuilds, and `BlocListener` for 6 + one-shot side effects (navigation, snackbars). All state classes must be 7 + immutable with `copyWith()`. Use `HydratedBloc` where session persistence is 8 + needed (e.g. theme preference). 9 + 10 + Drift (formerly Moor) for SQLite persistence. Type-safe, reactive (stream-based 11 + queries auto-update the UI), compile-time checked SQL, and built-in migration 12 + support. Tables: `accounts` (DID, handle, tokens), `cached_profiles`, 13 + `cached_posts`, `settings`. 14 + 15 + Feature-first folder structure: 16 + 17 + ```sh 18 + lib/ 19 + core/ # shared models, theme, routing, DI 20 + features/ 21 + auth/ # bloc, data, presentation 22 + profile/ # bloc, data, presentation 23 + settings/ # bloc, data, presentation 24 + ``` 25 + 26 + ## Packages 27 + 28 + | Package | Purpose | 29 + | ---------------- | ------------------------------------------------- | 30 + | `bluesky` | Full AT Protocol + `app.bsky.*` / `chat.bsky.*` | 31 + | `atproto_oauth` | AT Protocol OAuth 2.0 for Flutter | 32 + | `bluesky_text` | Rich text / facet parsing | 33 + | `flutter_bloc` | BLoC / Cubit state management | 34 + | `drift` | Type-safe reactive SQLite ORM | 35 + | `go_router` | Declarative routing | 36 + 37 + ## Authentication 38 + 39 + ### 1. OAuth 2.0 (Production) 40 + 41 + AT Protocol mandates **DPoP + PAR + PKCE** for all clients. Lazurite is a 42 + public native client (`token_endpoint_auth_method: "none"`, 43 + `application_type: "native"`). 44 + 45 + **Constants:** 46 + 47 + ```dart 48 + static const kClientId = 'https://lazurite.stormlightlabs.org/client-metadata.json'; 49 + static const kRedirectUri = 'http://127.0.0.1/callback'; 50 + static const kScope = 'atproto transition:generic'; 51 + ``` 52 + 53 + Client metadata is hosted at `https://lazurite.stormlightlabs.org/client-metadata.json`. 54 + The PDS fetches this document to verify the client during the OAuth flow. 55 + 56 + **Flow:** 57 + 58 + 1. Generate a DPoP keypair (store private key in platform keychain; non-exportable). 59 + 2. Generate PKCE `code_verifier` → `code_challenge`. 60 + 3. POST to the PDS **PAR endpoint** with `kClientId`, `code_challenge`, 61 + `kScope`, and an initial DPoP Proof JWT → receive a `request_uri`. 62 + 4. Open system browser / `ASWebAuthenticationSession` / 63 + `CustomTabsIntent` to the PDS **authorization endpoint** with the 64 + `request_uri`. 65 + 5. User authenticates on their PDS and grants consent. 66 + 6. PDS redirects to the **loopback redirect** (`http://127.0.0.1/callback`) 67 + with an authorization `code`. The app starts a temporary local HTTP server 68 + to capture the callback. 69 + 7. Exchange `code` + `code_verifier` + new DPoP Proof JWT at the **token 70 + endpoint** → receive DPoP-bound `access_token` + `refresh_token`. 71 + 8. Every API request sends the `access_token` in `Authorization` and a fresh 72 + DPoP Proof JWT in the `DPoP` header. 73 + 74 + Token lifetimes: `access_token` ~2 h, `refresh_token` ~2 months. The 75 + `atproto_oauth` package handles automatic refresh. 76 + 77 + ### 2. App Password (Debug Only) 78 + 79 + Calls `com.atproto.server.createSession` with handle + app password 80 + (`xxxx-xxxx-xxxx-xxxx`). Returns `accessJwt` / `refreshJwt` + DID / handle. 81 + Rate limit: 30 req / 5 min, 300 / day. 82 + 83 + App passwords cannot delete or migrate the account, nor create other app 84 + passwords. Guard this path behind a compile-time debug flag 85 + (`kDebugMode` / `--dart-define`). 86 + 87 + ### 3. Login & Logout 88 + 89 + - **Login screen:** handle input + "Sign in with BlueSky" button (OAuth) and, 90 + in debug builds, an app-password form. 91 + - **Session restore:** on launch, read stored tokens from Drift → attempt 92 + silent refresh → land on home or login. 93 + - **Logout:** revoke tokens, wipe Drift session row, clear in-memory Bloc 94 + state, navigate to login. 95 + - **Multi-account (stretch):** `accounts` table supports multiple DIDs; account 96 + switcher in settings. 97 + 98 + ## Profile Rendering 99 + 100 + Data source: `app.bsky.actor.getProfile` (single) / 101 + `app.bsky.actor.getProfiles` (batch). 102 + 103 + **Profile fields to render:** 104 + 105 + - Avatar + banner images (CDN URIs) 106 + - `displayName`, `handle`, `description` 107 + - Follower / following / post counts 108 + - `pronouns`, `website` 109 + 110 + ### 1. Posts 111 + 112 + Fetch via `app.bsky.feed.getAuthorFeed`. Paginate with `cursor`; support 113 + `filter` values: `posts_no_replies`, `posts_with_media`, 114 + `posts_and_author_threads`. 115 + 116 + Each feed item is an `app.bsky.feed.defs#feedViewPost` containing a `post` 117 + view. Key fields: `text`, `createdAt`, `embed` (images ≤ 4, quote posts, 118 + external link cards, video), `reply` parent/root refs, `langs`. 119 + 120 + ### 2. Post Facets (Rich Text) 121 + 122 + Facets annotate byte ranges of the UTF-8 `text` field. Each facet has an 123 + `index` (`byteStart` inclusive, `byteEnd` exclusive) and a `features` array. 124 + 125 + | Feature type | Payload | 126 + | ---------------------------------- | --------- | 127 + | `app.bsky.richtext.facet#mention` | `did` | 128 + | `app.bsky.richtext.facet#link` | `uri` | 129 + | `app.bsky.richtext.facet#tag` | `tag` | 130 + 131 + Use the `bluesky_text` package to parse facets. Render mentions as tappable 132 + profile links, URIs as tappable external links, and hashtags as tappable search 133 + links. Byte indices are **UTF-8**—do not use Dart's UTF-16 string indices 134 + directly. 135 + 136 + ## Settings 137 + 138 + ### 1. Light / Dark Mode 139 + 140 + System default, Light, and Dark. Persist choice in Drift `settings` table. 141 + Apply via `ThemeMode` on `MaterialApp`. Use `HydratedBloc` or a `SettingsCubit` 142 + that reads/writes the preference. 143 + 144 + ### 2. Custom Themes — Oxocarbon 145 + 146 + Ship two built-in themes derived from the Oxocarbon palette (IBM Carbon 147 + inspired): 148 + 149 + **Dark**: 150 + 151 + | Token | Hex | Role | 152 + | -------- | --------- | --------------------------- | 153 + | base00 | `#161616` | Background | 154 + | base01 | `#262626` | Surface / card | 155 + | base02 | `#393939` | Selection / divider | 156 + | base03 | `#525252` | Muted text | 157 + | base04 | `#dde1e6` | Secondary text | 158 + | base05 | `#f2f4f8` | Primary text | 159 + | base06 | `#ffffff` | Bright text | 160 + | base07 | `#08bdba` | Teal accent | 161 + | base08 | `#3ddbd9` | Cyan highlight | 162 + | base09 | `#78a9ff` | Blue accent | 163 + | base0A | `#ee5396` | Pink / error | 164 + | base0B | `#33b1ff` | Light blue | 165 + | base0C | `#ff7eb6` | Magenta | 166 + | base0D | `#42be65` | Green / success | 167 + | base0E | `#be95ff` | Purple accent | 168 + | base0F | `#82cfff` | Sky blue | 169 + 170 + **Light**: 171 + 172 + | Token | Hex | Role | 173 + | -------- | --------- | --------------------------- | 174 + | base00 | `#ffffff` | Background | 175 + | base01 | `#f2f2f2` | Surface / card | 176 + | base02 | `#d0d0d0` | Selection / divider | 177 + | base03 | `#161616` | Primary text | 178 + | base04 | `#37474F` | Secondary text | 179 + | base05 | `#90A4AE` | Muted text | 180 + | base06 | `#525252` | Subheading text | 181 + | base07 | `#08bdba` | Teal accent | 182 + | base08 | `#ff7eb6` | Pink accent | 183 + | base09 | `#ee5396` | Error | 184 + | base0A | `#FF6F00` | Orange / warning | 185 + | base0B | `#0f62fe` | Primary blue | 186 + | base0C | `#673AB7` | Purple accent | 187 + | base0D | `#42be65` | Green / success | 188 + | base0E | `#be95ff` | Lavender accent | 189 + | base0F | `#FFAB91` | Salmon | 190 + 191 + Map these tokens to a `ThemeData` / `ColorScheme` and expose a 192 + `OxocarbonTheme.dark()` / `OxocarbonTheme.light()` factory. 193 + 194 + ## Development 195 + 196 + `/scripts` directory with Python utilities for inspecting AT Protocol / BlueSky 197 + API data. 198 + 199 + Scripts to include: 200 + 201 + | Script | Purpose | 202 + | ------------------- | ---------------------------------------------------- | 203 + | `fetch_profile.py` | Fetch and pretty-print a profile via `getProfile` | 204 + | `fetch_feed.py` | Fetch author feed and dump post/facet structures | 205 + | `resolve_handle.py` | Resolve handle → DID via `com.atproto.identity` | 206 + 207 + Use the `atproto` Python SDK (`atproto` on PyPI). Each script should accept 208 + CLI args (handle, limit, etc.) and output JSON to stdout.
+136
docs/specs/phase-2.md
··· 1 + # Lazurite Phase 2 Spec 2 + 3 + ## Feeds 4 + 5 + Phase 1 builds profile author feeds only. Phase 2 adds the full home feed 6 + experience: the user's timeline, custom feed generators, and feed management. 7 + 8 + ### Timeline 9 + 10 + `app.bsky.feed.getTimeline` — reverse-chronological feed of posts from 11 + followed accounts. Paginate with `cursor`; `limit` 1–100 (default 50). 12 + 13 + ### Feed Generators 14 + 15 + Feed generators are third-party algorithmic feeds identified by AT-URIs 16 + (e.g. `at://did:plc:…/app.bsky.feed.generator/whats-hot`). 17 + 18 + | Endpoint | Purpose | 19 + | --------------------------------- | -------------------------------------- | 20 + | `app.bsky.feed.getFeed` | Fetch hydrated posts from a generator | 21 + | `app.bsky.feed.getFeedGenerator` | Metadata for a single generator | 22 + | `app.bsky.feed.getFeedGenerators` | Batch metadata for multiple generators | 23 + | `app.bsky.feed.getSuggestedFeeds` | Discover new feed generators | 24 + 25 + `getFeed` takes the generator's AT-URI + `cursor` / `limit`. The AppView 26 + resolves posts returned by the generator and hydrates them into full 27 + `feedViewPost` views. 28 + 29 + ### Rendering 30 + 31 + Each feed renders as the same post-card list built in Phase 1. The home screen 32 + uses a horizontally-swipable tab bar — one tab per pinned feed, with 33 + "Following" (timeline) as the default. 34 + 35 + ### Feed Management 36 + 37 + User feed preferences are stored server-side via 38 + `app.bsky.actor.putPreferences` / `getPreferences`. The preferences object 39 + contains a `savedFeedsPrefV2` array, where each entry has: 40 + 41 + - `id` — unique client-generated identifier 42 + - `type` — `feed` (generator) or `timeline` or `list` 43 + - `value` — AT-URI of the feed generator (or `timeline` literal) 44 + - `pinned` — whether the feed appears as a home tab 45 + 46 + **Operations:** 47 + 48 + | Action | Implementation | 49 + | ----------- | -------------------------------------------------------- | 50 + | Pin / Unpin | Toggle `pinned` flag, call `putPreferences` | 51 + | Reorder | Drag-to-reorder in settings, update array order, persist | 52 + | Remove | Remove entry from `savedFeedsPrefV2`, persist | 53 + | Add | Browse `getSuggestedFeeds`, append entry, persist | 54 + 55 + Build a `FeedPreferencesCubit` that reads on launch and writes back on 56 + mutation. Cache the preferences array in Drift for offline access. 57 + 58 + ## Search 59 + 60 + ### Search Posts 61 + 62 + `app.bsky.feed.searchPosts` — full-text search across the network. 63 + 64 + | Parameter | Description | 65 + | ---------- | -------------------------------------------- | 66 + | `q` | Query string (Lucene syntax supported) | 67 + | `sort` | `top` or `latest` | 68 + | `author` | Filter to a specific account (at-identifier) | 69 + | `mentions` | Filter to posts mentioning an account | 70 + | `lang` | BCP-47 language filter | 71 + | `since` | Posts after this datetime | 72 + | `until` | Posts before this datetime | 73 + | `tag` | Hashtag filter (without `#`; multiple = AND) | 74 + | `domain` | Posts containing links to this domain | 75 + | `url` | Posts containing this exact URL | 76 + 77 + Returns paginated `postView` objects. `limit` 1–100. 78 + 79 + ### Search Actors 80 + 81 + | Endpoint | Purpose | 82 + | -------------------------------------- | ------------------------------- | 83 + | `app.bsky.actor.searchActors` | Full profile search by query | 84 + | `app.bsky.actor.searchActorsTypeahead` | Prefix autocomplete for handles | 85 + 86 + `searchActors` returns `profileView` objects, paginated (`limit` 1–100). 87 + `searchActorsTypeahead` is lightweight, intended for real-time autocomplete 88 + in the search bar. 89 + 90 + ### Persisted Search History 91 + 92 + Store recent queries in a Drift `search_history` table: 93 + 94 + | Column | Type | Notes | 95 + | ------------- | -------- | -------------------------- | 96 + | `id` | integer | PK autoincrement | 97 + | `query` | text | The search string | 98 + | `type` | text | `posts` or `actors` | 99 + | `searched_at` | datetime | Timestamp | 100 + | `account_did` | text | FK to `accounts`, per-user | 101 + 102 + Display recent searches below the search bar. Tap to re-execute; swipe to 103 + delete. Cap at 50 entries per account, evicting oldest on insert. 104 + 105 + Build a `SearchBloc` with events: `QuerySubmitted`, `TypeaheadRequested`, 106 + `HistoryCleared`, `HistoryEntryDeleted`. 107 + 108 + ## Dev Tools 109 + 110 + ### PDS Explorer (pdsls.dev replica) 111 + 112 + An in-app developer tool (debug builds) that replicates the core functionality 113 + of [pdsls.dev](https://pdsls.dev) — a client-side AT Protocol repository 114 + browser. 115 + 116 + **Core features:** 117 + 118 + 1. **Handle / DID resolution** — enter a handle, resolve to DID via 119 + `com.atproto.identity.resolveHandle`. 120 + 2. **Repository overview** — call `com.atproto.repo.describeRepo` to list all 121 + collections (NSIDs) in a user's repo with record counts. 122 + 3. **Collection browser** — select a collection, paginate through records via 123 + `com.atproto.repo.listRecords` (`limit`, `cursor`, `reverse`). 124 + 4. **Record inspector** — tap a record to view the full JSON via 125 + `com.atproto.repo.getRecord`. Pretty-print with syntax highlighting. 126 + 5. **AT-URI input** — paste an `at://` URI to jump directly to a record. 127 + 128 + | API Endpoint | Usage | 129 + | ------------------------------------ | ------------------------------------ | 130 + | `com.atproto.identity.resolveHandle` | Handle → DID | 131 + | `com.atproto.repo.describeRepo` | DID/handle → collection list | 132 + | `com.atproto.repo.listRecords` | Collection → paginated record list | 133 + | `com.atproto.repo.getRecord` | Collection + rkey → full record JSON | 134 + 135 + Guard behind `kDebugMode`. No separate Bloc needed — use a `DevToolsCubit` 136 + with simple request/response state, since this is a stateless exploration tool.
+40
docs/tasks/phase-1.md
··· 1 + # Phase 1 Milestones 2 + 3 + ## M0 — Project Scaffolding 4 + 5 + - [x] Add dependencies (`bluesky`, `atproto_oauth`, `bluesky_text`, `flutter_bloc`, `drift`, `go_router`) 6 + - [ ] Set up feature-first folder structure (`core/`, `features/auth|profile|settings/`) 7 + - [ ] Configure Drift database with `accounts`, `cached_profiles`, `cached_posts`, `settings` tables 8 + - [ ] Configure `go_router` with initial route definitions (login, home, profile, settings) 9 + 10 + ## M1 — Authentication 11 + 12 + - [ ] Implement App Password login (`createSession`) behind `kDebugMode` flag 13 + - [ ] Implement OAuth 2.0 flow (DPoP + PAR + PKCE) via `atproto_oauth` 14 + - [ ] Set up loopback redirect listener (`http://127.0.0.1/callback`) 15 + - [ ] Build `AuthBloc` — events: `LoginRequested`, `LogoutRequested`, `SessionRestored`; states: `Unauthenticated`, `Authenticating`, `Authenticated`, `AuthError` 16 + - [ ] Session persistence: store/restore tokens in Drift, silent refresh on launch 17 + - [ ] Build login screen (handle input, OAuth button, debug app-password form) 18 + - [ ] Logout: revoke tokens, clear Drift row, reset Bloc, navigate to login 19 + 20 + ## M2 — Profile Rendering 21 + 22 + - [ ] Build `ProfileBloc` — fetch via `getProfile` / `getProfiles` 23 + - [ ] Profile screen: avatar, banner, display name, handle, description, stats (followers/following/posts), pronouns, website 24 + - [ ] Build `FeedBloc` — paginated fetch via `getAuthorFeed` with cursor + filter support 25 + - [ ] Post card widget: text, timestamps, embeds (images, quote posts, link cards) 26 + - [ ] Facet rendering: parse via `bluesky_text`, render mentions / links / hashtags as tappable spans (UTF-8 byte-safe) 27 + 28 + ## M3 — Settings & Theming 29 + 30 + - [ ] `SettingsCubit` backed by Drift — theme mode preference (system / light / dark) 31 + - [ ] Oxocarbon Dark `ThemeData` / `ColorScheme` 32 + - [ ] Oxocarbon Light `ThemeData` / `ColorScheme` 33 + - [ ] Theme picker in settings screen 34 + - [ ] Respect system theme when set to "system" 35 + 36 + ## M4 — Dev Scripts 37 + 38 + - [ ] `scripts/fetch_profile.py` — pretty-print profile JSON 39 + - [ ] `scripts/fetch_feed.py` — dump post + facet structures 40 + - [ ] `scripts/resolve_handle.py` — resolve handle → DID
+31
docs/tasks/phase-2.md
··· 1 + # Phase 2 Milestones 2 + 3 + ## M5 — Feeds 4 + 5 + - [ ] Build home screen with horizontally-swipable tab bar (one tab per pinned feed) 6 + - [ ] Implement timeline feed via `getTimeline` with cursor pagination 7 + - [ ] Implement feed generator rendering via `getFeed` (AT-URI + pagination) 8 + - [ ] `FeedPreferencesCubit` — read/write `savedFeedsPrefV2` via `getPreferences` / `putPreferences` 9 + - [ ] Cache feed preferences in Drift for offline access 10 + - [ ] Feed discovery screen via `getSuggestedFeeds` — browse and add generators 11 + - [ ] Feed management UI — pin/unpin, drag-to-reorder, remove saved feeds 12 + 13 + ## M6 — Search 14 + 15 + - [ ] Search screen with text input, sort toggle (`top` / `latest`), and result tabs (posts / actors) 16 + - [ ] `SearchBloc` — events: `QuerySubmitted`, `TypeaheadRequested`, `HistoryCleared`, `HistoryEntryDeleted` 17 + - [ ] Post search via `searchPosts` with paginated results 18 + - [ ] Actor search via `searchActors` with paginated results 19 + - [ ] Typeahead autocomplete via `searchActorsTypeahead` 20 + - [ ] Drift migration: add `search_history` table (query, type, searched_at, account_did) 21 + - [ ] Persisted search history — display recent queries, tap to re-execute, swipe to delete, cap at 50 per account 22 + 23 + ## M7 — Dev Tools (PDS Explorer) 24 + 25 + - [ ] `DevToolsCubit` with request/response state for stateless exploration 26 + - [ ] Handle / DID input with resolution via `resolveHandle` 27 + - [ ] Repository overview via `describeRepo` — list collections with record counts 28 + - [ ] Collection browser via `listRecords` — paginated record list per collection 29 + - [ ] Record inspector via `getRecord` — pretty-printed JSON with syntax highlighting 30 + - [ ] AT-URI input — paste `at://` URI to jump directly to a record 31 + - [ ] Guard all dev tools screens behind `kDebugMode`
+34
ios/.gitignore
··· 1 + **/dgph 2 + *.mode1v3 3 + *.mode2v3 4 + *.moved-aside 5 + *.pbxuser 6 + *.perspectivev3 7 + **/*sync/ 8 + .sconsign.dblite 9 + .tags* 10 + **/.vagrant/ 11 + **/DerivedData/ 12 + Icon? 13 + **/Pods/ 14 + **/.symlinks/ 15 + profile 16 + xcuserdata 17 + **/.generated/ 18 + Flutter/App.framework 19 + Flutter/Flutter.framework 20 + Flutter/Flutter.podspec 21 + Flutter/Generated.xcconfig 22 + Flutter/ephemeral/ 23 + Flutter/app.flx 24 + Flutter/app.zip 25 + Flutter/flutter_assets/ 26 + Flutter/flutter_export_environment.sh 27 + ServiceDefinitions.json 28 + Runner/GeneratedPluginRegistrant.* 29 + 30 + # Exceptions to above rules. 31 + !default.mode1v3 32 + !default.mode2v3 33 + !default.pbxuser 34 + !default.perspectivev3
+26
ios/Flutter/AppFrameworkInfo.plist
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 3 + <plist version="1.0"> 4 + <dict> 5 + <key>CFBundleDevelopmentRegion</key> 6 + <string>en</string> 7 + <key>CFBundleExecutable</key> 8 + <string>App</string> 9 + <key>CFBundleIdentifier</key> 10 + <string>io.flutter.flutter.app</string> 11 + <key>CFBundleInfoDictionaryVersion</key> 12 + <string>6.0</string> 13 + <key>CFBundleName</key> 14 + <string>App</string> 15 + <key>CFBundlePackageType</key> 16 + <string>FMWK</string> 17 + <key>CFBundleShortVersionString</key> 18 + <string>1.0</string> 19 + <key>CFBundleSignature</key> 20 + <string>????</string> 21 + <key>CFBundleVersion</key> 22 + <string>1.0</string> 23 + <key>MinimumOSVersion</key> 24 + <string>13.0</string> 25 + </dict> 26 + </plist>
+1
ios/Flutter/Debug.xcconfig
··· 1 + #include "Generated.xcconfig"
+1
ios/Flutter/Release.xcconfig
··· 1 + #include "Generated.xcconfig"
+616
ios/Runner.xcodeproj/project.pbxproj
··· 1 + // !$*UTF8*$! 2 + { 3 + archiveVersion = 1; 4 + classes = { 5 + }; 6 + objectVersion = 54; 7 + objects = { 8 + 9 + /* Begin PBXBuildFile section */ 10 + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 12 + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 14 + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 15 + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 16 + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 17 + /* End PBXBuildFile section */ 18 + 19 + /* Begin PBXContainerItemProxy section */ 20 + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { 21 + isa = PBXContainerItemProxy; 22 + containerPortal = 97C146E61CF9000F007C117D /* Project object */; 23 + proxyType = 1; 24 + remoteGlobalIDString = 97C146ED1CF9000F007C117D; 25 + remoteInfo = Runner; 26 + }; 27 + /* End PBXContainerItemProxy section */ 28 + 29 + /* Begin PBXCopyFilesBuildPhase section */ 30 + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 31 + isa = PBXCopyFilesBuildPhase; 32 + buildActionMask = 2147483647; 33 + dstPath = ""; 34 + dstSubfolderSpec = 10; 35 + files = ( 36 + ); 37 + name = "Embed Frameworks"; 38 + runOnlyForDeploymentPostprocessing = 0; 39 + }; 40 + /* End PBXCopyFilesBuildPhase section */ 41 + 42 + /* Begin PBXFileReference section */ 43 + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; 44 + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; 45 + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; }; 46 + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 47 + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; 48 + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; }; 49 + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; 50 + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; 51 + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; 52 + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; 53 + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 54 + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; 55 + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 56 + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; 57 + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 58 + /* End PBXFileReference section */ 59 + 60 + /* Begin PBXFrameworksBuildPhase section */ 61 + 97C146EB1CF9000F007C117D /* Frameworks */ = { 62 + isa = PBXFrameworksBuildPhase; 63 + buildActionMask = 2147483647; 64 + files = ( 65 + ); 66 + runOnlyForDeploymentPostprocessing = 0; 67 + }; 68 + /* End PBXFrameworksBuildPhase section */ 69 + 70 + /* Begin PBXGroup section */ 71 + 331C8082294A63A400263BE5 /* RunnerTests */ = { 72 + isa = PBXGroup; 73 + children = ( 74 + 331C807B294A618700263BE5 /* RunnerTests.swift */, 75 + ); 76 + path = RunnerTests; 77 + sourceTree = "<group>"; 78 + }; 79 + 9740EEB11CF90186004384FC /* Flutter */ = { 80 + isa = PBXGroup; 81 + children = ( 82 + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 83 + 9740EEB21CF90195004384FC /* Debug.xcconfig */, 84 + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 85 + 9740EEB31CF90195004384FC /* Generated.xcconfig */, 86 + ); 87 + name = Flutter; 88 + sourceTree = "<group>"; 89 + }; 90 + 97C146E51CF9000F007C117D = { 91 + isa = PBXGroup; 92 + children = ( 93 + 9740EEB11CF90186004384FC /* Flutter */, 94 + 97C146F01CF9000F007C117D /* Runner */, 95 + 97C146EF1CF9000F007C117D /* Products */, 96 + 331C8082294A63A400263BE5 /* RunnerTests */, 97 + ); 98 + sourceTree = "<group>"; 99 + }; 100 + 97C146EF1CF9000F007C117D /* Products */ = { 101 + isa = PBXGroup; 102 + children = ( 103 + 97C146EE1CF9000F007C117D /* Runner.app */, 104 + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, 105 + ); 106 + name = Products; 107 + sourceTree = "<group>"; 108 + }; 109 + 97C146F01CF9000F007C117D /* Runner */ = { 110 + isa = PBXGroup; 111 + children = ( 112 + 97C146FA1CF9000F007C117D /* Main.storyboard */, 113 + 97C146FD1CF9000F007C117D /* Assets.xcassets */, 114 + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 115 + 97C147021CF9000F007C117D /* Info.plist */, 116 + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 117 + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 118 + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 119 + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 120 + ); 121 + path = Runner; 122 + sourceTree = "<group>"; 123 + }; 124 + /* End PBXGroup section */ 125 + 126 + /* Begin PBXNativeTarget section */ 127 + 331C8080294A63A400263BE5 /* RunnerTests */ = { 128 + isa = PBXNativeTarget; 129 + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; 130 + buildPhases = ( 131 + 331C807D294A63A400263BE5 /* Sources */, 132 + 331C807F294A63A400263BE5 /* Resources */, 133 + ); 134 + buildRules = ( 135 + ); 136 + dependencies = ( 137 + 331C8086294A63A400263BE5 /* PBXTargetDependency */, 138 + ); 139 + name = RunnerTests; 140 + productName = RunnerTests; 141 + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; 142 + productType = "com.apple.product-type.bundle.unit-test"; 143 + }; 144 + 97C146ED1CF9000F007C117D /* Runner */ = { 145 + isa = PBXNativeTarget; 146 + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 147 + buildPhases = ( 148 + 9740EEB61CF901F6004384FC /* Run Script */, 149 + 97C146EA1CF9000F007C117D /* Sources */, 150 + 97C146EB1CF9000F007C117D /* Frameworks */, 151 + 97C146EC1CF9000F007C117D /* Resources */, 152 + 9705A1C41CF9048500538489 /* Embed Frameworks */, 153 + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 154 + ); 155 + buildRules = ( 156 + ); 157 + dependencies = ( 158 + ); 159 + name = Runner; 160 + productName = Runner; 161 + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 162 + productType = "com.apple.product-type.application"; 163 + }; 164 + /* End PBXNativeTarget section */ 165 + 166 + /* Begin PBXProject section */ 167 + 97C146E61CF9000F007C117D /* Project object */ = { 168 + isa = PBXProject; 169 + attributes = { 170 + BuildIndependentTargetsInParallel = YES; 171 + LastUpgradeCheck = 1510; 172 + ORGANIZATIONNAME = ""; 173 + TargetAttributes = { 174 + 331C8080294A63A400263BE5 = { 175 + CreatedOnToolsVersion = 14.0; 176 + TestTargetID = 97C146ED1CF9000F007C117D; 177 + }; 178 + 97C146ED1CF9000F007C117D = { 179 + CreatedOnToolsVersion = 7.3.1; 180 + LastSwiftMigration = 1100; 181 + }; 182 + }; 183 + }; 184 + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 185 + compatibilityVersion = "Xcode 9.3"; 186 + developmentRegion = en; 187 + hasScannedForEncodings = 0; 188 + knownRegions = ( 189 + en, 190 + Base, 191 + ); 192 + mainGroup = 97C146E51CF9000F007C117D; 193 + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 194 + projectDirPath = ""; 195 + projectRoot = ""; 196 + targets = ( 197 + 97C146ED1CF9000F007C117D /* Runner */, 198 + 331C8080294A63A400263BE5 /* RunnerTests */, 199 + ); 200 + }; 201 + /* End PBXProject section */ 202 + 203 + /* Begin PBXResourcesBuildPhase section */ 204 + 331C807F294A63A400263BE5 /* Resources */ = { 205 + isa = PBXResourcesBuildPhase; 206 + buildActionMask = 2147483647; 207 + files = ( 208 + ); 209 + runOnlyForDeploymentPostprocessing = 0; 210 + }; 211 + 97C146EC1CF9000F007C117D /* Resources */ = { 212 + isa = PBXResourcesBuildPhase; 213 + buildActionMask = 2147483647; 214 + files = ( 215 + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 216 + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 217 + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 218 + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 219 + ); 220 + runOnlyForDeploymentPostprocessing = 0; 221 + }; 222 + /* End PBXResourcesBuildPhase section */ 223 + 224 + /* Begin PBXShellScriptBuildPhase section */ 225 + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 226 + isa = PBXShellScriptBuildPhase; 227 + alwaysOutOfDate = 1; 228 + buildActionMask = 2147483647; 229 + files = ( 230 + ); 231 + inputPaths = ( 232 + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", 233 + ); 234 + name = "Thin Binary"; 235 + outputPaths = ( 236 + ); 237 + runOnlyForDeploymentPostprocessing = 0; 238 + shellPath = /bin/sh; 239 + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 240 + }; 241 + 9740EEB61CF901F6004384FC /* Run Script */ = { 242 + isa = PBXShellScriptBuildPhase; 243 + alwaysOutOfDate = 1; 244 + buildActionMask = 2147483647; 245 + files = ( 246 + ); 247 + inputPaths = ( 248 + ); 249 + name = "Run Script"; 250 + outputPaths = ( 251 + ); 252 + runOnlyForDeploymentPostprocessing = 0; 253 + shellPath = /bin/sh; 254 + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 255 + }; 256 + /* End PBXShellScriptBuildPhase section */ 257 + 258 + /* Begin PBXSourcesBuildPhase section */ 259 + 331C807D294A63A400263BE5 /* Sources */ = { 260 + isa = PBXSourcesBuildPhase; 261 + buildActionMask = 2147483647; 262 + files = ( 263 + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, 264 + ); 265 + runOnlyForDeploymentPostprocessing = 0; 266 + }; 267 + 97C146EA1CF9000F007C117D /* Sources */ = { 268 + isa = PBXSourcesBuildPhase; 269 + buildActionMask = 2147483647; 270 + files = ( 271 + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 272 + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 273 + ); 274 + runOnlyForDeploymentPostprocessing = 0; 275 + }; 276 + /* End PBXSourcesBuildPhase section */ 277 + 278 + /* Begin PBXTargetDependency section */ 279 + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { 280 + isa = PBXTargetDependency; 281 + target = 97C146ED1CF9000F007C117D /* Runner */; 282 + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; 283 + }; 284 + /* End PBXTargetDependency section */ 285 + 286 + /* Begin PBXVariantGroup section */ 287 + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 288 + isa = PBXVariantGroup; 289 + children = ( 290 + 97C146FB1CF9000F007C117D /* Base */, 291 + ); 292 + name = Main.storyboard; 293 + sourceTree = "<group>"; 294 + }; 295 + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 296 + isa = PBXVariantGroup; 297 + children = ( 298 + 97C147001CF9000F007C117D /* Base */, 299 + ); 300 + name = LaunchScreen.storyboard; 301 + sourceTree = "<group>"; 302 + }; 303 + /* End PBXVariantGroup section */ 304 + 305 + /* Begin XCBuildConfiguration section */ 306 + 249021D3217E4FDB00AE95B9 /* Profile */ = { 307 + isa = XCBuildConfiguration; 308 + buildSettings = { 309 + ALWAYS_SEARCH_USER_PATHS = NO; 310 + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 311 + CLANG_ANALYZER_NONNULL = YES; 312 + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 313 + CLANG_CXX_LIBRARY = "libc++"; 314 + CLANG_ENABLE_MODULES = YES; 315 + CLANG_ENABLE_OBJC_ARC = YES; 316 + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 317 + CLANG_WARN_BOOL_CONVERSION = YES; 318 + CLANG_WARN_COMMA = YES; 319 + CLANG_WARN_CONSTANT_CONVERSION = YES; 320 + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 321 + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 322 + CLANG_WARN_EMPTY_BODY = YES; 323 + CLANG_WARN_ENUM_CONVERSION = YES; 324 + CLANG_WARN_INFINITE_RECURSION = YES; 325 + CLANG_WARN_INT_CONVERSION = YES; 326 + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 327 + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 328 + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 329 + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 330 + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 331 + CLANG_WARN_STRICT_PROTOTYPES = YES; 332 + CLANG_WARN_SUSPICIOUS_MOVE = YES; 333 + CLANG_WARN_UNREACHABLE_CODE = YES; 334 + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 335 + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 336 + COPY_PHASE_STRIP = NO; 337 + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 338 + ENABLE_NS_ASSERTIONS = NO; 339 + ENABLE_STRICT_OBJC_MSGSEND = YES; 340 + ENABLE_USER_SCRIPT_SANDBOXING = NO; 341 + GCC_C_LANGUAGE_STANDARD = gnu99; 342 + GCC_NO_COMMON_BLOCKS = YES; 343 + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 344 + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 345 + GCC_WARN_UNDECLARED_SELECTOR = YES; 346 + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 347 + GCC_WARN_UNUSED_FUNCTION = YES; 348 + GCC_WARN_UNUSED_VARIABLE = YES; 349 + IPHONEOS_DEPLOYMENT_TARGET = 13.0; 350 + MTL_ENABLE_DEBUG_INFO = NO; 351 + SDKROOT = iphoneos; 352 + SUPPORTED_PLATFORMS = iphoneos; 353 + TARGETED_DEVICE_FAMILY = "1,2"; 354 + VALIDATE_PRODUCT = YES; 355 + }; 356 + name = Profile; 357 + }; 358 + 249021D4217E4FDB00AE95B9 /* Profile */ = { 359 + isa = XCBuildConfiguration; 360 + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 361 + buildSettings = { 362 + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 363 + CLANG_ENABLE_MODULES = YES; 364 + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 365 + ENABLE_BITCODE = NO; 366 + INFOPLIST_FILE = Runner/Info.plist; 367 + LD_RUNPATH_SEARCH_PATHS = ( 368 + "$(inherited)", 369 + "@executable_path/Frameworks", 370 + ); 371 + PRODUCT_BUNDLE_IDENTIFIER = com.example.lazurite; 372 + PRODUCT_NAME = "$(TARGET_NAME)"; 373 + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 374 + SWIFT_VERSION = 5.0; 375 + VERSIONING_SYSTEM = "apple-generic"; 376 + }; 377 + name = Profile; 378 + }; 379 + 331C8088294A63A400263BE5 /* Debug */ = { 380 + isa = XCBuildConfiguration; 381 + buildSettings = { 382 + BUNDLE_LOADER = "$(TEST_HOST)"; 383 + CODE_SIGN_STYLE = Automatic; 384 + CURRENT_PROJECT_VERSION = 1; 385 + GENERATE_INFOPLIST_FILE = YES; 386 + MARKETING_VERSION = 1.0; 387 + PRODUCT_BUNDLE_IDENTIFIER = com.example.lazurite.RunnerTests; 388 + PRODUCT_NAME = "$(TARGET_NAME)"; 389 + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 390 + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 391 + SWIFT_VERSION = 5.0; 392 + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; 393 + }; 394 + name = Debug; 395 + }; 396 + 331C8089294A63A400263BE5 /* Release */ = { 397 + isa = XCBuildConfiguration; 398 + buildSettings = { 399 + BUNDLE_LOADER = "$(TEST_HOST)"; 400 + CODE_SIGN_STYLE = Automatic; 401 + CURRENT_PROJECT_VERSION = 1; 402 + GENERATE_INFOPLIST_FILE = YES; 403 + MARKETING_VERSION = 1.0; 404 + PRODUCT_BUNDLE_IDENTIFIER = com.example.lazurite.RunnerTests; 405 + PRODUCT_NAME = "$(TARGET_NAME)"; 406 + SWIFT_VERSION = 5.0; 407 + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; 408 + }; 409 + name = Release; 410 + }; 411 + 331C808A294A63A400263BE5 /* Profile */ = { 412 + isa = XCBuildConfiguration; 413 + buildSettings = { 414 + BUNDLE_LOADER = "$(TEST_HOST)"; 415 + CODE_SIGN_STYLE = Automatic; 416 + CURRENT_PROJECT_VERSION = 1; 417 + GENERATE_INFOPLIST_FILE = YES; 418 + MARKETING_VERSION = 1.0; 419 + PRODUCT_BUNDLE_IDENTIFIER = com.example.lazurite.RunnerTests; 420 + PRODUCT_NAME = "$(TARGET_NAME)"; 421 + SWIFT_VERSION = 5.0; 422 + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; 423 + }; 424 + name = Profile; 425 + }; 426 + 97C147031CF9000F007C117D /* Debug */ = { 427 + isa = XCBuildConfiguration; 428 + buildSettings = { 429 + ALWAYS_SEARCH_USER_PATHS = NO; 430 + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 431 + CLANG_ANALYZER_NONNULL = YES; 432 + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 433 + CLANG_CXX_LIBRARY = "libc++"; 434 + CLANG_ENABLE_MODULES = YES; 435 + CLANG_ENABLE_OBJC_ARC = YES; 436 + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 437 + CLANG_WARN_BOOL_CONVERSION = YES; 438 + CLANG_WARN_COMMA = YES; 439 + CLANG_WARN_CONSTANT_CONVERSION = YES; 440 + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 441 + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 442 + CLANG_WARN_EMPTY_BODY = YES; 443 + CLANG_WARN_ENUM_CONVERSION = YES; 444 + CLANG_WARN_INFINITE_RECURSION = YES; 445 + CLANG_WARN_INT_CONVERSION = YES; 446 + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 447 + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 448 + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 449 + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 450 + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 451 + CLANG_WARN_STRICT_PROTOTYPES = YES; 452 + CLANG_WARN_SUSPICIOUS_MOVE = YES; 453 + CLANG_WARN_UNREACHABLE_CODE = YES; 454 + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 455 + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 456 + COPY_PHASE_STRIP = NO; 457 + DEBUG_INFORMATION_FORMAT = dwarf; 458 + ENABLE_STRICT_OBJC_MSGSEND = YES; 459 + ENABLE_TESTABILITY = YES; 460 + ENABLE_USER_SCRIPT_SANDBOXING = NO; 461 + GCC_C_LANGUAGE_STANDARD = gnu99; 462 + GCC_DYNAMIC_NO_PIC = NO; 463 + GCC_NO_COMMON_BLOCKS = YES; 464 + GCC_OPTIMIZATION_LEVEL = 0; 465 + GCC_PREPROCESSOR_DEFINITIONS = ( 466 + "DEBUG=1", 467 + "$(inherited)", 468 + ); 469 + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 470 + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 471 + GCC_WARN_UNDECLARED_SELECTOR = YES; 472 + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 473 + GCC_WARN_UNUSED_FUNCTION = YES; 474 + GCC_WARN_UNUSED_VARIABLE = YES; 475 + IPHONEOS_DEPLOYMENT_TARGET = 13.0; 476 + MTL_ENABLE_DEBUG_INFO = YES; 477 + ONLY_ACTIVE_ARCH = YES; 478 + SDKROOT = iphoneos; 479 + TARGETED_DEVICE_FAMILY = "1,2"; 480 + }; 481 + name = Debug; 482 + }; 483 + 97C147041CF9000F007C117D /* Release */ = { 484 + isa = XCBuildConfiguration; 485 + buildSettings = { 486 + ALWAYS_SEARCH_USER_PATHS = NO; 487 + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 488 + CLANG_ANALYZER_NONNULL = YES; 489 + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 490 + CLANG_CXX_LIBRARY = "libc++"; 491 + CLANG_ENABLE_MODULES = YES; 492 + CLANG_ENABLE_OBJC_ARC = YES; 493 + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 494 + CLANG_WARN_BOOL_CONVERSION = YES; 495 + CLANG_WARN_COMMA = YES; 496 + CLANG_WARN_CONSTANT_CONVERSION = YES; 497 + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 498 + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 499 + CLANG_WARN_EMPTY_BODY = YES; 500 + CLANG_WARN_ENUM_CONVERSION = YES; 501 + CLANG_WARN_INFINITE_RECURSION = YES; 502 + CLANG_WARN_INT_CONVERSION = YES; 503 + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 504 + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 505 + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 506 + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 507 + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 508 + CLANG_WARN_STRICT_PROTOTYPES = YES; 509 + CLANG_WARN_SUSPICIOUS_MOVE = YES; 510 + CLANG_WARN_UNREACHABLE_CODE = YES; 511 + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 512 + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 513 + COPY_PHASE_STRIP = NO; 514 + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 515 + ENABLE_NS_ASSERTIONS = NO; 516 + ENABLE_STRICT_OBJC_MSGSEND = YES; 517 + ENABLE_USER_SCRIPT_SANDBOXING = NO; 518 + GCC_C_LANGUAGE_STANDARD = gnu99; 519 + GCC_NO_COMMON_BLOCKS = YES; 520 + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 521 + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 522 + GCC_WARN_UNDECLARED_SELECTOR = YES; 523 + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 524 + GCC_WARN_UNUSED_FUNCTION = YES; 525 + GCC_WARN_UNUSED_VARIABLE = YES; 526 + IPHONEOS_DEPLOYMENT_TARGET = 13.0; 527 + MTL_ENABLE_DEBUG_INFO = NO; 528 + SDKROOT = iphoneos; 529 + SUPPORTED_PLATFORMS = iphoneos; 530 + SWIFT_COMPILATION_MODE = wholemodule; 531 + SWIFT_OPTIMIZATION_LEVEL = "-O"; 532 + TARGETED_DEVICE_FAMILY = "1,2"; 533 + VALIDATE_PRODUCT = YES; 534 + }; 535 + name = Release; 536 + }; 537 + 97C147061CF9000F007C117D /* Debug */ = { 538 + isa = XCBuildConfiguration; 539 + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 540 + buildSettings = { 541 + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 542 + CLANG_ENABLE_MODULES = YES; 543 + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 544 + ENABLE_BITCODE = NO; 545 + INFOPLIST_FILE = Runner/Info.plist; 546 + LD_RUNPATH_SEARCH_PATHS = ( 547 + "$(inherited)", 548 + "@executable_path/Frameworks", 549 + ); 550 + PRODUCT_BUNDLE_IDENTIFIER = com.example.lazurite; 551 + PRODUCT_NAME = "$(TARGET_NAME)"; 552 + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 553 + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 554 + SWIFT_VERSION = 5.0; 555 + VERSIONING_SYSTEM = "apple-generic"; 556 + }; 557 + name = Debug; 558 + }; 559 + 97C147071CF9000F007C117D /* Release */ = { 560 + isa = XCBuildConfiguration; 561 + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 562 + buildSettings = { 563 + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 564 + CLANG_ENABLE_MODULES = YES; 565 + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 566 + ENABLE_BITCODE = NO; 567 + INFOPLIST_FILE = Runner/Info.plist; 568 + LD_RUNPATH_SEARCH_PATHS = ( 569 + "$(inherited)", 570 + "@executable_path/Frameworks", 571 + ); 572 + PRODUCT_BUNDLE_IDENTIFIER = com.example.lazurite; 573 + PRODUCT_NAME = "$(TARGET_NAME)"; 574 + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 575 + SWIFT_VERSION = 5.0; 576 + VERSIONING_SYSTEM = "apple-generic"; 577 + }; 578 + name = Release; 579 + }; 580 + /* End XCBuildConfiguration section */ 581 + 582 + /* Begin XCConfigurationList section */ 583 + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { 584 + isa = XCConfigurationList; 585 + buildConfigurations = ( 586 + 331C8088294A63A400263BE5 /* Debug */, 587 + 331C8089294A63A400263BE5 /* Release */, 588 + 331C808A294A63A400263BE5 /* Profile */, 589 + ); 590 + defaultConfigurationIsVisible = 0; 591 + defaultConfigurationName = Release; 592 + }; 593 + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 594 + isa = XCConfigurationList; 595 + buildConfigurations = ( 596 + 97C147031CF9000F007C117D /* Debug */, 597 + 97C147041CF9000F007C117D /* Release */, 598 + 249021D3217E4FDB00AE95B9 /* Profile */, 599 + ); 600 + defaultConfigurationIsVisible = 0; 601 + defaultConfigurationName = Release; 602 + }; 603 + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 604 + isa = XCConfigurationList; 605 + buildConfigurations = ( 606 + 97C147061CF9000F007C117D /* Debug */, 607 + 97C147071CF9000F007C117D /* Release */, 608 + 249021D4217E4FDB00AE95B9 /* Profile */, 609 + ); 610 + defaultConfigurationIsVisible = 0; 611 + defaultConfigurationName = Release; 612 + }; 613 + /* End XCConfigurationList section */ 614 + }; 615 + rootObject = 97C146E61CF9000F007C117D /* Project object */; 616 + }
+7
ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <Workspace 3 + version = "1.0"> 4 + <FileRef 5 + location = "self:"> 6 + </FileRef> 7 + </Workspace>
+8
ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 3 + <plist version="1.0"> 4 + <dict> 5 + <key>IDEDidComputeMac32BitWarning</key> 6 + <true/> 7 + </dict> 8 + </plist>
+8
ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 3 + <plist version="1.0"> 4 + <dict> 5 + <key>PreviewsEnabled</key> 6 + <false/> 7 + </dict> 8 + </plist>
+101
ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <Scheme 3 + LastUpgradeVersion = "1510" 4 + version = "1.3"> 5 + <BuildAction 6 + parallelizeBuildables = "YES" 7 + buildImplicitDependencies = "YES"> 8 + <BuildActionEntries> 9 + <BuildActionEntry 10 + buildForTesting = "YES" 11 + buildForRunning = "YES" 12 + buildForProfiling = "YES" 13 + buildForArchiving = "YES" 14 + buildForAnalyzing = "YES"> 15 + <BuildableReference 16 + BuildableIdentifier = "primary" 17 + BlueprintIdentifier = "97C146ED1CF9000F007C117D" 18 + BuildableName = "Runner.app" 19 + BlueprintName = "Runner" 20 + ReferencedContainer = "container:Runner.xcodeproj"> 21 + </BuildableReference> 22 + </BuildActionEntry> 23 + </BuildActionEntries> 24 + </BuildAction> 25 + <TestAction 26 + buildConfiguration = "Debug" 27 + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" 28 + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" 29 + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" 30 + shouldUseLaunchSchemeArgsEnv = "YES"> 31 + <MacroExpansion> 32 + <BuildableReference 33 + BuildableIdentifier = "primary" 34 + BlueprintIdentifier = "97C146ED1CF9000F007C117D" 35 + BuildableName = "Runner.app" 36 + BlueprintName = "Runner" 37 + ReferencedContainer = "container:Runner.xcodeproj"> 38 + </BuildableReference> 39 + </MacroExpansion> 40 + <Testables> 41 + <TestableReference 42 + skipped = "NO" 43 + parallelizable = "YES"> 44 + <BuildableReference 45 + BuildableIdentifier = "primary" 46 + BlueprintIdentifier = "331C8080294A63A400263BE5" 47 + BuildableName = "RunnerTests.xctest" 48 + BlueprintName = "RunnerTests" 49 + ReferencedContainer = "container:Runner.xcodeproj"> 50 + </BuildableReference> 51 + </TestableReference> 52 + </Testables> 53 + </TestAction> 54 + <LaunchAction 55 + buildConfiguration = "Debug" 56 + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" 57 + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" 58 + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" 59 + launchStyle = "0" 60 + useCustomWorkingDirectory = "NO" 61 + ignoresPersistentStateOnLaunch = "NO" 62 + debugDocumentVersioning = "YES" 63 + debugServiceExtension = "internal" 64 + enableGPUValidationMode = "1" 65 + allowLocationSimulation = "YES"> 66 + <BuildableProductRunnable 67 + runnableDebuggingMode = "0"> 68 + <BuildableReference 69 + BuildableIdentifier = "primary" 70 + BlueprintIdentifier = "97C146ED1CF9000F007C117D" 71 + BuildableName = "Runner.app" 72 + BlueprintName = "Runner" 73 + ReferencedContainer = "container:Runner.xcodeproj"> 74 + </BuildableReference> 75 + </BuildableProductRunnable> 76 + </LaunchAction> 77 + <ProfileAction 78 + buildConfiguration = "Profile" 79 + shouldUseLaunchSchemeArgsEnv = "YES" 80 + savedToolIdentifier = "" 81 + useCustomWorkingDirectory = "NO" 82 + debugDocumentVersioning = "YES"> 83 + <BuildableProductRunnable 84 + runnableDebuggingMode = "0"> 85 + <BuildableReference 86 + BuildableIdentifier = "primary" 87 + BlueprintIdentifier = "97C146ED1CF9000F007C117D" 88 + BuildableName = "Runner.app" 89 + BlueprintName = "Runner" 90 + ReferencedContainer = "container:Runner.xcodeproj"> 91 + </BuildableReference> 92 + </BuildableProductRunnable> 93 + </ProfileAction> 94 + <AnalyzeAction 95 + buildConfiguration = "Debug"> 96 + </AnalyzeAction> 97 + <ArchiveAction 98 + buildConfiguration = "Release" 99 + revealArchiveInOrganizer = "YES"> 100 + </ArchiveAction> 101 + </Scheme>
+7
ios/Runner.xcworkspace/contents.xcworkspacedata
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <Workspace 3 + version = "1.0"> 4 + <FileRef 5 + location = "group:Runner.xcodeproj"> 6 + </FileRef> 7 + </Workspace>
+8
ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 3 + <plist version="1.0"> 4 + <dict> 5 + <key>IDEDidComputeMac32BitWarning</key> 6 + <true/> 7 + </dict> 8 + </plist>
+8
ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 3 + <plist version="1.0"> 4 + <dict> 5 + <key>PreviewsEnabled</key> 6 + <false/> 7 + </dict> 8 + </plist>
+13
ios/Runner/AppDelegate.swift
··· 1 + import Flutter 2 + import UIKit 3 + 4 + @main 5 + @objc class AppDelegate: FlutterAppDelegate { 6 + override func application( 7 + _ application: UIApplication, 8 + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 + ) -> Bool { 10 + GeneratedPluginRegistrant.register(with: self) 11 + return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 + } 13 + }
+122
ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
··· 1 + { 2 + "images" : [ 3 + { 4 + "size" : "20x20", 5 + "idiom" : "iphone", 6 + "filename" : "Icon-App-20x20@2x.png", 7 + "scale" : "2x" 8 + }, 9 + { 10 + "size" : "20x20", 11 + "idiom" : "iphone", 12 + "filename" : "Icon-App-20x20@3x.png", 13 + "scale" : "3x" 14 + }, 15 + { 16 + "size" : "29x29", 17 + "idiom" : "iphone", 18 + "filename" : "Icon-App-29x29@1x.png", 19 + "scale" : "1x" 20 + }, 21 + { 22 + "size" : "29x29", 23 + "idiom" : "iphone", 24 + "filename" : "Icon-App-29x29@2x.png", 25 + "scale" : "2x" 26 + }, 27 + { 28 + "size" : "29x29", 29 + "idiom" : "iphone", 30 + "filename" : "Icon-App-29x29@3x.png", 31 + "scale" : "3x" 32 + }, 33 + { 34 + "size" : "40x40", 35 + "idiom" : "iphone", 36 + "filename" : "Icon-App-40x40@2x.png", 37 + "scale" : "2x" 38 + }, 39 + { 40 + "size" : "40x40", 41 + "idiom" : "iphone", 42 + "filename" : "Icon-App-40x40@3x.png", 43 + "scale" : "3x" 44 + }, 45 + { 46 + "size" : "60x60", 47 + "idiom" : "iphone", 48 + "filename" : "Icon-App-60x60@2x.png", 49 + "scale" : "2x" 50 + }, 51 + { 52 + "size" : "60x60", 53 + "idiom" : "iphone", 54 + "filename" : "Icon-App-60x60@3x.png", 55 + "scale" : "3x" 56 + }, 57 + { 58 + "size" : "20x20", 59 + "idiom" : "ipad", 60 + "filename" : "Icon-App-20x20@1x.png", 61 + "scale" : "1x" 62 + }, 63 + { 64 + "size" : "20x20", 65 + "idiom" : "ipad", 66 + "filename" : "Icon-App-20x20@2x.png", 67 + "scale" : "2x" 68 + }, 69 + { 70 + "size" : "29x29", 71 + "idiom" : "ipad", 72 + "filename" : "Icon-App-29x29@1x.png", 73 + "scale" : "1x" 74 + }, 75 + { 76 + "size" : "29x29", 77 + "idiom" : "ipad", 78 + "filename" : "Icon-App-29x29@2x.png", 79 + "scale" : "2x" 80 + }, 81 + { 82 + "size" : "40x40", 83 + "idiom" : "ipad", 84 + "filename" : "Icon-App-40x40@1x.png", 85 + "scale" : "1x" 86 + }, 87 + { 88 + "size" : "40x40", 89 + "idiom" : "ipad", 90 + "filename" : "Icon-App-40x40@2x.png", 91 + "scale" : "2x" 92 + }, 93 + { 94 + "size" : "76x76", 95 + "idiom" : "ipad", 96 + "filename" : "Icon-App-76x76@1x.png", 97 + "scale" : "1x" 98 + }, 99 + { 100 + "size" : "76x76", 101 + "idiom" : "ipad", 102 + "filename" : "Icon-App-76x76@2x.png", 103 + "scale" : "2x" 104 + }, 105 + { 106 + "size" : "83.5x83.5", 107 + "idiom" : "ipad", 108 + "filename" : "Icon-App-83.5x83.5@2x.png", 109 + "scale" : "2x" 110 + }, 111 + { 112 + "size" : "1024x1024", 113 + "idiom" : "ios-marketing", 114 + "filename" : "Icon-App-1024x1024@1x.png", 115 + "scale" : "1x" 116 + } 117 + ], 118 + "info" : { 119 + "version" : 1, 120 + "author" : "xcode" 121 + } 122 + }
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png

This is a binary file and will not be displayed.

ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png

This is a binary file and will not be displayed.

ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png

This is a binary file and will not be displayed.

ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png

This is a binary file and will not be displayed.

ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png

This is a binary file and will not be displayed.

ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png

This is a binary file and will not be displayed.

ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png

This is a binary file and will not be displayed.

ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png

This is a binary file and will not be displayed.

ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png

This is a binary file and will not be displayed.

ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png

This is a binary file and will not be displayed.

ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png

This is a binary file and will not be displayed.

ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png

This is a binary file and will not be displayed.

ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png

This is a binary file and will not be displayed.

ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png

This is a binary file and will not be displayed.

ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png

This is a binary file and will not be displayed.

+23
ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
··· 1 + { 2 + "images" : [ 3 + { 4 + "idiom" : "universal", 5 + "filename" : "LaunchImage.png", 6 + "scale" : "1x" 7 + }, 8 + { 9 + "idiom" : "universal", 10 + "filename" : "LaunchImage@2x.png", 11 + "scale" : "2x" 12 + }, 13 + { 14 + "idiom" : "universal", 15 + "filename" : "LaunchImage@3x.png", 16 + "scale" : "3x" 17 + } 18 + ], 19 + "info" : { 20 + "version" : 1, 21 + "author" : "xcode" 22 + } 23 + }
ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png

This is a binary file and will not be displayed.

ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png

This is a binary file and will not be displayed.

ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png

This is a binary file and will not be displayed.

+5
ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
··· 1 + # Launch Screen Assets 2 + 3 + You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 + 5 + You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
+37
ios/Runner/Base.lproj/LaunchScreen.storyboard
··· 1 + <?xml version="1.0" encoding="UTF-8" standalone="no"?> 2 + <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM"> 3 + <dependencies> 4 + <deployment identifier="iOS"/> 5 + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/> 6 + </dependencies> 7 + <scenes> 8 + <!--View Controller--> 9 + <scene sceneID="EHf-IW-A2E"> 10 + <objects> 11 + <viewController id="01J-lp-oVM" sceneMemberID="viewController"> 12 + <layoutGuides> 13 + <viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/> 14 + <viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/> 15 + </layoutGuides> 16 + <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> 17 + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> 18 + <subviews> 19 + <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"> 20 + </imageView> 21 + </subviews> 22 + <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> 23 + <constraints> 24 + <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/> 25 + <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/> 26 + </constraints> 27 + </view> 28 + </viewController> 29 + <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> 30 + </objects> 31 + <point key="canvasLocation" x="53" y="375"/> 32 + </scene> 33 + </scenes> 34 + <resources> 35 + <image name="LaunchImage" width="168" height="185"/> 36 + </resources> 37 + </document>
+26
ios/Runner/Base.lproj/Main.storyboard
··· 1 + <?xml version="1.0" encoding="UTF-8" standalone="no"?> 2 + <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r"> 3 + <dependencies> 4 + <deployment identifier="iOS"/> 5 + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/> 6 + </dependencies> 7 + <scenes> 8 + <!--Flutter View Controller--> 9 + <scene sceneID="tne-QT-ifu"> 10 + <objects> 11 + <viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController"> 12 + <layoutGuides> 13 + <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/> 14 + <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/> 15 + </layoutGuides> 16 + <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> 17 + <rect key="frame" x="0.0" y="0.0" width="600" height="600"/> 18 + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> 19 + <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/> 20 + </view> 21 + </viewController> 22 + <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> 23 + </objects> 24 + </scene> 25 + </scenes> 26 + </document>
+49
ios/Runner/Info.plist
··· 1 + <?xml version="1.0" encoding="UTF-8"?> 2 + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 3 + <plist version="1.0"> 4 + <dict> 5 + <key>CFBundleDevelopmentRegion</key> 6 + <string>$(DEVELOPMENT_LANGUAGE)</string> 7 + <key>CFBundleDisplayName</key> 8 + <string>Lazurite</string> 9 + <key>CFBundleExecutable</key> 10 + <string>$(EXECUTABLE_NAME)</string> 11 + <key>CFBundleIdentifier</key> 12 + <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> 13 + <key>CFBundleInfoDictionaryVersion</key> 14 + <string>6.0</string> 15 + <key>CFBundleName</key> 16 + <string>lazurite</string> 17 + <key>CFBundlePackageType</key> 18 + <string>APPL</string> 19 + <key>CFBundleShortVersionString</key> 20 + <string>$(FLUTTER_BUILD_NAME)</string> 21 + <key>CFBundleSignature</key> 22 + <string>????</string> 23 + <key>CFBundleVersion</key> 24 + <string>$(FLUTTER_BUILD_NUMBER)</string> 25 + <key>LSRequiresIPhoneOS</key> 26 + <true/> 27 + <key>UILaunchStoryboardName</key> 28 + <string>LaunchScreen</string> 29 + <key>UIMainStoryboardFile</key> 30 + <string>Main</string> 31 + <key>UISupportedInterfaceOrientations</key> 32 + <array> 33 + <string>UIInterfaceOrientationPortrait</string> 34 + <string>UIInterfaceOrientationLandscapeLeft</string> 35 + <string>UIInterfaceOrientationLandscapeRight</string> 36 + </array> 37 + <key>UISupportedInterfaceOrientations~ipad</key> 38 + <array> 39 + <string>UIInterfaceOrientationPortrait</string> 40 + <string>UIInterfaceOrientationPortraitUpsideDown</string> 41 + <string>UIInterfaceOrientationLandscapeLeft</string> 42 + <string>UIInterfaceOrientationLandscapeRight</string> 43 + </array> 44 + <key>CADisableMinimumFrameDurationOnPhone</key> 45 + <true/> 46 + <key>UIApplicationSupportsIndirectInputEvents</key> 47 + <true/> 48 + </dict> 49 + </plist>
+1
ios/Runner/Runner-Bridging-Header.h
··· 1 + #import "GeneratedPluginRegistrant.h"
+12
ios/RunnerTests/RunnerTests.swift
··· 1 + import Flutter 2 + import UIKit 3 + import XCTest 4 + 5 + class RunnerTests: XCTestCase { 6 + 7 + func testExample() { 8 + // If you add code to the Runner application, consider adding tests here. 9 + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 + } 11 + 12 + }
+122
lib/main.dart
··· 1 + import 'package:flutter/material.dart'; 2 + 3 + void main() { 4 + runApp(const MyApp()); 5 + } 6 + 7 + class MyApp extends StatelessWidget { 8 + const MyApp({super.key}); 9 + 10 + // This widget is the root of your application. 11 + @override 12 + Widget build(BuildContext context) { 13 + return MaterialApp( 14 + title: 'Flutter Demo', 15 + theme: ThemeData( 16 + // This is the theme of your application. 17 + // 18 + // TRY THIS: Try running your application with "flutter run". You'll see 19 + // the application has a purple toolbar. Then, without quitting the app, 20 + // try changing the seedColor in the colorScheme below to Colors.green 21 + // and then invoke "hot reload" (save your changes or press the "hot 22 + // reload" button in a Flutter-supported IDE, or press "r" if you used 23 + // the command line to start the app). 24 + // 25 + // Notice that the counter didn't reset back to zero; the application 26 + // state is not lost during the reload. To reset the state, use hot 27 + // restart instead. 28 + // 29 + // This works for code too, not just values: Most code changes can be 30 + // tested with just a hot reload. 31 + colorScheme: .fromSeed(seedColor: Colors.deepPurple), 32 + ), 33 + home: const MyHomePage(title: 'Flutter Demo Home Page'), 34 + ); 35 + } 36 + } 37 + 38 + class MyHomePage extends StatefulWidget { 39 + const MyHomePage({super.key, required this.title}); 40 + 41 + // This widget is the home page of your application. It is stateful, meaning 42 + // that it has a State object (defined below) that contains fields that affect 43 + // how it looks. 44 + 45 + // This class is the configuration for the state. It holds the values (in this 46 + // case the title) provided by the parent (in this case the App widget) and 47 + // used by the build method of the State. Fields in a Widget subclass are 48 + // always marked "final". 49 + 50 + final String title; 51 + 52 + @override 53 + State<MyHomePage> createState() => _MyHomePageState(); 54 + } 55 + 56 + class _MyHomePageState extends State<MyHomePage> { 57 + int _counter = 0; 58 + 59 + void _incrementCounter() { 60 + setState(() { 61 + // This call to setState tells the Flutter framework that something has 62 + // changed in this State, which causes it to rerun the build method below 63 + // so that the display can reflect the updated values. If we changed 64 + // _counter without calling setState(), then the build method would not be 65 + // called again, and so nothing would appear to happen. 66 + _counter++; 67 + }); 68 + } 69 + 70 + @override 71 + Widget build(BuildContext context) { 72 + // This method is rerun every time setState is called, for instance as done 73 + // by the _incrementCounter method above. 74 + // 75 + // The Flutter framework has been optimized to make rerunning build methods 76 + // fast, so that you can just rebuild anything that needs updating rather 77 + // than having to individually change instances of widgets. 78 + return Scaffold( 79 + appBar: AppBar( 80 + // TRY THIS: Try changing the color here to a specific color (to 81 + // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar 82 + // change color while the other colors stay the same. 83 + backgroundColor: Theme.of(context).colorScheme.inversePrimary, 84 + // Here we take the value from the MyHomePage object that was created by 85 + // the App.build method, and use it to set our appbar title. 86 + title: Text(widget.title), 87 + ), 88 + body: Center( 89 + // Center is a layout widget. It takes a single child and positions it 90 + // in the middle of the parent. 91 + child: Column( 92 + // Column is also a layout widget. It takes a list of children and 93 + // arranges them vertically. By default, it sizes itself to fit its 94 + // children horizontally, and tries to be as tall as its parent. 95 + // 96 + // Column has various properties to control how it sizes itself and 97 + // how it positions its children. Here we use mainAxisAlignment to 98 + // center the children vertically; the main axis here is the vertical 99 + // axis because Columns are vertical (the cross axis would be 100 + // horizontal). 101 + // 102 + // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" 103 + // action in the IDE, or press "p" in the console), to see the 104 + // wireframe for each widget. 105 + mainAxisAlignment: .center, 106 + children: [ 107 + const Text('You have pushed the button this many times:'), 108 + Text( 109 + '$_counter', 110 + style: Theme.of(context).textTheme.headlineMedium, 111 + ), 112 + ], 113 + ), 114 + ), 115 + floatingActionButton: FloatingActionButton( 116 + onPressed: _incrementCounter, 117 + tooltip: 'Increment', 118 + child: const Icon(Icons.add), 119 + ), 120 + ); 121 + } 122 + }
+554
pubspec.lock
··· 1 + # Generated by pub 2 + # See https://dart.dev/tools/pub/glossary#lockfile 3 + packages: 4 + async: 5 + dependency: transitive 6 + description: 7 + name: async 8 + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" 9 + url: "https://pub.dev" 10 + source: hosted 11 + version: "2.13.0" 12 + at_primitives: 13 + dependency: transitive 14 + description: 15 + name: at_primitives 16 + sha256: "016ffaa419b01befe6f5c6869c84d570060a5388655354a25668dbb1ad8ade3f" 17 + url: "https://pub.dev" 18 + source: hosted 19 + version: "1.0.0" 20 + atproto: 21 + dependency: transitive 22 + description: 23 + name: atproto 24 + sha256: "115944b8e4859bdaa0fe0d5162725ddf80269f14e79a33ab7ee05db972932ef9" 25 + url: "https://pub.dev" 26 + source: hosted 27 + version: "1.4.1" 28 + atproto_core: 29 + dependency: transitive 30 + description: 31 + name: atproto_core 32 + sha256: "76b8c6237cf0c446c2f056cd76723eb5d7993ecaf7f7cc417b1b6bf26d8f24ce" 33 + url: "https://pub.dev" 34 + source: hosted 35 + version: "1.2.0" 36 + atproto_oauth: 37 + dependency: "direct main" 38 + description: 39 + name: atproto_oauth 40 + sha256: "42a3a98dae11eb8c2a8a83051ebda9d0a711aec998c280c4f94a1a7127ba0aed" 41 + url: "https://pub.dev" 42 + source: hosted 43 + version: "0.2.0" 44 + base_codecs: 45 + dependency: transitive 46 + description: 47 + name: base_codecs 48 + sha256: "41701a12ede9912663decd708279924ece5018566daa7d1f484d5f4f10894f91" 49 + url: "https://pub.dev" 50 + source: hosted 51 + version: "1.0.1" 52 + bloc: 53 + dependency: transitive 54 + description: 55 + name: bloc 56 + sha256: a48653a82055a900b88cd35f92429f068c5a8057ae9b136d197b3d56c57efb81 57 + url: "https://pub.dev" 58 + source: hosted 59 + version: "9.2.0" 60 + bluesky: 61 + dependency: "direct main" 62 + description: 63 + name: bluesky 64 + sha256: "21539161860842fde4f03094d2e3f117624808b606c6a6f5905bf1238683dc4f" 65 + url: "https://pub.dev" 66 + source: hosted 67 + version: "1.4.1" 68 + bluesky_text: 69 + dependency: "direct main" 70 + description: 71 + name: bluesky_text 72 + sha256: effbb9dc7579744b1a24fff6e79e6ce9285162f29833afd427eaa9d53f4c0daa 73 + url: "https://pub.dev" 74 + source: hosted 75 + version: "1.1.1" 76 + boolean_selector: 77 + dependency: transitive 78 + description: 79 + name: boolean_selector 80 + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" 81 + url: "https://pub.dev" 82 + source: hosted 83 + version: "2.1.2" 84 + buffer: 85 + dependency: transitive 86 + description: 87 + name: buffer 88 + sha256: "389da2ec2c16283c8787e0adaede82b1842102f8c8aae2f49003a766c5c6b3d1" 89 + url: "https://pub.dev" 90 + source: hosted 91 + version: "1.2.3" 92 + cbor: 93 + dependency: transitive 94 + description: 95 + name: cbor 96 + sha256: "2c5c37650f0a2d25149f03e748ab7b2857787bde338f95fe947738b80d713da2" 97 + url: "https://pub.dev" 98 + source: hosted 99 + version: "6.5.1" 100 + characters: 101 + dependency: transitive 102 + description: 103 + name: characters 104 + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 105 + url: "https://pub.dev" 106 + source: hosted 107 + version: "1.4.0" 108 + clock: 109 + dependency: transitive 110 + description: 111 + name: clock 112 + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b 113 + url: "https://pub.dev" 114 + source: hosted 115 + version: "1.1.2" 116 + code_assets: 117 + dependency: transitive 118 + description: 119 + name: code_assets 120 + sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687" 121 + url: "https://pub.dev" 122 + source: hosted 123 + version: "1.0.0" 124 + collection: 125 + dependency: transitive 126 + description: 127 + name: collection 128 + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" 129 + url: "https://pub.dev" 130 + source: hosted 131 + version: "1.19.1" 132 + convert: 133 + dependency: transitive 134 + description: 135 + name: convert 136 + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 137 + url: "https://pub.dev" 138 + source: hosted 139 + version: "3.1.2" 140 + crypto: 141 + dependency: transitive 142 + description: 143 + name: crypto 144 + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf 145 + url: "https://pub.dev" 146 + source: hosted 147 + version: "3.0.7" 148 + cupertino_icons: 149 + dependency: "direct main" 150 + description: 151 + name: cupertino_icons 152 + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 153 + url: "https://pub.dev" 154 + source: hosted 155 + version: "1.0.8" 156 + dart_multihash: 157 + dependency: transitive 158 + description: 159 + name: dart_multihash 160 + sha256: "7bef7091497c531f94bf82102805a69d97e4e5d120000dcbbc4a1da679060e0a" 161 + url: "https://pub.dev" 162 + source: hosted 163 + version: "1.0.1" 164 + drift: 165 + dependency: "direct main" 166 + description: 167 + name: drift 168 + sha256: "61f876c0291b194980bafd203f48e85d5fb04e4a7334367d1a89f44004dbcb83" 169 + url: "https://pub.dev" 170 + source: hosted 171 + version: "2.32.0" 172 + fake_async: 173 + dependency: transitive 174 + description: 175 + name: fake_async 176 + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" 177 + url: "https://pub.dev" 178 + source: hosted 179 + version: "1.3.3" 180 + ffi: 181 + dependency: transitive 182 + description: 183 + name: ffi 184 + sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" 185 + url: "https://pub.dev" 186 + source: hosted 187 + version: "2.2.0" 188 + file: 189 + dependency: transitive 190 + description: 191 + name: file 192 + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 193 + url: "https://pub.dev" 194 + source: hosted 195 + version: "7.0.1" 196 + flutter: 197 + dependency: "direct main" 198 + description: flutter 199 + source: sdk 200 + version: "0.0.0" 201 + flutter_bloc: 202 + dependency: "direct main" 203 + description: 204 + name: flutter_bloc 205 + sha256: cf51747952201a455a1c840f8171d273be009b932c75093020f9af64f2123e38 206 + url: "https://pub.dev" 207 + source: hosted 208 + version: "9.1.1" 209 + flutter_lints: 210 + dependency: "direct dev" 211 + description: 212 + name: flutter_lints 213 + sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" 214 + url: "https://pub.dev" 215 + source: hosted 216 + version: "6.0.0" 217 + flutter_test: 218 + dependency: "direct dev" 219 + description: flutter 220 + source: sdk 221 + version: "0.0.0" 222 + flutter_web_plugins: 223 + dependency: transitive 224 + description: flutter 225 + source: sdk 226 + version: "0.0.0" 227 + freezed_annotation: 228 + dependency: transitive 229 + description: 230 + name: freezed_annotation 231 + sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8" 232 + url: "https://pub.dev" 233 + source: hosted 234 + version: "3.1.0" 235 + glob: 236 + dependency: transitive 237 + description: 238 + name: glob 239 + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de 240 + url: "https://pub.dev" 241 + source: hosted 242 + version: "2.1.3" 243 + go_router: 244 + dependency: "direct main" 245 + description: 246 + name: go_router 247 + sha256: "7974313e217a7771557add6ff2238acb63f635317c35fa590d348fb238f00896" 248 + url: "https://pub.dev" 249 + source: hosted 250 + version: "17.1.0" 251 + hex: 252 + dependency: transitive 253 + description: 254 + name: hex 255 + sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a" 256 + url: "https://pub.dev" 257 + source: hosted 258 + version: "0.2.0" 259 + hooks: 260 + dependency: transitive 261 + description: 262 + name: hooks 263 + sha256: e79ed1e8e1929bc6ecb6ec85f0cb519c887aa5b423705ded0d0f2d9226def388 264 + url: "https://pub.dev" 265 + source: hosted 266 + version: "1.0.2" 267 + http: 268 + dependency: transitive 269 + description: 270 + name: http 271 + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" 272 + url: "https://pub.dev" 273 + source: hosted 274 + version: "1.6.0" 275 + http_parser: 276 + dependency: transitive 277 + description: 278 + name: http_parser 279 + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" 280 + url: "https://pub.dev" 281 + source: hosted 282 + version: "4.1.2" 283 + json_annotation: 284 + dependency: transitive 285 + description: 286 + name: json_annotation 287 + sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8 288 + url: "https://pub.dev" 289 + source: hosted 290 + version: "4.11.0" 291 + leak_tracker: 292 + dependency: transitive 293 + description: 294 + name: leak_tracker 295 + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" 296 + url: "https://pub.dev" 297 + source: hosted 298 + version: "11.0.2" 299 + leak_tracker_flutter_testing: 300 + dependency: transitive 301 + description: 302 + name: leak_tracker_flutter_testing 303 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" 304 + url: "https://pub.dev" 305 + source: hosted 306 + version: "3.0.10" 307 + leak_tracker_testing: 308 + dependency: transitive 309 + description: 310 + name: leak_tracker_testing 311 + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" 312 + url: "https://pub.dev" 313 + source: hosted 314 + version: "3.0.2" 315 + lints: 316 + dependency: transitive 317 + description: 318 + name: lints 319 + sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" 320 + url: "https://pub.dev" 321 + source: hosted 322 + version: "6.1.0" 323 + logging: 324 + dependency: transitive 325 + description: 326 + name: logging 327 + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 328 + url: "https://pub.dev" 329 + source: hosted 330 + version: "1.3.0" 331 + matcher: 332 + dependency: transitive 333 + description: 334 + name: matcher 335 + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 336 + url: "https://pub.dev" 337 + source: hosted 338 + version: "0.12.17" 339 + material_color_utilities: 340 + dependency: transitive 341 + description: 342 + name: material_color_utilities 343 + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec 344 + url: "https://pub.dev" 345 + source: hosted 346 + version: "0.11.1" 347 + meta: 348 + dependency: transitive 349 + description: 350 + name: meta 351 + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" 352 + url: "https://pub.dev" 353 + source: hosted 354 + version: "1.17.0" 355 + mime: 356 + dependency: transitive 357 + description: 358 + name: mime 359 + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" 360 + url: "https://pub.dev" 361 + source: hosted 362 + version: "2.0.0" 363 + multiformats: 364 + dependency: transitive 365 + description: 366 + name: multiformats 367 + sha256: a6e85f37aa228e82b5644fc68d28f76622fe894d9191ebd1f994296325a89482 368 + url: "https://pub.dev" 369 + source: hosted 370 + version: "1.0.2" 371 + nanoid: 372 + dependency: transitive 373 + description: 374 + name: nanoid 375 + sha256: be3f8752d9046c825df2f3914195151eb876f3ad64b9d833dd0b799b77b8759e 376 + url: "https://pub.dev" 377 + source: hosted 378 + version: "1.0.0" 379 + native_toolchain_c: 380 + dependency: transitive 381 + description: 382 + name: native_toolchain_c 383 + sha256: "92b2ca62c8bd2b8d2f267cdfccf9bfbdb7322f778f8f91b3ce5b5cda23a3899f" 384 + url: "https://pub.dev" 385 + source: hosted 386 + version: "0.17.5" 387 + nested: 388 + dependency: transitive 389 + description: 390 + name: nested 391 + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" 392 + url: "https://pub.dev" 393 + source: hosted 394 + version: "1.0.0" 395 + path: 396 + dependency: transitive 397 + description: 398 + name: path 399 + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" 400 + url: "https://pub.dev" 401 + source: hosted 402 + version: "1.9.1" 403 + pointycastle: 404 + dependency: transitive 405 + description: 406 + name: pointycastle 407 + sha256: "92aa3841d083cc4b0f4709b5c74fd6409a3e6ba833ffc7dc6a8fee096366acf5" 408 + url: "https://pub.dev" 409 + source: hosted 410 + version: "4.0.0" 411 + provider: 412 + dependency: transitive 413 + description: 414 + name: provider 415 + sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" 416 + url: "https://pub.dev" 417 + source: hosted 418 + version: "6.1.5+1" 419 + pub_semver: 420 + dependency: transitive 421 + description: 422 + name: pub_semver 423 + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" 424 + url: "https://pub.dev" 425 + source: hosted 426 + version: "2.2.0" 427 + sky_engine: 428 + dependency: transitive 429 + description: flutter 430 + source: sdk 431 + version: "0.0.0" 432 + source_span: 433 + dependency: transitive 434 + description: 435 + name: source_span 436 + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" 437 + url: "https://pub.dev" 438 + source: hosted 439 + version: "1.10.2" 440 + sqlite3: 441 + dependency: transitive 442 + description: 443 + name: sqlite3 444 + sha256: caa693ad15a587a2b4fde093b728131a1827903872171089dedb16f7665d3a91 445 + url: "https://pub.dev" 446 + source: hosted 447 + version: "3.2.0" 448 + stack_trace: 449 + dependency: transitive 450 + description: 451 + name: stack_trace 452 + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" 453 + url: "https://pub.dev" 454 + source: hosted 455 + version: "1.12.1" 456 + stream_channel: 457 + dependency: transitive 458 + description: 459 + name: stream_channel 460 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" 461 + url: "https://pub.dev" 462 + source: hosted 463 + version: "2.1.4" 464 + string_scanner: 465 + dependency: transitive 466 + description: 467 + name: string_scanner 468 + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" 469 + url: "https://pub.dev" 470 + source: hosted 471 + version: "1.4.1" 472 + term_glyph: 473 + dependency: transitive 474 + description: 475 + name: term_glyph 476 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" 477 + url: "https://pub.dev" 478 + source: hosted 479 + version: "1.2.2" 480 + test_api: 481 + dependency: transitive 482 + description: 483 + name: test_api 484 + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 485 + url: "https://pub.dev" 486 + source: hosted 487 + version: "0.7.7" 488 + typed_data: 489 + dependency: transitive 490 + description: 491 + name: typed_data 492 + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 493 + url: "https://pub.dev" 494 + source: hosted 495 + version: "1.4.0" 496 + vector_math: 497 + dependency: transitive 498 + description: 499 + name: vector_math 500 + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b 501 + url: "https://pub.dev" 502 + source: hosted 503 + version: "2.2.0" 504 + vm_service: 505 + dependency: transitive 506 + description: 507 + name: vm_service 508 + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" 509 + url: "https://pub.dev" 510 + source: hosted 511 + version: "15.0.2" 512 + web: 513 + dependency: transitive 514 + description: 515 + name: web 516 + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" 517 + url: "https://pub.dev" 518 + source: hosted 519 + version: "1.1.1" 520 + web_socket: 521 + dependency: transitive 522 + description: 523 + name: web_socket 524 + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" 525 + url: "https://pub.dev" 526 + source: hosted 527 + version: "1.0.1" 528 + web_socket_channel: 529 + dependency: transitive 530 + description: 531 + name: web_socket_channel 532 + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 533 + url: "https://pub.dev" 534 + source: hosted 535 + version: "3.0.3" 536 + xrpc: 537 + dependency: transitive 538 + description: 539 + name: xrpc 540 + sha256: ccaaf5224d1037a196bdfd03059027961bed3980718c9ce956be39d6bce152df 541 + url: "https://pub.dev" 542 + source: hosted 543 + version: "1.0.3" 544 + yaml: 545 + dependency: transitive 546 + description: 547 + name: yaml 548 + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce 549 + url: "https://pub.dev" 550 + source: hosted 551 + version: "3.1.3" 552 + sdks: 553 + dart: ">=3.10.1 <4.0.0" 554 + flutter: ">=3.35.0"
+83
pubspec.yaml
··· 1 + name: lazurite 2 + description: "A new Flutter project." 3 + # The following line prevents the package from being accidentally published to 4 + # pub.dev using `flutter pub publish`. This is preferred for private packages. 5 + publish_to: "none" # Remove this line if you wish to publish to pub.dev 6 + 7 + # The following defines the version and build number for your application. 8 + # A version number is three numbers separated by dots, like 1.2.43 9 + # followed by an optional build number separated by a +. 10 + # Both the version and the builder number may be overridden in flutter 11 + # build by specifying --build-name and --build-number, respectively. 12 + # In Android, build-name is used as versionName while build-number used as versionCode. 13 + # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 14 + # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. 15 + # Read more about iOS versioning at 16 + # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 17 + # In Windows, build-name is used as the major, minor, and patch parts 18 + # of the product and file versions while build-number is used as the build suffix. 19 + version: 1.0.0+1 20 + 21 + environment: 22 + sdk: ^3.10.1 23 + 24 + dependencies: 25 + flutter: 26 + sdk: flutter 27 + 28 + cupertino_icons: ^1.0.8 29 + bluesky: ^1.4.1 30 + bluesky_text: ^1.1.1 31 + atproto_oauth: ^0.2.0 32 + flutter_bloc: ^9.1.1 33 + drift: ^2.12.0 34 + go_router: ^17.1.0 35 + 36 + dev_dependencies: 37 + flutter_test: 38 + sdk: flutter 39 + 40 + # The "flutter_lints" package below contains a set of recommended lints to 41 + # encourage good coding practices. The lint set provided by the package is 42 + # activated in the `analysis_options.yaml` file located at the root of your 43 + # package. See that file for information about deactivating specific lint 44 + # rules and activating additional ones. 45 + flutter_lints: ^6.0.0 46 + 47 + # For information on the generic Dart part of this file, see the 48 + # following page: https://dart.dev/tools/pub/pubspec 49 + 50 + # The following section is specific to Flutter packages. 51 + flutter: 52 + uses-material-design: true 53 + 54 + # To add assets to your application, add an assets section, like this: 55 + # assets: 56 + # - images/a_dot_burr.jpeg 57 + # - images/a_dot_ham.jpeg 58 + 59 + # An image asset can refer to one or more resolution-specific "variants", see 60 + # https://flutter.dev/to/resolution-aware-images 61 + 62 + # For details regarding adding assets from package dependencies, see 63 + # https://flutter.dev/to/asset-from-package 64 + 65 + # To add custom fonts to your application, add a fonts section here, 66 + # in this "flutter" section. Each entry in this list should have a 67 + # "family" key with the font family name, and a "fonts" key with a 68 + # list giving the asset and other descriptors for the font. For 69 + # example: 70 + # fonts: 71 + # - family: Schyler 72 + # fonts: 73 + # - asset: fonts/Schyler-Regular.ttf 74 + # - asset: fonts/Schyler-Italic.ttf 75 + # style: italic 76 + # - family: Trajan Pro 77 + # fonts: 78 + # - asset: fonts/TrajanPro.ttf 79 + # - asset: fonts/TrajanPro_Bold.ttf 80 + # weight: 700 81 + # 82 + # For details regarding fonts from package dependencies, 83 + # see https://flutter.dev/to/font-from-package
+30
test/widget_test.dart
··· 1 + // This is a basic Flutter widget test. 2 + // 3 + // To perform an interaction with a widget in your test, use the WidgetTester 4 + // utility in the flutter_test package. For example, you can send tap and scroll 5 + // gestures. You can also use WidgetTester to find child widgets in the widget 6 + // tree, read text, and verify that the values of widget properties are correct. 7 + 8 + import 'package:flutter/material.dart'; 9 + import 'package:flutter_test/flutter_test.dart'; 10 + 11 + import 'package:lazurite/main.dart'; 12 + 13 + void main() { 14 + testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 + // Build our app and trigger a frame. 16 + await tester.pumpWidget(const MyApp()); 17 + 18 + // Verify that our counter starts at 0. 19 + expect(find.text('0'), findsOneWidget); 20 + expect(find.text('1'), findsNothing); 21 + 22 + // Tap the '+' icon and trigger a frame. 23 + await tester.tap(find.byIcon(Icons.add)); 24 + await tester.pump(); 25 + 26 + // Verify that our counter has incremented. 27 + expect(find.text('0'), findsNothing); 28 + expect(find.text('1'), findsOneWidget); 29 + }); 30 + }