Compare commits

..

No commits in common. "master" and "1.0.0-rc3" have entirely different histories.

300 changed files with 7946 additions and 4276 deletions

View file

@ -1,29 +0,0 @@
// Project generated by Kotlin Multiplatform Wizard
{
"spec": {
"template_id": "kmt",
"targets": {
"android": {
"ui": [
"compose"
]
},
"ios": {
"ui": [
"compose"
]
},
"desktop": {
"ui": [
"compose"
]
},
"web": {
"ui": [
"compose"
]
}
}
},
"timestamp": "2025-03-16T01:49:22.397134638Z"
}

23
.run/desktopApp.run.xml Normal file
View file

@ -0,0 +1,23 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="desktopApp" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$/desktopApp" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="run" />
</list>
</option>
<option name="vmOptions" value="" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
</configuration>
</component>

View file

@ -2,16 +2,9 @@ when:
event: push
branch: [ dockerBuild, master ]
matrix:
GRADLE_VERSION:
- 8.11.1
JDK_VERSION:
- jdk17
- jdk21
steps:
- name: build
image: gradle:${GRADLE_VERSION}-${JDK_VERSION}
image: gradle:8.10.2-jdk17
environment:
ANDROID_HOME: /sdk
commands:

View file

@ -2,16 +2,9 @@ when:
event: push
branch: [ dockerBuild, master ]
matrix:
GRADLE_VERSION:
- 8.11.1
JDK_VERSION:
- 17
- 21
steps:
- name: build
image: gradle:${GRADLE_VERSION}-${JDK_VERSION}
image: gradle:8.10.2-jdk17
environment:
ANDROID_HOME: /sdk
commands:

View file

@ -1,4 +1,3 @@
#file: noinspection YAMLSchemaValidation
when:
- event: tag
ref: ref/tags/0.0.*

View file

@ -2,15 +2,8 @@ when:
event: push
branch: [ dockerBuild, master ]
matrix:
GRADLE_VERSION:
- 8.11.1
JDK_VERSION:
- 17
- 21
steps:
- name: build
image: gradle:${GRADLE_VERSION}-${JDK_VERSION}
image: gradle:8.10.2-jdk21
commands:
- gradle wasmJsBrowserDistribution

View file

@ -1,11 +1,8 @@
# Original build used JVM 21.0.3
# Gradle version 8.10.2
ARG GRADLE_VERSION="8.11.1"
ARG JDK_VERSION="jdk21"
### For RPM Builds, need to implement Nebula RPM plugin; see https://plugins.gradle.org/plugin/com.netflix.nebula.rpm
FROM gradle:${GRADLE_VERSION}-${JDK_VERSION} AS menagerie_buildcache
FROM gradle:8.10.2-jdk21 AS cache
RUN apt update && apt install -y sdkmanager openjdk-11-jdk
RUN sdkmanager "tools"
RUN yes | sdkmanager --licenses
@ -13,12 +10,20 @@ RUN mkdir -p /src
ENV GRADLE_USER_HOME=/home/gradle/cache_home
COPY gradle /src/gradle
COPY build.gradle.kts gradle.properties settings.gradle.kts /src/.
COPY composeApp/build.gradle.kts /src/webApp/.
COPY androidApp/build.gradle.kts /src/androidApp/.
COPY automotiveApp/build.gradle.kts /src/automotiveApp/.
COPY desktopApp/build.gradle.kts /src/desktopApp/.
COPY shared/build.gradle.kts /src/shared/.
COPY tvApp/build.gradle.kts /src/tvApp/.
COPY webApp/build.gradle.kts /src/webApp/.
WORKDIR /src
RUN ls -lahR
RUN gradle -q javaToolchains
RUN gradle buildEnvironment --refresh-dependencies
FROM cache AS build
FROM gradle:8.10.2-jdk21 AS build
WORKDIR /src
COPY --from=cache /home/gradle/cache_home /home/gradle/.
COPY --chown=gradle:gradle . /src/.
RUN gradle clean && gradle packageReleaseDeb
RUN gradle clean
RUN gradle packageReleaseDeb

View file

@ -1,10 +1,6 @@
# Original build used JVM 21.0.3
# Gradle version 8.10.2
ARG GRADLE_VERSION="8.11.1"
ARG JDK_VERSION="jdk21"
FROM gradle:${GRADLE_VERSION}-${JDK_VERSION} AS cache
FROM gradle:8.10.2-jdk21 AS cache
RUN apt update && apt install -y sdkmanager openjdk-11-jdk
RUN sdkmanager "tools"
RUN yes | sdkmanager --licenses
@ -12,15 +8,20 @@ RUN mkdir -p /src
ENV GRADLE_USER_HOME=/home/gradle/cache_home
COPY gradle /src/gradle
COPY build.gradle.kts gradle.properties settings.gradle.kts /src/.
COPY composeApp/build.gradle.kts /src/webApp/.
COPY androidApp/build.gradle.kts /src/androidApp/.
COPY automotiveApp/build.gradle.kts /src/automotiveApp/.
COPY desktopApp/build.gradle.kts /src/desktopApp/.
COPY shared/build.gradle.kts /src/shared/.
COPY tvApp/build.gradle.kts /src/tvApp/.
COPY webApp/build.gradle.kts /src/webApp/.
WORKDIR /src
RUN gradle buildEnvironment --refresh-dependencies
FROM cache AS build
FROM gradle:8.10.2-jdk21 AS build
WORKDIR /src
COPY --from=cache /home/gradle/cache_home /home/gradle/.
COPY --chown=gradle:gradle . /src/.
RUN gradle clean && gradle wasmJsBrowserDistribution
FROM nginx:latest
COPY --from=build /src/composeApp/build/dist/wasmJs/productionExecutable /usr/share/nginx/html
COPY --from=build /src/webApp/build/dist/wasmJs/productionExecutable /usr/share/nginx/html

201
LICENSE.txt Normal file
View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2020-2021 JetBrains s.r.o. and and respective authors and developers.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -1,21 +0,0 @@
This is a Kotlin Multiplatform project targeting Android, iOS, Web, Desktop.
* `/composeApp` is for code that will be shared across your Compose Multiplatform applications.
It contains several subfolders:
- `commonMain` is for code thats common for all targets.
- Other folders are for Kotlin code that will be compiled for only the platform indicated in the folder name.
For example, if you want to use Apples CoreCrypto for the iOS part of your Kotlin app,
`iosMain` would be the right folder for such calls.
* `/iosApp` contains iOS applications. Even if youre sharing your UI with Compose Multiplatform,
you need this entry point for your iOS app. This is also where you should add SwiftUI code for your project.
Learn more about [Kotlin Multiplatform](https://www.jetbrains.com/help/kotlin-multiplatform-dev/get-started.html),
[Compose Multiplatform](https://github.com/JetBrains/compose-multiplatform/#compose-multiplatform),
[Kotlin/Wasm](https://kotl.in/wasm/)…
We would appreciate your feedback on Compose/Web and Kotlin/Wasm in the public Slack channel [#compose-web](https://slack-chats.kotlinlang.org/c/compose-web).
If you face any issues, please report them on [GitHub](https://github.com/JetBrains/compose-multiplatform/issues).
You can open the web application by running the `:composeApp:wasmJsBrowserDevelopmentRun` Gradle task.

View file

@ -0,0 +1,55 @@
plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.android.application)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.compose)
}
kotlin {
androidTarget()
sourceSets {
val androidMain by getting {
dependencies {
implementation(project(":shared"))
}
}
}
}
android {
compileSdk = (findProperty("android.compileSdk") as String).toInt()
namespace = "com.menagerie.cookbook"
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig {
minSdk = (findProperty("android.minSdk") as String).toInt()
targetSdk = (findProperty("android.targetSdk") as String).toInt()
versionCode = 1
versionName = "1.0.0"
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
buildTypes {
getByName("release") {
signingConfig = signingConfigs.getByName("debug")
applicationVariants.all {
val variant = this
variant.outputs
.map { it as com.android.build.gradle.internal.api.BaseVariantOutputImpl }
.forEach { output ->
val outputFileName = "bakers-menagerie${variant.flavorName}_${variant.buildType.name}_${variant.versionName}.apk"
output.outputFileName = outputFileName
}
}
}
}
kotlin {
jvmToolchain(11)
}
}
dependencies {
implementation(libs.androidx.window)
}

View file

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@android:style/Theme.Material.Light.NoActionBar">
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<activity
android:exported="true"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|mnc|colorMode|density|fontScale|fontWeightAdjustment|keyboard|layoutDirection|locale|mcc|navigation|smallestScreenSize|touchscreen|uiMode"
android:name=".MainActivity">
android:name="com.cookbook.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View file

@ -0,0 +1,19 @@
package com.cookbook
import MainView
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, true)
setContent {
MainView()
}
}
}

View file

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View file

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View file

@ -2,5 +2,4 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ophelia_background" />
<foreground android:drawable="@drawable/ophelia" />
<monochrome android:drawable="@drawable/ophelia"/>
</adaptive-icon>

View file

@ -2,5 +2,4 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ophelia_background" />
<foreground android:drawable="@drawable/ophelia" />
<monochrome android:drawable="@drawable/ophelia"/>
</adaptive-icon>

View file

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Before After
Before After

View file

@ -0,0 +1,39 @@
plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.android.application)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.compose)
}
kotlin {
androidTarget()
sourceSets {
val androidMain by getting {
dependencies {
implementation(project(":shared"))
}
}
}
}
android {
compileSdk = (findProperty("android.compileSdk") as String).toInt()
namespace = "com.menagerie.cookbook"
sourceSets["main"].manifest.srcFile("src/main/AndroidManifest.xml")
defaultConfig {
minSdk = (findProperty("android.minSdk") as String).toInt()
targetSdk = (findProperty("android.targetSdk") as String).toInt()
versionCode = 1
versionName = "1.0"
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlin {
jvmToolchain(11)
}
}

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature
android:name="android.hardware.type.automotive"
android:required="true" />
<uses-feature
android:name="android.software.leanback"
android:required="false" />
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<application
android:allowBackup="true"
android:appCategory="audio"
android:banner="@drawable/ic_launcher_foreground"
android:icon="@drawable/ic_launcher_foreground"
android:label="@string/app_name"
android:roundIcon="@drawable/ic_launcher_foreground"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<activity
android:name="com.recipe.automotiveapp.MainActivityAutomotive"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View file

@ -0,0 +1,15 @@
package com.recipe.automotiveapp
import MainView
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
class MainActivityAutomotive : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MainView(isLargeScreen = true)
}
}
}

View file

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View file

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View file

@ -0,0 +1,3 @@
<resources>
<string name="app_name">Baker\'s Menagerie</string>
</resources>

View file

@ -1,9 +1,19 @@
plugins {
// this is necessary to avoid the plugins to be loaded multiple times
// in each subproject's classloader
alias(libs.plugins.androidApplication) apply false
alias(libs.plugins.androidLibrary) apply false
alias(libs.plugins.composeMultiplatform) apply false
alias(libs.plugins.composeCompiler) apply false
alias(libs.plugins.kotlinMultiplatform) apply false
}
alias(libs.plugins.kotlin.multiplatform).apply(false)
alias(libs.plugins.android.application).apply(false)
alias(libs.plugins.android.library).apply(false)
alias(libs.plugins.compose).apply(false)
}
allprojects {
repositories {
google()
mavenCentral()
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
mavenLocal()
maven("https://maven.pkg.jetbrains.space/kotlin/p/wasm/experimental")
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev/")
}
}

10
cleanup.sh Executable file
View file

@ -0,0 +1,10 @@
#!/bin/sh
rm -rf .idea
./gradlew clean
rm -rf .gradle
rm -rf build
rm -rf */build
rm -rf iosApp/iosApp.xcworkspace
rm -rf iosApp/Pods
rm -rf iosApp/iosApp.xcodeproj/project.xcworkspace
rm -rf iosApp/iosApp.xcodeproj/xcuserdata

View file

@ -1,122 +0,0 @@
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidApplication)
alias(libs.plugins.composeMultiplatform)
alias(libs.plugins.composeCompiler)
}
kotlin {
androidTarget {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
}
}
jvm("desktop")
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
moduleName = "composeApp"
browser {
val rootDirPath = project.rootDir.path
val projectDirPath = project.projectDir.path
commonWebpackConfig {
outputFileName = "composeApp.js"
devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply {
static = (static ?: mutableListOf()).apply {
// Serve sources to debug inside browser
add(rootDirPath)
add(projectDirPath)
}
}
}
}
binaries.executable()
}
sourceSets {
val desktopMain by getting
androidMain.dependencies {
implementation(compose.preview)
implementation(libs.androidx.activity.compose)
}
commonMain.dependencies {
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material)
implementation(compose.ui)
implementation(compose.components.resources)
implementation(compose.components.uiToolingPreview)
implementation(libs.androidx.lifecycle.viewmodel)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.material3)
implementation(libs.androidx.navigation.compose)
implementation(libs.multiplatform.settings)
implementation(libs.multiplatform.settings.no.arg)
}
desktopMain.dependencies {
implementation(compose.desktop.currentOs)
implementation(libs.kotlinx.coroutines.swing)
}
}
}
android {
namespace = "com.menagerie.bakers"
compileSdk = libs.versions.android.compileSdk.get().toInt()
defaultConfig {
applicationId = "com.menagerie.bakers"
minSdk = libs.versions.android.minSdk.get().toInt()
targetSdk = libs.versions.android.targetSdk.get().toInt()
versionCode = 1
versionName = "1.0.0"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
buildTypes {
getByName("release") {
signingConfig = signingConfigs.getByName("debug")
applicationVariants.all {
val variant = this
variant.outputs
.map { it as com.android.build.gradle.internal.api.BaseVariantOutputImpl }
.forEach { output ->
val outputFileName = "bakers-menagerie${variant.flavorName}_${variant.buildType.name}_${variant.versionName}.apk"
output.outputFileName = outputFileName
}
}
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}
dependencies {
debugImplementation(compose.uiTooling)
}
compose.desktop {
application {
mainClass = "com.menagerie.bakers.MainKt"
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "com.menagerie.bakers"
packageVersion = "1.0.0"
}
}
}

View file

@ -1,31 +0,0 @@
package com.menagerie.bakers
import android.content.res.Configuration
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.ui.platform.LocalConfiguration
import androidx.core.view.WindowCompat
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, true)
DisplayManager.device = Device.Android
setContent {
DisplayManager.size = Size.Small
DisplayManager.orientation = when(LocalConfiguration.current.orientation) {
Configuration.ORIENTATION_PORTRAIT -> Orientation.Portrait
Configuration.ORIENTATION_LANDSCAPE -> Orientation.Landscape
else -> Orientation.Landscape
}
App()
}
}
}

View file

@ -1,6 +0,0 @@
package com.menagerie.bakers
import com.russhwolf.settings.BuildConfig
actual val isDebug: Boolean
get() = BuildConfig.DEBUG

View file

@ -1,22 +0,0 @@
package com.menagerie.bakers.util
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
class AndroidClipboardController(private val context: Context) : ClipboardController {
override fun copyToClipboard(text: String) {
val clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Copied Text", text)
clipboardManager.setPrimaryClip(clip)
}
}
@Composable
actual fun rememberClipboardController(): ClipboardController {
val context = LocalContext.current
return remember(context) { AndroidClipboardController(context) }
}

View file

@ -1,53 +0,0 @@
package com.menagerie.bakers.view
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.menagerie.bakers.SortBy
import com.menagerie.bakers.Theme
import com.menagerie.bakers.model.TTT
import com.menagerie.bakers.view.util.MainDropDown
@Composable
actual fun SettingsMenu(
theme: Theme,
onTheme: () -> Unit,
animate: Boolean,
onAnim: () -> Unit,
helpUs: Boolean,
onHelpToggle: (Boolean) -> Unit,
ttt: TTT,
onTTTToggle: () -> Unit,
andOr: Boolean,
onAndOr: () -> Unit,
sortBy: SortBy,
onSortToggle: () -> Unit,
descending: Boolean,
onDesc: () -> Unit,
onRandom: () -> Unit,
onBack: () -> Unit,
onClear: () -> Unit,
discreet: Boolean,
onDiscreet: () -> Unit,
) {
MainDropDown(
modifier = Modifier,
andOr = andOr,
descending = descending,
theme = theme,
sortBy = sortBy,
helpUs = helpUs,
animate = animate,
ttt = ttt,
onHelp = onHelpToggle,
onTTTToggle = onTTTToggle,
onTag = onAndOr,
onSortBy = onSortToggle,
onDesc = onDesc,
onTheme = onTheme,
onAnim = onAnim,
onRandom = onRandom,
onBack = {},
onDiscreet = onDiscreet,
discreet = discreet,
) { }
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M396-396q-32-32-58.5-67T289-537q-5 14-6.5 28.5T281-480q0 83 58 141t141 58q14 0 28.5-2t28.5-6q-39-22-74-48.5T396-396Zm57-56q51 51 114 87.5T702-308q-40 51-98 79.5T481-200q-117 0-198.5-81.5T201-480q0-65 28.5-123t79.5-98q20 72 56.5 135T453-452Zm290 72q-20-5-39.5-11T665-405q8-18 11.5-36.5T680-480q0-83-58.5-141.5T480-680q-20 0-38.5 3.5T405-665q-8-19-13.5-38T381-742q24-9 49-13.5t51-4.5q117 0 198.5 81.5T761-480q0 26-4.5 51T743-380ZM440-840v-120h80v120h-80Zm0 840v-120h80V0h-80Zm323-706-57-57 85-84 57 56-85 85ZM169-113l-57-56 85-85 57 57-85 84Zm671-327v-80h120v80H840ZM0-440v-80h120v80H0Zm791 328-85-85 57-57 84 85-56 57ZM197-706l-84-85 56-57 85 85-57 57Zm199 310Z"/></svg>

Before

Width:  |  Height:  |  Size: 785 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M480-120q-150 0-255-105T120-480q0-150 105-255t255-105q14 0 27.5 1t26.5 3q-41 29-65.5 75.5T444-660q0 90 63 153t153 63q55 0 101-24.5t75-65.5q2 13 3 26.5t1 27.5q0 150-105 255T480-120Zm0-80q88 0 158-48.5T740-375q-20 5-40 8t-40 3q-123 0-209.5-86.5T364-660q0-20 3-40t8-40q-78 32-126.5 102T200-480q0 116 82 198t198 82Zm-10-270Z"/></svg>

Before

Width:  |  Height:  |  Size: 445 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 311 KiB

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M480-120q-138 0-240.5-91.5T122-440h82q14 104 92.5 172T480-200q117 0 198.5-81.5T760-480q0-117-81.5-198.5T480-760q-69 0-129 32t-101 88h110v80H120v-240h80v94q51-64 124.5-99T480-840q75 0 140.5 28.5t114 77q48.5 48.5 77 114T840-480q0 75-28.5 140.5t-77 114q-48.5 48.5-114 77T480-120Zm112-192L440-464v-216h80v184l128 128-56 56Z"/></svg>

Before

Width:  |  Height:  |  Size: 444 B

View file

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="M480,840Q342,840 239.5,748.5Q137,657 122,520L204,520Q218,624 296.5,692Q375,760 480,760Q597,760 678.5,678.5Q760,597 760,480Q760,363 678.5,281.5Q597,200 480,200Q411,200 351,232Q291,264 250,320L360,320L360,400L120,400L120,160L200,160L200,254Q251,190 324.5,155Q398,120 480,120Q555,120 620.5,148.5Q686,177 734.5,225.5Q783,274 811.5,339.5Q840,405 840,480Q840,555 811.5,620.5Q783,686 734.5,734.5Q686,783 620.5,811.5Q555,840 480,840ZM592,648L440,496L440,280L520,280L520,464L648,592L592,648Z"
android:fillColor="#e3e3e3"/>
</vector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M480-360q50 0 85-35t35-85q0-50-35-85t-85-35q-50 0-85 35t-35 85q0 50 35 85t85 35Zm0 80q-83 0-141.5-58.5T280-480q0-83 58.5-141.5T480-680q83 0 141.5 58.5T680-480q0 83-58.5 141.5T480-280ZM200-440H40v-80h160v80Zm720 0H760v-80h160v80ZM440-760v-160h80v160h-80Zm0 720v-160h80v160h-80ZM256-650l-101-97 57-59 96 100-52 56Zm492 496-97-101 53-55 101 97-57 59Zm-98-550 97-101 59 57-100 96-56-52ZM154-212l101-97 55 53-97 101-59-57Zm326-268Z"/></svg>

Before

Width:  |  Height:  |  Size: 551 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M560-160v-80h104L537-367l57-57 126 126v-102h80v240H560Zm-344 0-56-56 504-504H560v-80h240v240h-80v-104L216-160Zm151-377L160-744l56-56 207 207-56 56Z"/></svg>

Before

Width:  |  Height:  |  Size: 272 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

View file

@ -1,765 +0,0 @@
package com.menagerie.bakers
import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionLayout
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.ExitToApp
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import bakersmenagerie.composeapp.generated.resources.Res
import bakersmenagerie.composeapp.generated.resources.auto_mode
import bakersmenagerie.composeapp.generated.resources.dark_mode
import bakersmenagerie.composeapp.generated.resources.history
import bakersmenagerie.composeapp.generated.resources.history_android
import bakersmenagerie.composeapp.generated.resources.light_mode
import bakersmenagerie.composeapp.generated.resources.shuffle
import bakersmenagerie.composeapp.generated.resources.tune
import com.menagerie.bakers.SettingsKeys.ANIM_KEY
import com.menagerie.bakers.SettingsKeys.AON_KEY
import com.menagerie.bakers.SettingsKeys.DESC_KEY
import com.menagerie.bakers.SettingsKeys.DISCREET_KEY
import com.menagerie.bakers.SettingsKeys.SORT_BY_KEY
import com.menagerie.bakers.SettingsKeys.THEME_KEY
import com.menagerie.bakers.SettingsKeys.TTT_KEY
import com.menagerie.bakers.ui.theme.MainTheme
import com.menagerie.bakers.view.details.RecipeDetails
import org.jetbrains.compose.resources.painterResource
import com.menagerie.bakers.view.list.RecipesListScreen
import com.menagerie.bakers.view.BookShelf
import com.menagerie.bakers.view.util.FilterCard
import com.menagerie.bakers.view.HomeScreen
import com.menagerie.bakers.view.util.InputFieldState
import com.menagerie.bakers.view.util.getFilteredRecipeList
import com.menagerie.bakers.view.util.getRecipeList
import com.menagerie.bakers.model.Recipe
import com.menagerie.bakers.model.TTT
import com.menagerie.bakers.model.TagType
import com.menagerie.bakers.view.SettingsMenu
import com.russhwolf.settings.Settings
enum class Theme {
Auto,
Light,
Dark
}
enum class SortBy {
Name,
Time,
Ingredients,
}
enum class RecipeAppScreen {
List,
History,
Details,
DetailHistory,
}
@OptIn(ExperimentalSharedTransitionApi::class) //This is a test commit comment
@Composable
fun App(
onClose: () -> Unit = {}
) {
//Grab an instance of the Settings object
val settings by remember { mutableStateOf(Settings()) }
//used to navigate through the different compose screens
val navController = rememberNavController()
//Grab and set Theme from Settings
val themeSetting by remember { mutableStateOf(settings.getStringOrNull(THEME_KEY)) }
var theme by remember {
mutableStateOf(
when (themeSetting) {
Theme.Dark.name -> Theme.Dark
Theme.Light.name -> Theme.Light
else -> Theme.Auto
}
)
}
val isDarkTheme: Boolean? = when (theme) {
Theme.Dark -> true
Theme.Light -> false
Theme.Auto -> null
}
val onThemeToggle = {
theme = when (theme) {
Theme.Auto -> Theme.Light
Theme.Light -> Theme.Dark
Theme.Dark -> Theme.Auto
}
when (theme) {
Theme.Auto -> settings.putString(THEME_KEY, Theme.Auto.name)
Theme.Light -> settings.putString(THEME_KEY, Theme.Light.name)
Theme.Dark -> settings.putString(THEME_KEY, Theme.Dark.name)
}
}
// END Theme
// Grab and set Sort Order from Settings
val sortBySetting by remember { mutableStateOf(settings.getStringOrNull(SORT_BY_KEY)) }
var sortBy by remember {
mutableStateOf(
when (sortBySetting) {
SortBy.Name.name -> SortBy.Name
SortBy.Time.name -> SortBy.Time
SortBy.Ingredients.name -> SortBy.Ingredients
else -> SortBy.Name
}
)
}
val onSortToggle = {
sortBy = when (sortBy) {
SortBy.Name -> SortBy.Time
SortBy.Time -> SortBy.Ingredients
SortBy.Ingredients -> SortBy.Name
}
when (sortBy) {
SortBy.Name -> settings.putString(SORT_BY_KEY, SortBy.Name.name)
SortBy.Time -> settings.putString(SORT_BY_KEY, SortBy.Time.name)
SortBy.Ingredients -> settings.putString(SORT_BY_KEY, SortBy.Ingredients.name)
}
}
// END SortBy
//Grab and set TTT Level from Settings
val tttSetting by remember { mutableStateOf(settings.getString(TTT_KEY, TTT.TESTED.name)) }
var tttBy by remember {
mutableStateOf(
when (tttSetting) {
TTT.TRIED.name -> TTT.TRIED
TTT.TESTED.name -> TTT.TESTED
TTT.TRUE.name -> TTT.TRUE
else -> TTT.TESTED
}
)
}
val onTTTToggle = {
tttBy = when (tttBy) {
TTT.TRIED -> TTT.TESTED
TTT.TESTED -> TTT.TRUE
TTT.TRUE -> TTT.TRIED
}
when (tttBy) {
TTT.TRIED -> settings.putString(TTT_KEY, TTT.TRIED.name)
TTT.TESTED -> settings.putString(TTT_KEY, TTT.TESTED.name)
TTT.TRUE -> settings.putString(TTT_KEY, TTT.TRUE.name)
}
}
// END TTT
//Grab and set And/Or/Not from Settings
val returnSetting by remember { mutableStateOf(settings.getBoolean(AON_KEY, false)) }
var returnAnyMatch by remember { mutableStateOf(returnSetting) }
//END AON
//Grab and set Display Order from Settings
val descSetting by remember { mutableStateOf(settings.getBoolean(DESC_KEY, false)) }
var descending by remember { mutableStateOf(descSetting) }
//END Display Order
//Grab and set Animation State from Settings
val animSetting by remember { mutableStateOf(settings.getBoolean(ANIM_KEY, true)) }
var animate by remember { mutableStateOf(animSetting) }
val discreetSetting by remember {mutableStateOf(settings.getBoolean(DISCREET_KEY, false))}
var discreet by remember { mutableStateOf(discreetSetting) }
MainTheme(useDarkTheme = isDarkTheme ?: isSystemInDarkTheme()) {
var showFilter by remember { mutableStateOf(false) }
var menuBar by remember { mutableStateOf(false) }
var searchBar by remember { mutableStateOf(false) }
var history by remember { mutableStateOf(false) }
var search by remember { mutableStateOf("") }
val tags = remember { mutableStateListOf<String>() }
val recipeTags by remember { mutableStateOf(mutableMapOf<String, TagType>()) }
var book by remember { mutableStateOf("") }
val remove = remember { mutableStateListOf<String>() }
var helpUs by remember { mutableStateOf(false) }
val histogram = remember { mutableListOf<Recipe>() }
val filteredItems = getFilteredRecipeList(
tags = tags,
search = search,
lockTag = book,
sortBy = sortBy,
tttBy = tttBy,
showMissing = helpUs,
returnAny = returnAnyMatch,
reverse = descending,
)
recipeTags.clear()
for (recipe in filteredItems) {
for (tag in recipe.tags) {
if (!recipeTags.contains(tag.key))
recipeTags[tag.key] = tag.value
}
}
if (showFilter) {
FilterCard(
recipeTags = recipeTags,
activeTags = tags,
) {
if (it.isEmpty()) {
tags.clear()
}
for (item in it)
if (tags.contains(item).not())
tags.add(item)
remove.clear()
for (item in tags)
if (it.contains(item).not())
remove.add(item)
tags.removeAll(remove)
showFilter = false
}
}
val recipeCount = getRecipeList().size
var currentRecipe: Recipe
if (filteredItems.isNotEmpty())
currentRecipe = filteredItems.first()
Scaffold {
Column {
when (DisplayManager.device) {
Device.Android ->
if (menuBar)
Column {
Row {
SettingsMenu(
andOr = returnAnyMatch,
theme = theme,
sortBy = sortBy,
ttt = tttBy,
helpUs = helpUs,
descending = descending,
animate = animate,
discreet = discreet,
onDiscreet = {
discreet = !discreet
settings.putBoolean(DISCREET_KEY, discreet)
},
onAndOr = {
returnAnyMatch = !returnAnyMatch
settings.putBoolean(AON_KEY, returnAnyMatch)
},
onSortToggle = { onSortToggle.invoke() },
onDesc = {
descending = !descending
settings.putBoolean(DESC_KEY, descending)
},
onAnim = {
animate = !animate
settings.putBoolean(ANIM_KEY, animate)
},
onTheme = { onThemeToggle.invoke() },
onHelpToggle = { helpUs = !helpUs },
onTTTToggle = { onTTTToggle.invoke() },
onRandom = {
currentRecipe = filteredItems.random()
navController.navigate(
RecipeAppScreen.Details.name.plus(
"/${currentRecipe.title}"
)
)
},
onBack = {},
onClear = {settings.clear()}
)
Spacer(modifier = Modifier.weight(1f))
if(searchBar)
InputFieldState(
value = search,
label = "Search ".plus(book),
modifier = Modifier
.width(if (DisplayManager.size == Size.Large || DisplayManager.orientation == Orientation.Landscape) 400.dp else 250.dp)
.align(Alignment.CenterVertically)
) {
search = it
}
if (DisplayManager.orientation == Orientation.Portrait)
Spacer(modifier = Modifier.weight(1f))
if(histogram.isNotEmpty() && searchBar)
{
IconButton(
onClick = {
navController.navigate(RecipeAppScreen.History.name)
},
) {
Icon(painter = painterResource(Res.drawable.history_android), "History")
}
}
if(searchBar)
IconButton(
onClick = { showFilter = true },
) {
Icon(
painter = painterResource(Res.drawable.tune), "Filter"
)
}
}
val listState = rememberLazyGridState()
if(searchBar)
LazyVerticalGrid(
state = listState,
columns = GridCells.Adaptive(minSize = 125.dp)
)
{
items(tags.size) {
Button(onClick = { tags.remove(tags[it]) }) {
Text(text = tags[it])
}
}
}
}
Device.Desktop ->
if (menuBar)
Column {
Row {
IconButton(
onClick = { navController.popBackStack() },
content = {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Back"
)
}
)
IconButton(
onClick = { navController.navigate("Settings") },
content = {
Icon(
imageVector = Icons.Default.Settings,
contentDescription = ""
)
}
)
IconButton(
onClick = {onThemeToggle.invoke()},
content = {
Icon(
painter = painterResource(
when(theme) {
Theme.Auto -> Res.drawable.auto_mode
Theme.Light -> Res.drawable.light_mode
Theme.Dark -> Res.drawable.dark_mode
}
),
contentDescription = "theme"
)
}
)
if(history)
IconButton(
onClick = {
currentRecipe = histogram.random()
navController.navigate(
RecipeAppScreen.DetailHistory.name.plus(
"/${currentRecipe.title}"
)
)
},
content = {
Icon(
painter = painterResource(Res.drawable.shuffle),
contentDescription = "random"
)
}
)
else
IconButton(
onClick = {
currentRecipe = filteredItems.random()
navController.navigate(
RecipeAppScreen.Details.name.plus(
"/${currentRecipe.title}"
)
)
},
content = {
Icon(
painter = painterResource(Res.drawable.shuffle),
contentDescription = "random"
)
}
)
Spacer(modifier = Modifier.weight(1f))
if(searchBar)
InputFieldState(
value = search,
label = "Search ".plus(book),
modifier = Modifier
.width(if (DisplayManager.size == Size.Large || DisplayManager.orientation == Orientation.Landscape) 400.dp else 250.dp)
.align(Alignment.CenterVertically)
) {
search = it
}
if (DisplayManager.orientation == Orientation.Portrait)
Spacer(modifier = Modifier.weight(1f))
if(histogram.isNotEmpty() && searchBar)
{
IconButton(
onClick = {
navController.navigate(RecipeAppScreen.History.name)
},
modifier = Modifier.align(Alignment.CenterVertically).padding(bottom = 8.dp)
) {
Icon(painter = painterResource(Res.drawable.history), "History")
}
}
if(searchBar)
IconButton(
onClick = { showFilter = true },
modifier = Modifier.align(Alignment.CenterVertically).padding(bottom = 8.dp)
) {
Icon(
painter = painterResource(Res.drawable.tune), "Filter",
)
}
IconButton(
onClick = onClose,
modifier = Modifier.align(Alignment.CenterVertically).padding(bottom = 8.dp)
) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ExitToApp, "Close",
)
}
}
if(searchBar)
Row {
Spacer(modifier = Modifier.weight(1f))
if (DisplayManager.orientation == Orientation.Landscape && (tags.isNotEmpty() || search.isNotEmpty() || book.isNotEmpty()))
Text(
text = "Showing ".plus(filteredItems.size)
.plus(" out of ")
.plus(recipeCount).plus(" recipes"),
)
}
val listState = rememberLazyGridState()
if(searchBar)
LazyVerticalGrid(
state = listState,
columns = GridCells.Adaptive(minSize = 125.dp)
)
{
items(tags.size) {
Button(onClick = { tags.remove(tags[it]) }) {
Text(text = tags[it])
}
}
}
}
Device.Web ->
if(menuBar)
Column {
Row {
IconButton(
onClick = { navController.popBackStack() },
content = {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Back"
)
}
)
IconButton(
onClick = {
currentRecipe = filteredItems.random()
navController.navigate(
RecipeAppScreen.Details.name.plus(
"/${currentRecipe.title}"
)
)
},
content = {
Icon(
painter = painterResource(Res.drawable.shuffle),
contentDescription = "random"
)
}
)
IconButton(
onClick = {onThemeToggle.invoke()},
content = {
Icon(
painter = painterResource(
when(theme) {
Theme.Auto -> Res.drawable.auto_mode
Theme.Light -> Res.drawable.light_mode
Theme.Dark -> Res.drawable.dark_mode
}
),
contentDescription = "theme"
)
}
)
}
}
}
SharedTransitionLayout {
NavHost(
navController = navController,
startDestination = "Home",
modifier = Modifier.fillMaxSize()
) {
composable(route = "Home")
{
menuBar = false
HomeScreen(
onClose = onClose,
onGo = {
if (DisplayManager.device == Device.Web)
navController.navigate(RecipeAppScreen.List.name)
else
navController.navigate("BookShelf")
},
onSurprise = {
currentRecipe = filteredItems.random()
navController.navigate(RecipeAppScreen.Details.name.plus("/${currentRecipe.title}"))
},
)
}
composable(route = "BookShelf")
{
val shelfTags = mutableMapOf<String, TagType>()
for (recipe in getRecipeList()) {
for (tag in recipe.tags) {
if (!tags.contains(tag.key))
shelfTags[tag.key] = tag.value
}
}
menuBar = false
history = false
BookShelf(
tags = shelfTags,
onClick = { lockedTag ->
tags.clear()
search = ""
book = lockedTag
if (book == "SURPRISE") {
currentRecipe = filteredItems.random()
navController.navigate(RecipeAppScreen.Details.name.plus("/${currentRecipe.title}"))
} else {
navController.navigate(RecipeAppScreen.List.name)
}
}
)
}
composable(route = RecipeAppScreen.List.name) {
menuBar = true
searchBar = true
history = false
RecipesListScreen(
items = filteredItems,
animate = animate,
onClick = { recipe ->
currentRecipe = recipe
navController.navigate(RecipeAppScreen.Details.name.plus("/${recipe.title}"))
})
}
composable(route = RecipeAppScreen.History.name) {
menuBar = true
searchBar = false
history = true
RecipesListScreen(
items = histogram,
animate = animate,
onClick = { recipe ->
currentRecipe = recipe
navController.navigate(RecipeAppScreen.DetailHistory.name.plus("/${recipe.title}"))
})
}
composable(
route = RecipeAppScreen.Details.name.plus("/{recipe}"),
arguments = listOf(navArgument("recipe") {
type = NavType.StringType
})
) { args ->
menuBar = false
history = false
getRecipeList().find { it.title == args.arguments?.getString("recipe") }
?.let { it1 ->
if(histogram.contains(it1).not())
histogram.add(it1)
RecipeDetails(
navController = navController,
recipe = it1,
animate = animate,
discreet = discreet,
multipleRecipes = filteredItems.size > 1,
navTo = RecipeAppScreen.List.name,
goForward = {
val index = filteredItems.indexOf(it1)
val recipe =
if (index < filteredItems.size - 1) filteredItems[index + 1] else filteredItems.first()
navController.navigate(
RecipeAppScreen.Details.name.plus(
"/${recipe.title}"
)
)
})
}
}
composable(
route = RecipeAppScreen.DetailHistory.name.plus("/{recipe}"),
arguments = listOf(navArgument("recipe") {
type = NavType.StringType
})
) { args ->
menuBar = false
history = true
getRecipeList().find { it.title == args.arguments?.getString("recipe") }
?.let { it1 ->
RecipeDetails(
navController = navController,
recipe = it1,
animate = animate,
discreet = discreet,
multipleRecipes = histogram.size > 1,
navTo = RecipeAppScreen.History.name,
goForward = {
val index = histogram.indexOf(it1)
val recipe =
if (index < histogram.size - 1) histogram[index + 1] else histogram.first()
navController.navigate(
RecipeAppScreen.DetailHistory.name.plus(
"/${recipe.title}"
)
)
})
}
}
composable(
route = "Settings"
) {
menuBar = false
history = false
SettingsMenu(
theme = theme,
onTheme = { onThemeToggle.invoke() },
animate = animate,
onAnim = {
animate = !animate
settings.putBoolean(ANIM_KEY, animate)
},
helpUs = helpUs,
onHelpToggle = { helpUs = !helpUs },
ttt = tttBy,
onTTTToggle = { onTTTToggle.invoke() },
andOr = returnAnyMatch,
onAndOr = {
returnAnyMatch = !returnAnyMatch
settings.putBoolean(AON_KEY, returnAnyMatch)
},
sortBy = sortBy,
onSortToggle = { onSortToggle.invoke() },
descending = descending,
onDesc = {
descending = !descending
settings.putBoolean(DESC_KEY, descending)
},
onRandom = {},
onBack = { navController.popBackStack() },
onClear = {settings.clear()},
discreet = discreet,
onDiscreet = {
discreet = !discreet
settings.putBoolean(DISCREET_KEY, discreet)
}
)
}
}
}
}
}
}
}

View file

@ -1,25 +0,0 @@
package com.menagerie.bakers
enum class Size {
Small,
Medium,
Large,
}
enum class Orientation {
Portrait,
Landscape,
}
enum class Device {
Android,
Desktop,
Web,
}
object DisplayManager {
var size = Size.Small
var orientation = Orientation.Portrait
var device = Device.Android
}

View file

@ -1,11 +0,0 @@
package com.menagerie.bakers
object SettingsKeys {
const val THEME_KEY = "theme"
const val SORT_BY_KEY = "sortBy"
const val TTT_KEY = "ttt"
const val AON_KEY = "aon"
const val DESC_KEY = "desc"
const val ANIM_KEY = "animate"
const val DISCREET_KEY = "discreet"
}

View file

@ -1,3 +0,0 @@
package com.menagerie.bakers
expect val isDebug: Boolean

View file

@ -1,40 +0,0 @@
package com.menagerie.bakers.model
import androidx.compose.ui.graphics.Color
import org.jetbrains.compose.resources.DrawableResource
import bakersmenagerie.composeapp.generated.resources.Res
import bakersmenagerie.composeapp.generated.resources._10_strawberries
import kotlin.time.Duration
enum class TagType {
COURSE, // Breads, Apps, Desserts, Entrees, etc
CUISINE, // Country or Ethnicity of Origin
FLAVOUR, // Spicy, Sweet, Sour, Umami, Delicious
TECHNIQUE, // Fried, Baked, Boiled, Poached, etc
PROTEIN,
}
//For internal use and display purposes only (see get Recipe)
enum class TTT {
TRIED, //We've picked up a new recipe!
TESTED, //We've made this recipe several times
TRUE, //We've made this recipe a dozen times
}
data class Recipe(
val title: String = "PLACEHOLDER",
val description: String = "PLEASE DON'T FORGET ABOUT ME",
val prepTime: Duration = Duration.INFINITE,
val cookTime: Duration = Duration.INFINITE,
val servings: String = "Enough",
val ingredients: List<String> = listOf(),
val instructions: List<String> = listOf(),
val tags: Map<String, TagType> = mapOf(),
val image: DrawableResource = Res.drawable._10_strawberries,
val linkedRecipes : List<Recipe> = listOf(),
var bgColor: Color = Color.White,
var favourite: Boolean = false,
var iMadeThis: Boolean = false,
var ttt: TTT = TTT.TRIED
)

View file

@ -1,35 +0,0 @@
package com.menagerie.bakers.model.americas
import com.menagerie.bakers.model.Recipe
import com.menagerie.bakers.model.TagType
import bakersmenagerie.composeapp.generated.resources.Res
import bakersmenagerie.composeapp.generated.resources.burnt_miso
import com.menagerie.bakers.model.TTT
import kotlin.time.Duration
val burntMiso = Recipe(
title = "Burnt Miso",
description = "Bitter and Sweet, Burnt Miso adds a complex layer to Umami dishes.",
prepTime = Duration.parse("5m"),
cookTime = Duration.parse("30m"),
servings = "Literally Enough Though",
ingredients = listOf(
"Miso Paste"
),
instructions = listOf(
"Preheat oven to 400F",
"Spread as much Miso as you want to burn in a 1/2 inch thick layer on parchment paper on a baking sheet.",
"Bake for 30 Minutes or until charred.",
"Blend when using."
),
tags = mapOf(
"American" to TagType.CUISINE,
"Ingredient" to TagType.COURSE,
"Bitter" to TagType.FLAVOUR,
"Sweet" to TagType.FLAVOUR,
"Baked" to TagType.TECHNIQUE,
"Vegan" to TagType.PROTEIN,
),
image = Res.drawable.burnt_miso,
ttt = TTT.TESTED
)

View file

@ -1,47 +0,0 @@
package com.menagerie.bakers.model.americas
import com.menagerie.bakers.model.Recipe
import com.menagerie.bakers.model.TagType
import bakersmenagerie.composeapp.generated.resources.Res
import bakersmenagerie.composeapp.generated.resources.jerk
import com.menagerie.bakers.model.TTT
import kotlin.time.Duration
val jamaicanList = listOf(
Recipe(
title = "Jamaican Jerk Sauce",
description = "A spicy and flavorful Jamaican Jerk Sauce made with Scotch Bonnet Peppers, Red Onion, Garlic, and a blend of warm spices.",
prepTime = Duration.parse("15m"),
cookTime = Duration.ZERO, // No cooking involved
servings = "Makes about 2 cups",
ingredients = listOf(
"6 Scotch Bonnet Peppers, Chopped",
"1 Small Red Onions, Chopped",
"6 Garlic Cloves, Chopped",
"4 Stalks Scallions, ends trimmed",
"0.25 Cups Soy Sauce",
"0.25 Cups Apple Cider Vinegar",
"2 Tbsp Olive Oil",
"0.25 Cups Lime Juice",
"1 Tbsp Ginger, Grated",
"2 Tbsp Brown Sugar",
"1 Tsp Nutmeg",
"1 Tsp Allspice",
"1 Tsp Cinnamon",
"1 Tsp Dried Thyme",
"Salt to taste"
),
instructions = listOf(
"Combine all ingredients in a food processor, Blend. Adjust Soy [0.25 Cup], Vinegar [0.25 Cup], and Oil [2 Tbsp] ratios accordingly for consistency."
),
tags = mapOf(
"Jamaican" to TagType.CUISINE,
"Condiment" to TagType.COURSE,
"Spicy" to TagType.FLAVOUR,
"Savory" to TagType.FLAVOUR,
"Vegan" to TagType.PROTEIN,
),
image = Res.drawable.jerk,
ttt = TTT.TRUE,
)
)

View file

@ -1,73 +0,0 @@
package com.menagerie.bakers.model.asia
import bakersmenagerie.composeapp.generated.resources.Res
import bakersmenagerie.composeapp.generated.resources.gai_yang
import com.menagerie.bakers.model.Recipe
import com.menagerie.bakers.model.TTT
import com.menagerie.bakers.model.TagType
import kotlin.time.Duration
val laoList = listOf(
Recipe(
title = "Gai Yang",
description = "Spicy Grilled Lao Chicken.",
prepTime = Duration.parse("2h 20m"),
cookTime = Duration.parse("40m"),
servings = "Serves 4",
ingredients = listOf(
"2 Chicken Breast",
"2 Chicken Legs (thigh & drumstick combo)",
"HEADER-Marinade",
"1.5 Tsp White Peppercorns",
"2 Tsp Coriander Seeds, Toasted",
"1 Stalk Lemongrass, bottom half only, thinly sliced",
"5 Cloves Garlic",
"3 Tbsp Soy Sauce",
"3 Tbsp Fish Sauce",
"1 Tbsp Black Soy Sauce",
"1 Tbsp Sugar",
"0.5 Cups Water",
"2 Tbsp Vegetable Oil",
"HEADER-Dipping Sauce - Nam Jim Jeaw",
"2 Tbsp Thai Tamarind Paste",
"1 Tbsp Palm Sugar, Finely Chopped",
"1 Tbsp Fish Sauce",
"1 Tbsp Lime Juice",
"Chili Flakes, to taste",
"1 Tbsp Shallots, Finely Chopped",
"1 Tbsp Green Onion, Chopped",
"3 sprigs Cilantro, Chopped",
"1 Tbsp Toasted Rice Powder"
),
listOf(
"HEADER-Marinade",
"Grind White Peppercorns [1.5 Tsp] and Coriander Seeds [2 Tsp] into a powder. Add Lemongrass [1 stalk] and Garlic [5 cloves] and pound into a paste.",
"Transfer to a bowl. Add Soy Sauce [3 Tbsp], Fish Sauce [3 Tbsp], Black Soy Sauce [1 Tbsp], Sugar [1 Tbsp], Water [0.5 Cup], and Vegetable Oil [2 Tbsp]. Stir to dissolve Sugar.",
"Split Chicken Breast [1 whole] into halves, separate Chicken Legs [2] into thighs and drumsticks, and trim excess thigh skin.",
"Place Chicken in a bag or dish, pour Marinade over, and ensure all pieces are coated. Marinate for 2 hours or overnight, flipping halfway.",
"HEADER-Dipping Sauce - Nam Jim Jeaw",
"Toast raw rice in a dry skillet until dark brown. Cool, then grind into Toasted Rice Powder [1 Tbsp].",
"Combine Thai Tamarind Paste [2 Tbsp], Fish Sauce [1 Tbsp], Lime Juice [1 Tbsp], and Palm Sugar [1 Tbsp]. Stir until Sugar dissolves.",
"Add Chili Flakes and Shallots [1 Tbsp]. Just before serving, stir in Green Onions [1 Tbsp], Cilantro [3 sprigs], and Toasted Rice Powder [1 Tbsp].",
"HEADER-Grilling",
"Bring Chicken to room temperature 45 minutes before cooking. Preheat grill to medium.",
"Place Chicken skin-side up on grill (use cooler spots to prevent burning).",
"Reduce heat to low-medium. Cook with lid closed, flipping every 5 minutes, 18-30 minutes total.",
),
tags = mapOf(
"Laotian" to TagType.CUISINE,
"Grilled" to TagType.TECHNIQUE,
"Savory" to TagType.FLAVOUR,
"Spicy" to TagType.FLAVOUR,
"Tangy" to TagType.FLAVOUR,
"Carnivorous" to TagType.PROTEIN,
"Entree" to TagType.COURSE
),
image = Res.drawable.gai_yang,
ttt = TTT.TRIED,
)
)

View file

@ -1,40 +0,0 @@
package com.menagerie.bakers.model.asia
import com.menagerie.bakers.model.Recipe
import com.menagerie.bakers.model.TagType
import bakersmenagerie.composeapp.generated.resources.Res
import bakersmenagerie.composeapp.generated.resources._10_strawberries
import com.menagerie.bakers.model.TTT
import kotlin.time.Duration
val sinamak = Recipe(
title = "Sinamak",
description = "A spicy Filipino condiment made with Cane Vinegar, Siling Labuyo [Red Chili], Ginger, and other spices. Best served with grilled or fried dishes.",
prepTime = Duration.parse("10m"),
cookTime = Duration.ZERO, // No cooking involved
servings = "Makes 1 bottle",
ingredients = listOf(
"3.5 Cups Cane Vinegar (best if from Tuba or Palm Wine)",
"1 Cup Fresh Siling Labuyo [Red Chili]",
"3 Tbsp Ginger, sliced",
"3 Tbsp Langkawas, sliced [substitute extra Ginger and Lime Juice if not available]",
"1 Tbsp Peppercorns",
"1 Head Garlic"
),
instructions = listOf(
"Clean and slice the Ginger [3 Tbsp] and Langkawas [3 Tbsp].",
"Combine Langkawas [3 Tbsp], Ginger [3 Tbsp], Garlic [1 Head], whole Peppercorns [1 Tbsp], and Siling Labuyo Chili [1 Cup] in a long neck bottle (any clean liquor bottle will do).",
"Pour the Vinegar [3.5 Cups] in the bottle then tightly cover it.",
"Lightly Shake the bottle and store in room temperature for 2 days to allow the spices to blend with the vinegar, the longer you store it, the spicier it becomes.",
"Serve as condiment with Soy Sauce or Fish Sauce for your grilled and fried dish."
),
tags = mapOf(
"Filipino" to TagType.CUISINE,
"Condiment" to TagType.COURSE,
"Spicy" to TagType.FLAVOUR,
"Tangy" to TagType.FLAVOUR,
"Vegan" to TagType.PROTEIN,
),
image = Res.drawable._10_strawberries,
ttt = TTT.TRIED
)

View file

@ -1,49 +0,0 @@
package com.menagerie.bakers.model.europe
import com.menagerie.bakers.model.Recipe
import com.menagerie.bakers.model.TagType
import bakersmenagerie.composeapp.generated.resources.Res
import bakersmenagerie.composeapp.generated.resources.shkmeruli
import com.menagerie.bakers.model.TTT
import kotlin.time.Duration
val georgianList = listOf(
Recipe(
title = "Shkmeruli",
description = "Georgian chicken dish cooked in a creamy garlic sauce, known for its rich and flavorful broth.",
prepTime = Duration.parse("15m"),
cookTime = Duration.parse("45m"),
servings = "Serves 5",
ingredients = listOf(
"2 lbs Chicken Breast, Legs, and Thighs, skin-on",
"1 Tbsp Salt",
"3 Tbsp Ghee",
"0.25 Cups All-Purpose Flour",
"0.25 Cups Rice Flour",
"5 Tbsp Large Garlic Cloves (minced)",
"1.5 Cups Whole Milk",
"1 Tsp Khmeli Suneli Spice Blend",
"Freshly Ground Black Pepper (to taste)"
),
instructions = listOf(
"Pat dry the Chicken pieces and generously season with Salt [1 Tbsp].",
"Melt Ghee [2 Tbsp] in a heavy-bottomed pan or deep skillet. Heat the Ghee until shimmering.",
"Mix the Rice Flour [0.25 Cups] and All-Purpose Flour [0.25 Cups] in a shallow bowl. Dredge the Chicken pieces in Flour and transfer to the hot skillet. Fry on both sides until the skin is golden brown and crispy, about 5 minutes per side. When the first batch is done, remove from the pan and continue with the rest of the Chicken.",
"When all the pieces are fried, wipe the pan clean and melt the remaining Ghee [1 Tbsp]. Add Garlic [5 Tbsp] and cook, stirring, for 20 seconds.",
"Pour in the Whole Milk [1.5 cups] and bring to a simmer. Mix in the Khmeli Suneli Spice Blend [1 Tsp]. Lower the heat to medium-low and add the fried Chicken pieces.",
"Simmer for 25 minutes until the Chicken is tender and cooked through. If needed, season with more Salt and Freshly Ground Black Pepper."
),
tags = mapOf(
"Georgian" to TagType.CUISINE,
"Entree" to TagType.COURSE,
"Simmered" to TagType.TECHNIQUE,
"Pan Fried" to TagType.TECHNIQUE,
"Garlic" to TagType.FLAVOUR,
"Savory" to TagType.FLAVOUR,
"Creamy" to TagType.FLAVOUR,
"Carnivorous" to TagType.PROTEIN,
),
image = Res.drawable.shkmeruli,
ttt = TTT.TRIED,
)
)

View file

@ -1,5 +0,0 @@
package com.menagerie.bakers.util
interface ClipboardController {
fun copyToClipboard(text: String)
}

View file

@ -1,6 +0,0 @@
package com.menagerie.bakers.util
import androidx.compose.runtime.Composable
@Composable
expect fun rememberClipboardController(): ClipboardController

View file

@ -1,29 +0,0 @@
package com.menagerie.bakers.view
import androidx.compose.runtime.Composable
import com.menagerie.bakers.SortBy
import com.menagerie.bakers.Theme
import com.menagerie.bakers.model.TTT
@Composable
expect fun SettingsMenu(
theme: Theme,
onTheme: () -> Unit,
animate: Boolean,
onAnim: () -> Unit,
helpUs: Boolean,
onHelpToggle: (Boolean) -> Unit,
ttt: TTT,
onTTTToggle: () -> Unit,
andOr: Boolean,
onAndOr: () -> Unit,
sortBy: SortBy,
onSortToggle: () -> Unit,
descending: Boolean,
onDesc: () -> Unit,
onRandom: () -> Unit,
onBack: () -> Unit,
onClear: () -> Unit,
discreet: Boolean,
onDiscreet: () -> Unit,
)

Some files were not shown because too many files have changed in this diff Show more