Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1cc05af323 | ||
|
|
6eb048575c | ||
|
|
5cdc6135f5 | ||
|
|
f6be34ae48 | ||
|
|
4f06d1fc8f |
@@ -23,6 +23,11 @@ web-typecheck:
|
||||
pnpm -C web install --frozen-lockfile
|
||||
pnpm -C web typecheck
|
||||
|
||||
# Build the Android debug APK and copy it next to the android/ project root.
|
||||
apk:
|
||||
cd android && ./gradlew assembleDebug
|
||||
cp android/app/build/outputs/apk/debug/app-debug.apk android/app-debug.apk
|
||||
|
||||
# Full workspace build + tests.
|
||||
test:
|
||||
cargo fmt --all -- --check
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
*.iml
|
||||
.gradle/
|
||||
.kotlin/
|
||||
/local.properties
|
||||
/.idea/
|
||||
.DS_Store
|
||||
|
||||
@@ -2,12 +2,12 @@ package pt.naps.maestro.ui.sidebar
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.InfiniteTransition
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.LinearOutSlowInEasing
|
||||
import androidx.compose.animation.core.RepeatMode
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.infiniteRepeatable
|
||||
import androidx.compose.animation.core.keyframes
|
||||
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
@@ -52,18 +52,51 @@ fun activityStateWord(activity: ActivityState): String =
|
||||
ActivityState.Error -> "Error"
|
||||
}
|
||||
|
||||
/** A held bar that pulses its alpha between 1 and [minAlpha] every
|
||||
* [periodMs] — the blink/flash motion `AwaitingInput`/`Error` earn. */
|
||||
/** A hard on/off blink, one cycle per 1200ms — mirrors the web rail's
|
||||
* `maestro-rail-blink` keyframes (docs/SIDEBAR.md, web/src/components/sidebar/sidebar.css):
|
||||
* held at full alpha for the first half of the cycle, then snapped to 0 for
|
||||
* the second half, with no crossfade. */
|
||||
@Composable
|
||||
private fun InfiniteTransition.pulsingAlpha(minAlpha: Float, periodMs: Int): Float {
|
||||
private fun InfiniteTransition.blinkAlpha(): Float {
|
||||
val alpha by animateFloat(
|
||||
initialValue = 1f,
|
||||
targetValue = minAlpha,
|
||||
targetValue = 0f,
|
||||
animationSpec = infiniteRepeatable(
|
||||
animation = tween(durationMillis = periodMs, easing = LinearEasing),
|
||||
repeatMode = RepeatMode.Reverse,
|
||||
animation = keyframes {
|
||||
durationMillis = 1200
|
||||
1f at 0
|
||||
1f at 599
|
||||
0f at 600
|
||||
0f at 1199
|
||||
},
|
||||
repeatMode = RepeatMode.Restart,
|
||||
),
|
||||
label = "activity-rail-alpha",
|
||||
label = "activity-rail-blink",
|
||||
)
|
||||
return alpha
|
||||
}
|
||||
|
||||
/** A fast double-flash every 1400ms — mirrors the web rail's
|
||||
* `maestro-rail-flash` keyframes: two dips to quarter alpha early in the
|
||||
* cycle, then held at full alpha for the remainder. */
|
||||
@Composable
|
||||
private fun InfiniteTransition.flashAlpha(): Float {
|
||||
val alpha by animateFloat(
|
||||
initialValue = 1f,
|
||||
targetValue = 1f,
|
||||
animationSpec = infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = 1400
|
||||
1f at 0 using LinearOutSlowInEasing
|
||||
0.25f at 210 using LinearOutSlowInEasing
|
||||
1f at 420 using LinearOutSlowInEasing
|
||||
0.25f at 630 using LinearOutSlowInEasing
|
||||
1f at 840 using LinearOutSlowInEasing
|
||||
1f at 1400
|
||||
},
|
||||
repeatMode = RepeatMode.Restart,
|
||||
),
|
||||
label = "activity-rail-flash",
|
||||
)
|
||||
return alpha
|
||||
}
|
||||
@@ -77,8 +110,8 @@ fun ActivityRail(activity: ActivityState, modifier: Modifier = Modifier) {
|
||||
val color by animateColorAsState(targetValue = activityColor(activity), label = "activity-rail-color")
|
||||
val infinite = rememberInfiniteTransition(label = "activity-rail-motion")
|
||||
val alpha = when (activity) {
|
||||
ActivityState.AwaitingInput -> infinite.pulsingAlpha(minAlpha = 0.35f, periodMs = 600)
|
||||
ActivityState.Error -> infinite.pulsingAlpha(minAlpha = 0.25f, periodMs = 220)
|
||||
ActivityState.AwaitingInput -> infinite.blinkAlpha()
|
||||
ActivityState.Error -> infinite.flashAlpha()
|
||||
else -> 1f
|
||||
}
|
||||
Box(
|
||||
|
||||
@@ -2,10 +2,45 @@ package pt.naps.maestro.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
/** Palette translated from the web SPA's OKLCH design tokens
|
||||
* (web/src/styles.css) into sRGB. Dark is the default theme; Light is opt-in.
|
||||
* Semantic mapping (see docs referenced from issue #271):
|
||||
* bg -> background, surface* -> surface layers, ink -> onBackground/onSurface,
|
||||
* muted -> onSurfaceVariant, faint -> outline, accent -> primary,
|
||||
* live -> tertiary, danger -> error, warn -> secondary. */
|
||||
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
val Pink40 = Color(0xFF7D5260)
|
||||
// ---- Dark theme (default) ----
|
||||
val DarkBg = Color(0xFF0E1012)
|
||||
val DarkSurface = Color(0xFF16181B)
|
||||
val DarkSurface2 = Color(0xFF1E2125)
|
||||
val DarkSurface3 = Color(0xFF272B2F)
|
||||
val DarkBorder = Color(0xFF2E3136)
|
||||
val DarkBorderStrong = Color(0xFF42474D)
|
||||
val DarkInk = Color(0xFFEBEDEF)
|
||||
val DarkMuted = Color(0xFFA1A5AA)
|
||||
val DarkFaint = Color(0xFF777B7F)
|
||||
val DarkAccent = Color(0xFFE2E5E8)
|
||||
val DarkAccentStrong = Color(0xFFF4F5F7)
|
||||
val DarkAccentInk = Color(0xFF13161C)
|
||||
val DarkLive = Color(0xFF65EA92)
|
||||
val DarkDanger = Color(0xFFFF6F69)
|
||||
val DarkWarn = Color(0xFFEEBC4A)
|
||||
val DarkTermBg = Color(0xFF0B0C0F)
|
||||
|
||||
// ---- Light theme ----
|
||||
val LightBg = Color(0xFFF9FAFB)
|
||||
val LightSurface = Color(0xFFFFFFFF)
|
||||
val LightSurface2 = Color(0xFFF2F3F5)
|
||||
val LightSurface3 = Color(0xFFE6E8EA)
|
||||
val LightBorder = Color(0xFFD9DBDF)
|
||||
val LightBorderStrong = Color(0xFFC1C4C9)
|
||||
val LightInk = Color(0xFF20242A)
|
||||
val LightMuted = Color(0xFF565B62)
|
||||
val LightFaint = Color(0xFF81878D)
|
||||
val LightAccent = Color(0xFF23272B)
|
||||
val LightAccentStrong = Color(0xFF13161B)
|
||||
val LightAccentInk = Color(0xFFF9FAFB)
|
||||
val LightLive = Color(0xFF069A4A)
|
||||
val LightDanger = Color(0xFFD01D21)
|
||||
val LightWarn = Color(0xFFC78200)
|
||||
val LightTermBg = Color(0xFF0B0F14)
|
||||
|
||||
@@ -10,18 +10,71 @@ import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
/** Dark scheme (default), translated from the web SPA's dark token set
|
||||
* (web/src/styles.css) — see Color.kt for the token -> role mapping. */
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = Purple80,
|
||||
secondary = PurpleGrey80,
|
||||
tertiary = Pink80,
|
||||
background = DarkBg,
|
||||
onBackground = DarkInk,
|
||||
surface = DarkSurface,
|
||||
onSurface = DarkInk,
|
||||
surfaceVariant = DarkSurface2,
|
||||
onSurfaceVariant = DarkMuted,
|
||||
outline = DarkFaint,
|
||||
outlineVariant = DarkBorder,
|
||||
surfaceTint = DarkAccent,
|
||||
primary = DarkAccent,
|
||||
onPrimary = DarkAccentInk,
|
||||
primaryContainer = DarkSurface3,
|
||||
onPrimaryContainer = DarkInk,
|
||||
secondary = DarkWarn,
|
||||
onSecondary = DarkAccentInk,
|
||||
secondaryContainer = DarkSurface3,
|
||||
onSecondaryContainer = DarkInk,
|
||||
tertiary = DarkLive,
|
||||
onTertiary = DarkAccentInk,
|
||||
tertiaryContainer = DarkSurface3,
|
||||
onTertiaryContainer = DarkInk,
|
||||
error = DarkDanger,
|
||||
onError = DarkAccentInk,
|
||||
errorContainer = DarkSurface3,
|
||||
onErrorContainer = DarkDanger,
|
||||
)
|
||||
|
||||
/** Light scheme, opt-in. */
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = Purple40,
|
||||
secondary = PurpleGrey40,
|
||||
tertiary = Pink40,
|
||||
background = LightBg,
|
||||
onBackground = LightInk,
|
||||
surface = LightSurface,
|
||||
onSurface = LightInk,
|
||||
surfaceVariant = LightSurface2,
|
||||
onSurfaceVariant = LightMuted,
|
||||
outline = LightFaint,
|
||||
outlineVariant = LightBorder,
|
||||
surfaceTint = LightAccent,
|
||||
primary = LightAccent,
|
||||
onPrimary = LightAccentInk,
|
||||
primaryContainer = LightSurface3,
|
||||
onPrimaryContainer = LightInk,
|
||||
secondary = LightWarn,
|
||||
onSecondary = LightAccentInk,
|
||||
secondaryContainer = LightSurface3,
|
||||
onSecondaryContainer = LightInk,
|
||||
tertiary = LightLive,
|
||||
onTertiary = LightAccentInk,
|
||||
tertiaryContainer = LightSurface3,
|
||||
onTertiaryContainer = LightInk,
|
||||
error = LightDanger,
|
||||
onError = LightAccentInk,
|
||||
errorContainer = LightSurface3,
|
||||
onErrorContainer = LightDanger,
|
||||
)
|
||||
|
||||
/** Dark is the product default (docs/DESIGN.md); light is opt-in, and dynamic
|
||||
* color (Material You, API 31+) is opt-in on top of that. Edge-to-edge itself
|
||||
* is enabled once in MainActivity#onCreate — [enableEdgeToEdge] draws content
|
||||
* behind transparent system bars for whichever scheme resolves here, and the
|
||||
* default `SystemBarStyle.auto` picks light/dark bar icons from this theme's
|
||||
* [darkTheme] flag, not the system setting. */
|
||||
@Composable
|
||||
fun MaestroTheme(
|
||||
darkTheme: Boolean = true,
|
||||
|
||||
@@ -1,17 +1,99 @@
|
||||
package pt.naps.maestro.ui.theme
|
||||
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.Font
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.sp
|
||||
import pt.naps.maestro.R
|
||||
|
||||
/** Bundled (SIL OFL, android/licenses/JetBrainsMono-OFL.txt) rather than the
|
||||
* system mono fallback, so the terminal pane and code blocks render the same
|
||||
* face as the web SPA (--font-mono) on every device. */
|
||||
val JetBrainsMono = FontFamily(
|
||||
Font(R.font.jetbrains_mono_regular, FontWeight.Normal),
|
||||
Font(R.font.jetbrains_mono_medium, FontWeight.Medium),
|
||||
Font(R.font.jetbrains_mono_bold, FontWeight.Bold),
|
||||
)
|
||||
|
||||
/** UI chrome uses the platform sans (--font-sans is a system-ui stack on
|
||||
* web); only terminal/code content switches to [JetBrainsMono]. */
|
||||
private val Sans = FontFamily.Default
|
||||
|
||||
/** Scale roughly mirrors the web token steps (web/src/styles.css --fs-11
|
||||
* through --fs-20); role names are Material3's, not the CSS ones. */
|
||||
val Typography = Typography(
|
||||
bodyLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp,
|
||||
titleLarge = TextStyle(
|
||||
fontFamily = Sans,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
fontSize = 20.sp,
|
||||
lineHeight = 26.sp,
|
||||
letterSpacing = 0.sp,
|
||||
),
|
||||
titleMedium = TextStyle(
|
||||
fontFamily = Sans,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 17.sp,
|
||||
lineHeight = 24.sp,
|
||||
letterSpacing = 0.5.sp,
|
||||
letterSpacing = 0.sp,
|
||||
),
|
||||
titleSmall = TextStyle(
|
||||
fontFamily = Sans,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 15.sp,
|
||||
lineHeight = 20.sp,
|
||||
letterSpacing = 0.1.sp,
|
||||
),
|
||||
bodyLarge = TextStyle(
|
||||
fontFamily = Sans,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 15.sp,
|
||||
lineHeight = 22.sp,
|
||||
letterSpacing = 0.1.sp,
|
||||
),
|
||||
bodyMedium = TextStyle(
|
||||
fontFamily = Sans,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 14.sp,
|
||||
lineHeight = 20.sp,
|
||||
letterSpacing = 0.1.sp,
|
||||
),
|
||||
bodySmall = TextStyle(
|
||||
fontFamily = Sans,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 13.sp,
|
||||
lineHeight = 18.sp,
|
||||
letterSpacing = 0.1.sp,
|
||||
),
|
||||
labelLarge = TextStyle(
|
||||
fontFamily = Sans,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 13.sp,
|
||||
lineHeight = 18.sp,
|
||||
letterSpacing = 0.1.sp,
|
||||
),
|
||||
labelMedium = TextStyle(
|
||||
fontFamily = Sans,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 12.sp,
|
||||
lineHeight = 16.sp,
|
||||
letterSpacing = 0.2.sp,
|
||||
),
|
||||
labelSmall = TextStyle(
|
||||
fontFamily = Sans,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 11.sp,
|
||||
lineHeight = 14.sp,
|
||||
letterSpacing = 0.2.sp,
|
||||
),
|
||||
)
|
||||
|
||||
/** For terminal output and code blocks — callers set this explicitly instead
|
||||
* of a [Typography] role, since mono content isn't part of the M3 type scale. */
|
||||
val MonoTextStyle = TextStyle(
|
||||
fontFamily = JetBrainsMono,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 14.sp,
|
||||
lineHeight = 20.sp,
|
||||
)
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Matches web/public/icon.svg's background so the adaptive icon reads as
|
||||
the same mark as the PWA icon (issue #272). -->
|
||||
<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="#1B1B1B"
|
||||
android:fillColor="#1A1D24"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
</vector>
|
||||
|
||||
@@ -1,10 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Foreground layer of the adaptive icon: the same concentric-circle mark
|
||||
as web/public/icon.svg, scaled from its 512 viewBox into this 108 one
|
||||
(issue #272). Stays inside the adaptive-icon safe zone (r=35.86 of 54
|
||||
half-width, ~66%). -->
|
||||
<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="#D0BCFF"
|
||||
android:pathData="M54,24L74,84L64,84L54,52L44,84L34,84Z" />
|
||||
android:fillColor="#5FE3A1"
|
||||
android:pathData="M39.23,54 A14.77,14.77 0 1,0 68.77,54 A14.77,14.77 0 1,0 39.23,54 Z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#5FE3A1"
|
||||
android:strokeAlpha="0.5"
|
||||
android:strokeWidth="2.53"
|
||||
android:pathData="M28.69,54 A25.31,25.31 0 1,0 79.31,54 A25.31,25.31 0 1,0 28.69,54 Z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#5FE3A1"
|
||||
android:strokeAlpha="0.25"
|
||||
android:strokeWidth="2.11"
|
||||
android:pathData="M18.14,54 A35.86,35.86 0 1,0 89.86,54 A35.86,35.86 0 1,0 18.14,54 Z" />
|
||||
</vector>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,9 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<resources>
|
||||
<style name="Theme.Maestro" parent="android:Theme.Material.NoActionBar">
|
||||
<item name="android:statusBarColor" tools:targetApi="21">@android:color/black</item>
|
||||
<item name="android:windowBackground">@android:color/black</item>
|
||||
<!-- No statusBarColor override: MainActivity#onCreate calls
|
||||
enableEdgeToEdge(), which makes both system bars transparent at
|
||||
runtime (issue #271). Setting one here would only show briefly
|
||||
before that call lands. -->
|
||||
<item name="android:windowBackground">#FF0E1012</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
Reference in New Issue
Block a user