Restyle the watch app in the «Лес» palette with the Flutter app icon
- BalancePalette (Shared/AppTheme.swift): the palette of BalanceFlutter's default preset. Balance card in a green gradient with mint text, income teal (↙) and expense deep orange (↗) as in the Flutter app, mint accent and a forest «Добавить операцию» button instead of indigo/purple. - Money shows no kopecks when whole, otherwise exactly two decimals («8 480,50 ₽»), like the Flutter client. - App icon: same artwork as BalanceFlutter (branding/icon.svg), every size of AppIcon.appiconset rendered by the new branding/render.sh (resvg, bundled Roboto fonts). - Repository avatar and README banner redrawn in the same style as the BalanceFlutter banner, with real simulator screenshots (docs/). Checked on the watchOS 27.2 simulator (icon in the app grid, home and accounts screens). Builds for simulator and device. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No files matched your search
@@ -29,7 +29,8 @@ BalanceWatch/
|
|||||||
├── README.md
|
├── README.md
|
||||||
├── .github/workflows/build.yml # CI: сборка для симулятора и устройства
|
├── .github/workflows/build.yml # CI: сборка для симулятора и устройства
|
||||||
├── docs/CONTRACT_V2.md # общий контракт экосистемы v2 (копия)
|
├── docs/CONTRACT_V2.md # общий контракт экосистемы v2 (копия)
|
||||||
├── branding/ # аватар репозитория, не входит в бандл
|
├── branding/ # icon.svg, avatar, banner, render.sh; не входит в бандл
|
||||||
|
├── docs/ # CONTRACT_V2.md, скриншоты для баннера
|
||||||
├── BalanceWatch.xcodeproj/ # один таргет и одна схема — BalanceWatch
|
├── BalanceWatch.xcodeproj/ # один таргет и одна схема — BalanceWatch
|
||||||
└── BalanceWatch/
|
└── BalanceWatch/
|
||||||
├── App/
|
├── App/
|
||||||
@@ -210,3 +211,11 @@ SwiftData/SwiftUI, а `FinanceCalculations` работает через прот
|
|||||||
- [ ] Секреты и токены не попали в репозиторий и логи.
|
- [ ] Секреты и токены не попали в репозиторий и логи.
|
||||||
- [ ] CI собирает проект для симулятора и для устройства.
|
- [ ] CI собирает проект для симулятора и для устройства.
|
||||||
- [ ] README/AGENTS.md обновлены, если поменялась структура или сборка.
|
- [ ] README/AGENTS.md обновлены, если поменялась структура или сборка.
|
||||||
|
|
||||||
|
## Оформление
|
||||||
|
|
||||||
|
- Цвета интерфейса — только из `BalancePalette` (`Shared/AppTheme.swift`),
|
||||||
|
палитра «Лес», общая с BalanceFlutter. Не возвращай indigo/purple.
|
||||||
|
- Иконка, аватар и баннер рендерятся из SVG: `bash branding/render.sh`
|
||||||
|
(нужен `resvg`). PNG в `AppIcon.appiconset` руками не правь.
|
||||||
|
- Аватар репозитория в Gitea обновляется из `branding/avatar.png`.
|
||||||
@@ -11,7 +11,7 @@ struct BalanceWatchApp: App {
|
|||||||
WindowGroup {
|
WindowGroup {
|
||||||
WatchRootView()
|
WatchRootView()
|
||||||
.id(ledgerScope)
|
.id(ledgerScope)
|
||||||
.tint(.indigo)
|
.tint(BalancePalette.accent)
|
||||||
.preferredColorScheme(AppTheme(rawValue: appTheme)?.colorScheme)
|
.preferredColorScheme(AppTheme(rawValue: appTheme)?.colorScheme)
|
||||||
.modelContainer(containers.container(for: ledgerScope))
|
.modelContainer(containers.container(for: ledgerScope))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ struct WatchAccountsView: View {
|
|||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
Text(title).lineLimit(1)
|
Text(title).lineLimit(1)
|
||||||
Text(Money.double(cents).formattedCurrency(code: code))
|
Text(Money.double(cents).formattedCurrency(code: code))
|
||||||
.font(.caption2).foregroundStyle(cents < 0 ? .red : .secondary)
|
.font(.caption2).foregroundStyle(cents < 0 ? BalancePalette.expense : .secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ struct WatchAnalyticsView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Section("Итоги") {
|
Section("Итоги") {
|
||||||
metric("Доходы", summary.income, .green)
|
metric("Доходы", summary.income, BalancePalette.income)
|
||||||
metric("Расходы", summary.expenses, .red)
|
metric("Расходы", summary.expenses, BalancePalette.expense)
|
||||||
LabeledContent("Результат", value: summary.balance.formattedCurrency(code: currencyCode))
|
LabeledContent("Результат", value: summary.balance.formattedCurrency(code: currencyCode))
|
||||||
LabeledContent(
|
LabeledContent(
|
||||||
"Сбережения",
|
"Сбережения",
|
||||||
@@ -100,7 +100,7 @@ struct WatchAnalyticsView: View {
|
|||||||
|
|
||||||
private func tint(for item: CategorySpending) -> Color {
|
private func tint(for item: CategorySpending) -> Color {
|
||||||
item.colorName == "auto"
|
item.colorName == "auto"
|
||||||
? FinanceCategory.find(named: item.name)?.tint ?? .indigo
|
? FinanceCategory.find(named: item.name)?.tint ?? BalancePalette.forest
|
||||||
: FinanceCategory.color(named: item.colorName)
|
: FinanceCategory.color(named: item.colorName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,7 +173,7 @@ struct WatchTransactionsView: View {
|
|||||||
|
|
||||||
private func tint(_ transaction: FinanceTransaction) -> Color {
|
private func tint(_ transaction: FinanceTransaction) -> Color {
|
||||||
transaction.categoryColorName == "auto"
|
transaction.categoryColorName == "auto"
|
||||||
? FinanceCategory.find(named: transaction.categoryName)?.tint ?? .indigo
|
? FinanceCategory.find(named: transaction.categoryName)?.tint ?? BalancePalette.forest
|
||||||
: FinanceCategory.color(named: transaction.categoryColorName)
|
: FinanceCategory.color(named: transaction.categoryColorName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,9 +33,13 @@ private struct WatchHomeView: View {
|
|||||||
ScrollView {
|
ScrollView {
|
||||||
VStack(alignment: .leading, spacing: 12) {
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
VStack(alignment: .leading, spacing: 3) {
|
VStack(alignment: .leading, spacing: 3) {
|
||||||
Text("Общий баланс")
|
HStack(spacing: 5) {
|
||||||
.font(.caption)
|
Image(systemName: "wallet.bifold")
|
||||||
.foregroundStyle(.secondary)
|
.font(.caption2.weight(.semibold))
|
||||||
|
Text("Общий баланс")
|
||||||
|
.font(.caption)
|
||||||
|
}
|
||||||
|
.foregroundStyle(BalancePalette.mint.opacity(0.85))
|
||||||
Text(total.value.formattedCurrency(code: currencyCode))
|
Text(total.value.formattedCurrency(code: currencyCode))
|
||||||
.font(.title3.bold())
|
.font(.title3.bold())
|
||||||
.minimumScaleFactor(0.7)
|
.minimumScaleFactor(0.7)
|
||||||
@@ -49,7 +53,7 @@ private struct WatchHomeView: View {
|
|||||||
.padding(12)
|
.padding(12)
|
||||||
.background(
|
.background(
|
||||||
LinearGradient(
|
LinearGradient(
|
||||||
colors: [.indigo, .purple],
|
colors: [BalancePalette.cardTop, BalancePalette.cardBottom],
|
||||||
startPoint: .topLeading,
|
startPoint: .topLeading,
|
||||||
endPoint: .bottomTrailing
|
endPoint: .bottomTrailing
|
||||||
),
|
),
|
||||||
@@ -60,13 +64,15 @@ private struct WatchHomeView: View {
|
|||||||
HStack(spacing: 8) {
|
HStack(spacing: 8) {
|
||||||
watchMetric(
|
watchMetric(
|
||||||
title: "Доходы",
|
title: "Доходы",
|
||||||
|
symbol: "arrow.down.left",
|
||||||
amount: monthSummary.income,
|
amount: monthSummary.income,
|
||||||
tint: .green
|
tint: BalancePalette.income
|
||||||
)
|
)
|
||||||
watchMetric(
|
watchMetric(
|
||||||
title: "Расходы",
|
title: "Расходы",
|
||||||
|
symbol: "arrow.up.right",
|
||||||
amount: monthSummary.expenses,
|
amount: monthSummary.expenses,
|
||||||
tint: .red
|
tint: BalancePalette.expense
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,6 +83,7 @@ private struct WatchHomeView: View {
|
|||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
}
|
}
|
||||||
.buttonStyle(.borderedProminent)
|
.buttonStyle(.borderedProminent)
|
||||||
|
.tint(BalancePalette.forest)
|
||||||
|
|
||||||
VStack(spacing: 4) {
|
VStack(spacing: 4) {
|
||||||
if account.supportsV2 {
|
if account.supportsV2 {
|
||||||
@@ -137,7 +144,7 @@ private struct WatchHomeView: View {
|
|||||||
Text((transaction.kind == .income ? "+" : "−")
|
Text((transaction.kind == .income ? "+" : "−")
|
||||||
+ transaction.amount.formattedCurrency(code: currency.currency(of: transaction)))
|
+ transaction.amount.formattedCurrency(code: currency.currency(of: transaction)))
|
||||||
.font(.caption2.weight(.semibold))
|
.font(.caption2.weight(.semibold))
|
||||||
.foregroundStyle(transaction.kind == .income ? .green : .secondary)
|
.foregroundStyle(transaction.kind == .income ? BalancePalette.income : .secondary)
|
||||||
}
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
@@ -158,25 +165,38 @@ private struct WatchHomeView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func watchMetric(title: String, amount: Double, tint: Color) -> some View {
|
private func watchMetric(title: String, symbol: String, amount: Double, tint: Color) -> some View {
|
||||||
VStack(alignment: .leading, spacing: 3) {
|
VStack(alignment: .leading, spacing: 3) {
|
||||||
Text(title)
|
Label(title, systemImage: symbol)
|
||||||
.font(.caption2)
|
.font(.caption2)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
|
.labelStyle(TintedIconLabelStyle(tint: tint))
|
||||||
Text(amount.formattedCurrency(code: currencyCode))
|
Text(amount.formattedCurrency(code: currencyCode))
|
||||||
.font(.caption.bold())
|
.font(.caption.bold())
|
||||||
|
.foregroundStyle(tint)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.minimumScaleFactor(0.65)
|
.minimumScaleFactor(0.65)
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.padding(9)
|
.padding(9)
|
||||||
.background(tint.opacity(0.12), in: RoundedRectangle(cornerRadius: 12))
|
.background(tint.opacity(0.14), in: RoundedRectangle(cornerRadius: 12))
|
||||||
}
|
}
|
||||||
|
|
||||||
private func transactionTint(_ transaction: FinanceTransaction) -> Color {
|
private func transactionTint(_ transaction: FinanceTransaction) -> Color {
|
||||||
if transaction.categoryColorName == "auto" {
|
if transaction.categoryColorName == "auto" {
|
||||||
return FinanceCategory.find(named: transaction.categoryName)?.tint ?? .indigo
|
return FinanceCategory.find(named: transaction.categoryName)?.tint ?? BalancePalette.forest
|
||||||
}
|
}
|
||||||
return FinanceCategory.color(named: transaction.categoryColorName)
|
return FinanceCategory.color(named: transaction.categoryColorName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Иконка метрики в цвете метрики, подпись — вторичным цветом.
|
||||||
|
private struct TintedIconLabelStyle: LabelStyle {
|
||||||
|
let tint: Color
|
||||||
|
func makeBody(configuration: Configuration) -> some View {
|
||||||
|
HStack(spacing: 3) {
|
||||||
|
configuration.icon.foregroundStyle(tint)
|
||||||
|
configuration.title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 5.3 KiB |
@@ -23,3 +23,20 @@ enum AppTheme: String, CaseIterable, Identifiable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Палитра «Лес» — та же, что у пресета по умолчанию во Flutter-клиенте и у
|
||||||
|
/// иконки приложения.
|
||||||
|
enum BalancePalette {
|
||||||
|
/// Акцент интерфейса (кнопки, ссылки, выбор) — мятный, читается на чёрном.
|
||||||
|
static let accent = Color(red: 0x7f / 255, green: 0xd6 / 255, blue: 0xb9 / 255)
|
||||||
|
/// Основной цвет пресета «Лес».
|
||||||
|
static let forest = Color(red: 0x35 / 255, green: 0x68 / 255, blue: 0x59 / 255)
|
||||||
|
/// Градиент карточки общего баланса (как фон иконки).
|
||||||
|
static let cardTop = Color(red: 0x3f / 255, green: 0x7d / 255, blue: 0x6b / 255)
|
||||||
|
static let cardBottom = Color(red: 0x1a / 255, green: 0x4a / 255, blue: 0x3e / 255)
|
||||||
|
/// Текст на карточке баланса.
|
||||||
|
static let mint = Color(red: 0xbd / 255, green: 0xf7 / 255, blue: 0xe3 / 255)
|
||||||
|
/// Доходы — teal, расходы — deep orange, как во Flutter.
|
||||||
|
static let income = Color(red: 0x26 / 255, green: 0xb3 / 255, blue: 0xa3 / 255)
|
||||||
|
static let expense = Color(red: 0xff / 255, green: 0x6e / 255, blue: 0x40 / 255)
|
||||||
|
}
|
||||||
@@ -10,10 +10,13 @@ enum SupportedCurrency: String, CaseIterable, Identifiable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension Double {
|
extension Double {
|
||||||
|
/// Целые суммы без копеек, остальные — ровно с двумя знаками («8 480,50 ₽»),
|
||||||
|
/// как во Flutter-клиенте.
|
||||||
func formattedCurrency(code: String) -> String {
|
func formattedCurrency(code: String) -> String {
|
||||||
formatted(
|
let whole = (self * 100).rounded().truncatingRemainder(dividingBy: 100) == 0
|
||||||
|
return formatted(
|
||||||
.currency(code: code)
|
.currency(code: code)
|
||||||
.precision(.fractionLength(0 ... 2))
|
.precision(.fractionLength(whole ? 0 : 2))
|
||||||
.presentation(.narrow)
|
.presentation(.narrow)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="branding/banner.png" alt="BalanceWatch — учёт личных финансов на Apple Watch" width="100%">
|
<img src="branding/banner.png" alt="BalanceWatch — личные финансы на Apple Watch" width="100%">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
# BalanceWatch
|
# BalanceWatch
|
||||||
@@ -13,7 +13,8 @@
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
BalanceWatch/
|
BalanceWatch/
|
||||||
├── branding/ # аватар и баннер для README (PNG + SVG-исходники)
|
├── branding/ # иконка, аватар и баннер (SVG-исходники, PNG, render.sh)
|
||||||
|
├── docs/ # скриншоты для баннера
|
||||||
├── docs/CONTRACT_V2.md # общий контракт экосистемы v2 (копия, одинаковая во всех репозиториях)
|
├── docs/CONTRACT_V2.md # общий контракт экосистемы v2 (копия, одинаковая во всех репозиториях)
|
||||||
├── BalanceWatch.xcodeproj/
|
├── BalanceWatch.xcodeproj/
|
||||||
└── BalanceWatch/
|
└── BalanceWatch/
|
||||||
@@ -75,3 +76,15 @@ Simulator и watchOS device (без подписи). Запускается на
|
|||||||
кода из монорепозитория. Протокол синхронизации и правила расчётов должны
|
кода из монорепозитория. Протокол синхронизации и правила расчётов должны
|
||||||
совпадать с остальными клиентами Balance: при изменении контракта на
|
совпадать с остальными клиентами Balance: при изменении контракта на
|
||||||
сервере переносите правки и сюда.
|
сервере переносите правки и сюда.
|
||||||
|
|
||||||
|
## Оформление
|
||||||
|
|
||||||
|
Палитра «Лес» — та же, что у пресета по умолчанию в BalanceFlutter: карточка
|
||||||
|
баланса в зелёном градиенте, доходы бирюзовые (↙), расходы оранжевые (↗),
|
||||||
|
мятный акцент (`BalancePalette` в `Shared/AppTheme.swift`). Иконка — тот же
|
||||||
|
рисунок, что у Flutter-приложения (`branding/icon.svg`).
|
||||||
|
|
||||||
|
После правки SVG или скриншотов в `docs/` выполни `bash branding/render.sh`
|
||||||
|
(нужен `resvg`): скрипт перерисует все размеры иконки в
|
||||||
|
`Assets.xcassets/AppIcon.appiconset`, `branding/avatar.png` (512×512) и
|
||||||
|
`branding/banner.png` (1280×640). Шрифты Roboto лежат в `branding/fonts`.
|
||||||
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 77 KiB |
@@ -1,60 +1,30 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512">
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512" width="512" height="512">
|
||||||
|
<!-- Repository avatar: the watch with the app icon (palette «Лес»).
|
||||||
|
Render with branding/render.sh (needs resvg). -->
|
||||||
<defs>
|
<defs>
|
||||||
<radialGradient id="bg" cx="50%" cy="40%" r="75%">
|
<radialGradient id="bg" cx="50%" cy="42%" r="75%">
|
||||||
<stop offset="0" stop-color="#2a2368"/>
|
<stop offset="0" stop-color="#25594c"/>
|
||||||
<stop offset="1" stop-color="#0d0b26"/>
|
<stop offset="1" stop-color="#0c241e"/>
|
||||||
</radialGradient>
|
</radialGradient>
|
||||||
<linearGradient id="strap" x1="0" y1="0" x2="1" y2="0">
|
<linearGradient id="strap" x1="0" y1="0" x2="1" y2="0">
|
||||||
<stop offset="0" stop-color="#2b2660"/>
|
<stop offset="0" stop-color="#173f35"/>
|
||||||
<stop offset="0.5" stop-color="#3b3580"/>
|
<stop offset="0.5" stop-color="#245a4b"/>
|
||||||
<stop offset="1" stop-color="#2b2660"/>
|
<stop offset="1" stop-color="#173f35"/>
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<linearGradient id="case" x1="0" y1="0" x2="1" y2="1">
|
<linearGradient id="case" x1="0" y1="0" x2="1" y2="1">
|
||||||
<stop offset="0" stop-color="#6d68c9"/>
|
<stop offset="0" stop-color="#5a9c87"/>
|
||||||
<stop offset="1" stop-color="#3a3488"/>
|
<stop offset="1" stop-color="#1f5245"/>
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<linearGradient id="teal" x1="0" y1="0" x2="1" y2="1">
|
<clipPath id="screen"><rect x="132" y="142" width="248" height="228" rx="58"/></clipPath>
|
||||||
<stop offset="0" stop-color="#2fc7b0"/>
|
<filter id="haze" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="40"/></filter>
|
||||||
<stop offset="1" stop-color="#3f6fd8"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="coral" x1="0" y1="0" x2="1" y2="1">
|
|
||||||
<stop offset="0" stop-color="#ff8a5c"/>
|
|
||||||
<stop offset="1" stop-color="#e2475f"/>
|
|
||||||
</linearGradient>
|
|
||||||
<radialGradient id="ball" cx="38%" cy="35%" r="70%">
|
|
||||||
<stop offset="0" stop-color="#ffb08f"/>
|
|
||||||
<stop offset="0.5" stop-color="#f2614f"/>
|
|
||||||
<stop offset="1" stop-color="#b7323f"/>
|
|
||||||
</radialGradient>
|
|
||||||
<filter id="glow" x="-30%" y="-30%" width="160%" height="160%">
|
|
||||||
<feGaussianBlur stdDeviation="10"/>
|
|
||||||
</filter>
|
|
||||||
</defs>
|
</defs>
|
||||||
|
|
||||||
<!-- background -->
|
|
||||||
<rect width="512" height="512" fill="url(#bg)"/>
|
<rect width="512" height="512" fill="url(#bg)"/>
|
||||||
|
<ellipse cx="256" cy="256" rx="170" ry="150" fill="#39b997" opacity="0.22" filter="url(#haze)"/>
|
||||||
<!-- straps -->
|
|
||||||
<path d="M186 40 h140 l-10 100 h-120 z" fill="url(#strap)"/>
|
<path d="M186 40 h140 l-10 100 h-120 z" fill="url(#strap)"/>
|
||||||
<path d="M196 372 h120 l10 100 h-140 z" fill="url(#strap)"/>
|
<path d="M196 372 h120 l10 100 h-140 z" fill="url(#strap)"/>
|
||||||
|
<rect x="394" y="196" width="22" height="50" rx="8" fill="#4f8f7b"/>
|
||||||
<!-- crown and side button -->
|
<rect x="396" y="270" width="14" height="40" rx="6" fill="#3f7d6b"/>
|
||||||
<rect x="394" y="196" width="22" height="50" rx="8" fill="#5b56b8"/>
|
<rect x="106" y="116" width="300" height="280" rx="78" fill="url(#case)"/>
|
||||||
<rect x="396" y="270" width="14" height="40" rx="6" fill="#4a4599"/>
|
<rect x="124" y="134" width="264" height="244" rx="62" fill="#04120e"/>
|
||||||
|
<image x="132" y="132" width="248" height="248" xlink:href="../BalanceWatch/Assets.xcassets/AppIcon.appiconset/BalanceIcon-1024.png" clip-path="url(#screen)"/>
|
||||||
<!-- case -->
|
|
||||||
<rect x="106" y="116" width="294" height="280" rx="78" fill="url(#case)"/>
|
|
||||||
<!-- screen -->
|
|
||||||
<rect x="124" y="134" width="258" height="244" rx="62" fill="#07061a"/>
|
|
||||||
|
|
||||||
<!-- soft glow behind the mark -->
|
|
||||||
<ellipse cx="253" cy="256" rx="80" ry="90" fill="#4c45c2" opacity="0.45" filter="url(#glow)"/>
|
|
||||||
|
|
||||||
<!-- balance mark: two loops and a ball, as in the app icon -->
|
|
||||||
<rect x="190" y="166" width="126" height="94" rx="47" fill="none" stroke="url(#teal)" stroke-width="28"/>
|
|
||||||
<rect x="190" y="252" width="126" height="94" rx="47" fill="none" stroke="url(#coral)" stroke-width="28"/>
|
|
||||||
<!-- teal loop overlaps coral on the left, like the original figure -->
|
|
||||||
<path d="M204 213 v40" stroke="url(#teal)" stroke-width="28" stroke-linecap="round"/>
|
|
||||||
<circle cx="253" cy="256" r="24" fill="url(#ball)"/>
|
|
||||||
<ellipse cx="246" cy="247" rx="8" ry="5" fill="#ffffff" opacity="0.35"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 264 KiB |
@@ -1,80 +1,78 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 560" width="1280" height="560">
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1280 640" width="1280" height="640" font-family="Roboto, sans-serif">
|
||||||
|
<!-- Source of banner.png; same layout and palette («Лес») as the BalanceFlutter
|
||||||
|
banner. Render with branding/render.sh (needs resvg). Screenshots: docs/. -->
|
||||||
<defs>
|
<defs>
|
||||||
<radialGradient id="bg" cx="30%" cy="45%" r="90%">
|
<radialGradient id="bg" cx="70%" cy="45%" r="85%">
|
||||||
<stop offset="0" stop-color="#2a2368"/>
|
<stop offset="0" stop-color="#25594c"/>
|
||||||
<stop offset="1" stop-color="#0b0a22"/>
|
<stop offset="1" stop-color="#0c241e"/>
|
||||||
</radialGradient>
|
</radialGradient>
|
||||||
<linearGradient id="strap" x1="0" y1="0" x2="1" y2="0">
|
|
||||||
<stop offset="0" stop-color="#2b2660"/>
|
|
||||||
<stop offset="0.5" stop-color="#3b3580"/>
|
|
||||||
<stop offset="1" stop-color="#2b2660"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="case" x1="0" y1="0" x2="1" y2="1">
|
<linearGradient id="case" x1="0" y1="0" x2="1" y2="1">
|
||||||
<stop offset="0" stop-color="#6d68c9"/>
|
<stop offset="0" stop-color="#5a9c87"/>
|
||||||
<stop offset="1" stop-color="#3a3488"/>
|
<stop offset="1" stop-color="#1f5245"/>
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<linearGradient id="teal" x1="0" y1="0" x2="1" y2="1">
|
<linearGradient id="strap" x1="0" y1="0" x2="1" y2="0">
|
||||||
<stop offset="0" stop-color="#2fc7b0"/>
|
<stop offset="0" stop-color="#143a31"/>
|
||||||
<stop offset="1" stop-color="#3f6fd8"/>
|
<stop offset="0.5" stop-color="#22574a"/>
|
||||||
|
<stop offset="1" stop-color="#143a31"/>
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<linearGradient id="coral" x1="0" y1="0" x2="1" y2="1">
|
<linearGradient id="accent" x1="0" y1="0" x2="1" y2="0">
|
||||||
<stop offset="0" stop-color="#ff8a5c"/>
|
<stop offset="0" stop-color="#26b3a3"/>
|
||||||
<stop offset="1" stop-color="#e2475f"/>
|
<stop offset="0.5" stop-color="#a2f2d8"/>
|
||||||
|
<stop offset="1" stop-color="#f4511e"/>
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<linearGradient id="title" x1="0" y1="0" x2="1" y2="0">
|
<filter id="glow" x="-30%" y="-30%" width="160%" height="160%">
|
||||||
<stop offset="0" stop-color="#ffffff"/>
|
<feGaussianBlur stdDeviation="40"/>
|
||||||
<stop offset="1" stop-color="#c9c5ff"/>
|
|
||||||
</linearGradient>
|
|
||||||
<radialGradient id="ball" cx="38%" cy="35%" r="70%">
|
|
||||||
<stop offset="0" stop-color="#ffb08f"/>
|
|
||||||
<stop offset="0.5" stop-color="#f2614f"/>
|
|
||||||
<stop offset="1" stop-color="#b7323f"/>
|
|
||||||
</radialGradient>
|
|
||||||
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
|
|
||||||
<feGaussianBlur stdDeviation="10"/>
|
|
||||||
</filter>
|
</filter>
|
||||||
<filter id="haze" x="-50%" y="-50%" width="200%" height="200%">
|
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
|
||||||
<feGaussianBlur stdDeviation="60"/>
|
<feDropShadow dx="0" dy="14" stdDeviation="18" flood-color="#04120e" flood-opacity="0.7"/>
|
||||||
</filter>
|
</filter>
|
||||||
|
<clipPath id="iconClip"><rect x="72" y="88" width="120" height="120" rx="28"/></clipPath>
|
||||||
|
<clipPath id="backScreen"><rect x="704" y="150" width="206" height="246" rx="44"/></clipPath>
|
||||||
|
<clipPath id="frontScreen"><rect x="884" y="228" width="250" height="298" rx="54"/></clipPath>
|
||||||
</defs>
|
</defs>
|
||||||
|
|
||||||
<rect width="1280" height="560" fill="url(#bg)"/>
|
<rect width="1280" height="640" fill="url(#bg)"/>
|
||||||
<!-- ambient colour haze -->
|
<ellipse cx="930" cy="340" rx="300" ry="210" fill="#39b997" opacity="0.25" filter="url(#glow)"/>
|
||||||
<circle cx="330" cy="200" r="150" fill="#2fc7b0" opacity="0.18" filter="url(#haze)"/>
|
|
||||||
<circle cx="360" cy="380" r="150" fill="#e2475f" opacity="0.16" filter="url(#haze)"/>
|
|
||||||
|
|
||||||
<!-- watch (same drawing as avatar.svg), centred at (340, 280) -->
|
<!-- left: identity -->
|
||||||
<g transform="translate(340 280) scale(0.95) translate(-256 -256)">
|
<image x="72" y="88" width="120" height="120" xlink:href="../BalanceWatch/Assets.xcassets/AppIcon.appiconset/BalanceIcon-1024.png" clip-path="url(#iconClip)"/>
|
||||||
<path d="M186 40 h140 l-10 100 h-120 z" fill="url(#strap)"/>
|
<text x="68" y="306" font-size="84" font-weight="700" fill="#ffffff">BalanceWatch</text>
|
||||||
<path d="M196 372 h120 l10 100 h-140 z" fill="url(#strap)"/>
|
<rect x="74" y="330" width="120" height="6" rx="3" fill="url(#accent)"/>
|
||||||
<rect x="394" y="196" width="22" height="50" rx="8" fill="#5b56b8"/>
|
<text x="72" y="384" font-size="28" font-weight="500" fill="#a2f2d8">Личные финансы на Apple Watch</text>
|
||||||
<rect x="396" y="270" width="14" height="40" rx="6" fill="#4a4599"/>
|
<text x="72" y="428" font-size="22" fill="#9cc4b6">Самостоятельное приложение для часов,</text>
|
||||||
<rect x="106" y="116" width="294" height="280" rx="78" fill="url(#case)"/>
|
<text x="72" y="458" font-size="22" fill="#9cc4b6">синхронизация с self-hosted BalanceServer</text>
|
||||||
<rect x="124" y="134" width="258" height="244" rx="62" fill="#07061a"/>
|
|
||||||
<ellipse cx="253" cy="256" rx="80" ry="90" fill="#4c45c2" opacity="0.45" filter="url(#glow)"/>
|
<g font-size="18" font-weight="500" fill="#dff7ee">
|
||||||
<rect x="190" y="166" width="126" height="94" rx="47" fill="none" stroke="url(#teal)" stroke-width="28"/>
|
<rect x="72" y="494" width="128" height="36" rx="18" fill="#173f35" stroke="#3f7d6b"/>
|
||||||
<rect x="190" y="252" width="126" height="94" rx="47" fill="none" stroke="url(#coral)" stroke-width="28"/>
|
<text x="136" y="518" text-anchor="middle">watchOS 10+</text>
|
||||||
<circle cx="253" cy="256" r="24" fill="url(#ball)"/>
|
<rect x="210" y="494" width="96" height="36" rx="18" fill="#173f35" stroke="#3f7d6b"/>
|
||||||
<ellipse cx="246" cy="247" rx="8" ry="5" fill="#ffffff" opacity="0.35"/>
|
<text x="258" y="518" text-anchor="middle">SwiftUI</text>
|
||||||
|
<rect x="316" y="494" width="112" height="36" rx="18" fill="#173f35" stroke="#3f7d6b"/>
|
||||||
|
<text x="372" y="518" text-anchor="middle">SwiftData</text>
|
||||||
|
<rect x="72" y="540" width="214" height="36" rx="18" fill="#173f35" stroke="#3f7d6b"/>
|
||||||
|
<text x="179" y="564" text-anchor="middle">Счета и валюты</text>
|
||||||
|
<rect x="296" y="540" width="186" height="36" rx="18" fill="#173f35" stroke="#3f7d6b"/>
|
||||||
|
<text x="389" y="564" text-anchor="middle">Общие бюджеты</text>
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
<!-- text -->
|
<!-- back watch: accounts screen -->
|
||||||
<g font-family="Noto Sans, DejaVu Sans, sans-serif">
|
<path d="M752 60 h110 l-8 76 h-94 z" fill="url(#strap)"/>
|
||||||
<text x="640" y="240" font-size="80" font-weight="700" fill="url(#title)" letter-spacing="-1">BalanceWatch</text>
|
<path d="M760 410 h94 l8 76 h-110 z" fill="url(#strap)"/>
|
||||||
<text x="644" y="300" font-size="32" fill="#b9b5e8">Учёт личных финансов на Apple Watch</text>
|
<g filter="url(#shadow)">
|
||||||
|
<rect x="918" y="216" width="14" height="40" rx="6" fill="#4f8f7b"/>
|
||||||
<!-- tags -->
|
<rect x="690" y="136" width="234" height="274" rx="62" fill="url(#case)"/>
|
||||||
<g font-size="22" font-weight="600" fill="#e8e6ff">
|
|
||||||
<rect x="644" y="344" width="166" height="44" rx="22" fill="#ffffff" fill-opacity="0.08" stroke="#ffffff" stroke-opacity="0.18"/>
|
|
||||||
<text x="727" y="374" text-anchor="middle">watchOS 10+</text>
|
|
||||||
<rect x="824" y="344" width="122" height="44" rx="22" fill="#ffffff" fill-opacity="0.08" stroke="#ffffff" stroke-opacity="0.18"/>
|
|
||||||
<text x="885" y="374" text-anchor="middle">SwiftUI</text>
|
|
||||||
<rect x="960" y="344" width="142" height="44" rx="22" fill="#ffffff" fill-opacity="0.08" stroke="#ffffff" stroke-opacity="0.18"/>
|
|
||||||
<text x="1031" y="374" text-anchor="middle">SwiftData</text>
|
|
||||||
</g>
|
|
||||||
<g font-size="22" font-weight="600">
|
|
||||||
<rect x="644" y="402" width="288" height="44" rx="22" fill="#2fc7b0" fill-opacity="0.16" stroke="#2fc7b0" stroke-opacity="0.5"/>
|
|
||||||
<text x="788" y="432" text-anchor="middle" fill="#8ff0df">Sync с BalanceServer</text>
|
|
||||||
</g>
|
|
||||||
</g>
|
</g>
|
||||||
|
<rect x="700" y="146" width="214" height="254" rx="50" fill="#000000"/>
|
||||||
|
<image x="704" y="150" width="206" height="246" preserveAspectRatio="xMidYMid slice" xlink:href="../docs/accounts.png" clip-path="url(#backScreen)"/>
|
||||||
|
|
||||||
|
<!-- front watch: home screen -->
|
||||||
|
<path d="M944 120 h130 l-10 92 h-110 z" fill="url(#strap)"/>
|
||||||
|
<path d="M954 542 h110 l10 92 h-130 z" fill="url(#strap)"/>
|
||||||
|
<g filter="url(#shadow)">
|
||||||
|
<rect x="1150" y="300" width="18" height="52" rx="8" fill="#4f8f7b"/>
|
||||||
|
<rect x="1152" y="370" width="12" height="40" rx="5" fill="#3f7d6b"/>
|
||||||
|
<rect x="868" y="212" width="282" height="330" rx="74" fill="url(#case)"/>
|
||||||
|
</g>
|
||||||
|
<rect x="880" y="224" width="258" height="306" rx="60" fill="#000000"/>
|
||||||
|
<image x="884" y="228" width="250" height="298" preserveAspectRatio="xMidYMid slice" xlink:href="../docs/home.png" clip-path="url(#frontScreen)"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.6 KiB |
@@ -0,0 +1,203 @@
|
|||||||
|
Copyright 2011 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
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 [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" width="1024" height="1024">
|
||||||
|
<!-- Watch app icon, same artwork as BalanceFlutter: the in-app wallet mark in the «Лес» palette. Income (teal) and
|
||||||
|
expense (deep orange) cards stick out of the wallet. -->
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="bg" x1="0" y1="0" x2="0.35" y2="1">
|
||||||
|
<stop offset="0" stop-color="#3f7d6b"/>
|
||||||
|
<stop offset="1" stop-color="#1a4a3e"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="body" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#bdf7e3"/>
|
||||||
|
<stop offset="1" stop-color="#8fe6c9"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="income" x1="0" y1="0" x2="1" y2="1">
|
||||||
|
<stop offset="0" stop-color="#26b3a3"/>
|
||||||
|
<stop offset="1" stop-color="#00897b"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="expense" x1="0" y1="0" x2="1" y2="1">
|
||||||
|
<stop offset="0" stop-color="#ff8a5c"/>
|
||||||
|
<stop offset="1" stop-color="#f4511e"/>
|
||||||
|
</linearGradient>
|
||||||
|
<filter id="shadow" x="-20%" y="-20%" width="140%" height="150%">
|
||||||
|
<feDropShadow dx="0" dy="18" stdDeviation="22" flood-color="#0b2620" flood-opacity="0.45"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="1024" height="1024" fill="url(#bg)"/>
|
||||||
|
|
||||||
|
<g filter="url(#shadow)">
|
||||||
|
<!-- cards behind the wallet; arrows match the app's income/expense icons -->
|
||||||
|
<g transform="rotate(-10 400 330)">
|
||||||
|
<rect x="236" y="176" width="320" height="300" rx="36" fill="url(#income)"/>
|
||||||
|
<path d="M454 236 L338 352 M338 262 V352 H428" fill="none" stroke="#ffffff" stroke-width="32" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</g>
|
||||||
|
<g transform="rotate(10 624 330)">
|
||||||
|
<rect x="468" y="176" width="320" height="300" rx="36" fill="url(#expense)"/>
|
||||||
|
<path d="M570 352 L686 236 M596 236 H686 V326" fill="none" stroke="#ffffff" stroke-width="32" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- wallet -->
|
||||||
|
<rect x="196" y="420" width="632" height="420" rx="72" fill="url(#body)"/>
|
||||||
|
<!-- clasp tab -->
|
||||||
|
<rect x="590" y="560" width="268" height="152" rx="76" fill="#116b57"/>
|
||||||
|
<circle cx="666" cy="636" r="30" fill="#bdf7e3"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Re-renders the watch app icon (every size in AppIcon.appiconset), avatar.png
|
||||||
|
# and banner.png from their SVG sources. Requires resvg
|
||||||
|
# (https://github.com/linebender/resvg). Uses the bundled Roboto fonts, so the
|
||||||
|
# result does not depend on system fonts. icon.svg is the same artwork as the
|
||||||
|
# BalanceFlutter app icon (palette «Лес»).
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||||
|
fonts=(
|
||||||
|
--skip-system-fonts
|
||||||
|
--use-font-file fonts/Roboto-Regular.ttf
|
||||||
|
--use-font-file fonts/Roboto-Medium.ttf
|
||||||
|
--use-font-file fonts/Roboto-Bold.ttf
|
||||||
|
)
|
||||||
|
icons=../BalanceWatch/Assets.xcassets/AppIcon.appiconset
|
||||||
|
for file in "$icons"/BalanceIcon-*.png; do
|
||||||
|
size="${file##*-}"; size="${size%.png}"
|
||||||
|
resvg -w "$size" -h "$size" icon.svg "$file"
|
||||||
|
done
|
||||||
|
resvg "${fonts[@]}" -w 512 -h 512 avatar.svg avatar.png
|
||||||
|
resvg "${fonts[@]}" banner.svg banner.png
|
||||||
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 102 KiB |