feat(web): tane.comunes.org landing + localized app screenshots
- Hugo site (site/): EN/ES intro + legal (generated from docs/legal), an
ES/EN About page from docs/{que-es-tane,what-is-tane}.md, seed-green theme,
responsive header, store-badge placeholders, multi-stage Hugo->nginx image.
- Golden screenshot harness (test/screenshots/): en,es,fr,de,pt,ja + RTL demo,
real fonts via DejaVu Sans + FontManifest; skip-by-default tag so CI stays
green. collect_screenshots.sh feeds site/ and fastlane phoneScreenshots.
- Drop editorial notes from the public explainer.
|
|
@ -11,3 +11,13 @@
|
||||||
#
|
#
|
||||||
# A genuinely slower (but finite) test can opt out with @Timeout(Duration(...)).
|
# A genuinely slower (but finite) test can opt out with @Timeout(Duration(...)).
|
||||||
timeout: 90s
|
timeout: 90s
|
||||||
|
|
||||||
|
tags:
|
||||||
|
# Localized store/landing screenshots (test/screenshots/). These are asset
|
||||||
|
# GENERATION, not a correctness gate, and golden pixel-matching is sensitive
|
||||||
|
# to OS/font/Flutter version — so they must NOT run in the normal suite (incl.
|
||||||
|
# CI's `flutter test --coverage`). Run them explicitly:
|
||||||
|
# flutter test --run-skipped --tags screenshots test/screenshots/
|
||||||
|
# add --update-goldens to (re)write the PNGs.
|
||||||
|
screenshots:
|
||||||
|
skip: "asset generation; run with --run-skipped --tags screenshots"
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 163 KiB |
|
After Width: | Height: | Size: 175 KiB |
|
After Width: | Height: | Size: 143 KiB |
|
After Width: | Height: | Size: 174 KiB |
|
After Width: | Height: | Size: 125 KiB |
|
After Width: | Height: | Size: 165 KiB |
|
After Width: | Height: | Size: 179 KiB |
|
After Width: | Height: | Size: 144 KiB |
|
After Width: | Height: | Size: 184 KiB |
43
apps/app_seeds/test/screenshots/README.md
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
# Localized app screenshots
|
||||||
|
|
||||||
|
Golden-based harness that renders key screens as PNGs in several languages, for
|
||||||
|
the landing site (`tane.comunes.org`) and the app-store listings. No emulator —
|
||||||
|
it runs under `flutter test`, so it's deterministic and CI-friendly.
|
||||||
|
|
||||||
|
## Regenerate
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd apps/app_seeds
|
||||||
|
flutter test --update-goldens --run-skipped --tags screenshots test/screenshots/screenshots_test.dart
|
||||||
|
tool/collect_screenshots.sh # copy PNGs into site/ and fastlane/
|
||||||
|
```
|
||||||
|
|
||||||
|
Drop `--update-goldens` to *verify* the committed PNGs still match. The
|
||||||
|
`screenshots` tag is **skipped by default** (`dart_test.yaml`) — so it never
|
||||||
|
runs in the normal suite or CI's `flutter test --coverage`, where golden pixel
|
||||||
|
differences across OS/font versions would cause false failures. `--run-skipped`
|
||||||
|
opts it back in.
|
||||||
|
|
||||||
|
## What it produces
|
||||||
|
|
||||||
|
`goldens/<locale>/<screen>.png` for **en, es, fr, de, pt, ja**, screens
|
||||||
|
**home · inventory · market · calendar · detail**, plus `goldens/rtl/inventory.png`.
|
||||||
|
The market is rendered populated via a seeded `OffersCubit` with a no-op transport
|
||||||
|
(no network); home/calendar/detail/inventory render from the repository alone.
|
||||||
|
|
||||||
|
- Fonts: real glyphs come from `loadScreenshotFonts()` — every family in
|
||||||
|
`FontManifest.json` (MaterialIcons + bundled Noto/seedks) plus the bundled
|
||||||
|
**DejaVu Sans** as the Latin text face. No Roboto needed.
|
||||||
|
- `rtl/` is a **layout demo**, not a locale: English strings forced
|
||||||
|
right-to-left to prove the design is RTL-safe. The app ships no Arabic
|
||||||
|
translation yet, so there is deliberately no `ar/` with Arabic copy.
|
||||||
|
- Partial locales (e.g. `ja`) fall back to English for untranslated strings —
|
||||||
|
that's the app's real runtime behavior, shown honestly.
|
||||||
|
- These are raw device-canvas frames (no marketing captions or device bezels).
|
||||||
|
Add those downstream only if a store rejects plain screenshots.
|
||||||
|
|
||||||
|
## Adding a screen or locale
|
||||||
|
|
||||||
|
Edit `screenshotLocales` / the `screens` map in `screenshots_test.dart`. Screens
|
||||||
|
must render from the repository + cubits alone (no live Nostr/network stack), so
|
||||||
|
inventory/detail/calendar are the safe, self-contained set.
|
||||||
BIN
apps/app_seeds/test/screenshots/goldens/de/calendar.png
Normal file
|
After Width: | Height: | Size: 146 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/de/detail.png
Normal file
|
After Width: | Height: | Size: 180 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/de/home.png
Normal file
|
After Width: | Height: | Size: 119 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/de/inventory.png
Normal file
|
After Width: | Height: | Size: 164 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/de/market.png
Normal file
|
After Width: | Height: | Size: 185 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/en/calendar.png
Normal file
|
After Width: | Height: | Size: 143 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/en/detail.png
Normal file
|
After Width: | Height: | Size: 174 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/en/home.png
Normal file
|
After Width: | Height: | Size: 116 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/en/inventory.png
Normal file
|
After Width: | Height: | Size: 163 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/en/market.png
Normal file
|
After Width: | Height: | Size: 175 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/es/calendar.png
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/es/detail.png
Normal file
|
After Width: | Height: | Size: 184 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/es/home.png
Normal file
|
After Width: | Height: | Size: 125 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/es/inventory.png
Normal file
|
After Width: | Height: | Size: 165 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/es/market.png
Normal file
|
After Width: | Height: | Size: 179 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/fr/calendar.png
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/fr/detail.png
Normal file
|
After Width: | Height: | Size: 181 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/fr/home.png
Normal file
|
After Width: | Height: | Size: 129 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/fr/inventory.png
Normal file
|
After Width: | Height: | Size: 167 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/fr/market.png
Normal file
|
After Width: | Height: | Size: 186 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/ja/calendar.png
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/ja/detail.png
Normal file
|
After Width: | Height: | Size: 174 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/ja/home.png
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/ja/inventory.png
Normal file
|
After Width: | Height: | Size: 163 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/ja/market.png
Normal file
|
After Width: | Height: | Size: 175 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/pt/calendar.png
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/pt/detail.png
Normal file
|
After Width: | Height: | Size: 185 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/pt/home.png
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/pt/inventory.png
Normal file
|
After Width: | Height: | Size: 166 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/pt/market.png
Normal file
|
After Width: | Height: | Size: 172 KiB |
BIN
apps/app_seeds/test/screenshots/goldens/rtl/inventory.png
Normal file
|
After Width: | Height: | Size: 165 KiB |
188
apps/app_seeds/test/screenshots/screenshot_support.dart
Normal file
|
|
@ -0,0 +1,188 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:commons_core/commons_core.dart';
|
||||||
|
import 'package:drift/drift.dart' show Value;
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart' show FontLoader, rootBundle;
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:tane/app.dart' show materialLocaleFor;
|
||||||
|
import 'package:tane/data/species_repository.dart';
|
||||||
|
import 'package:tane/data/variety_repository.dart';
|
||||||
|
import 'package:tane/db/database.dart';
|
||||||
|
import 'package:tane/db/enums.dart';
|
||||||
|
import 'package:tane/domain/crop_calendar.dart' show monthsToMask;
|
||||||
|
import 'package:tane/i18n/strings.g.dart';
|
||||||
|
import 'package:tane/state/inventory_cubit.dart';
|
||||||
|
import 'package:tane/ui/theme.dart';
|
||||||
|
|
||||||
|
/// Marketing screenshot canvas: a tall phone at 3x. Logical 360x760.
|
||||||
|
const screenshotSize = Size(1080, 2280);
|
||||||
|
const screenshotPixelRatio = 3.0;
|
||||||
|
|
||||||
|
/// Locales we render real, translated screenshots for. Asturian (`ast`) is
|
||||||
|
/// omitted from store assets; the RTL demo is handled separately (the app has
|
||||||
|
/// no Arabic strings yet — see [ScreenshotLocale.rtlDemo]).
|
||||||
|
const screenshotLocales = <AppLocale>[
|
||||||
|
AppLocale.en,
|
||||||
|
AppLocale.es,
|
||||||
|
AppLocale.fr,
|
||||||
|
AppLocale.de,
|
||||||
|
AppLocale.pt,
|
||||||
|
AppLocale.ja,
|
||||||
|
];
|
||||||
|
|
||||||
|
bool _fontsLoaded = false;
|
||||||
|
|
||||||
|
/// Registers real glyph-bearing fonts so golden screenshots show text and icons
|
||||||
|
/// rather than the Ahem/tofu boxes flutter_test uses by default. Loads every
|
||||||
|
/// family declared in the test bundle's FontManifest.json (MaterialIcons, the
|
||||||
|
/// bundled Noto Sans JP/Arabic and seedks glyphs), then adds DejaVu Sans — a
|
||||||
|
/// bundled asset, not a font *family* — as the Latin text face so en/es/fr/de/pt
|
||||||
|
/// render legibly without needing Roboto.
|
||||||
|
Future<void> loadScreenshotFonts() async {
|
||||||
|
if (_fontsLoaded) return;
|
||||||
|
|
||||||
|
Future<void> family(String name, List<String> assets) async {
|
||||||
|
final loader = FontLoader(name);
|
||||||
|
for (final asset in assets) {
|
||||||
|
loader.addFont(rootBundle.load(asset));
|
||||||
|
}
|
||||||
|
await loader.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
final manifest =
|
||||||
|
json.decode(await rootBundle.loadString('FontManifest.json'))
|
||||||
|
as List<dynamic>;
|
||||||
|
for (final entry in manifest.cast<Map<String, dynamic>>()) {
|
||||||
|
await family(
|
||||||
|
entry['family'] as String,
|
||||||
|
[for (final f in entry['fonts'] as List) f['asset'] as String],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await family('DejaVu Sans', [
|
||||||
|
'assets/fonts/DejaVuSans.ttf',
|
||||||
|
'assets/fonts/DejaVuSans-Bold.ttf',
|
||||||
|
]);
|
||||||
|
_fontsLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const _fontFallbacks = <String>['Noto Sans JP', 'Noto Sans Arabic'];
|
||||||
|
|
||||||
|
/// The real app theme, but with every text style pinned to the bundled
|
||||||
|
/// [DejaVu Sans] so screenshots render legible glyphs (see [loadScreenshotFonts]).
|
||||||
|
ThemeData screenshotTheme() {
|
||||||
|
final base = buildTaneTheme();
|
||||||
|
return base.copyWith(
|
||||||
|
textTheme: base.textTheme.apply(
|
||||||
|
fontFamily: 'DejaVu Sans',
|
||||||
|
fontFamilyFallback: _fontFallbacks,
|
||||||
|
),
|
||||||
|
primaryTextTheme: base.primaryTextTheme.apply(
|
||||||
|
fontFamily: 'DejaVu Sans',
|
||||||
|
fontFamilyFallback: _fontFallbacks,
|
||||||
|
),
|
||||||
|
appBarTheme: base.appBarTheme.copyWith(
|
||||||
|
titleTextStyle: base.appBarTheme.titleTextStyle?.copyWith(
|
||||||
|
fontFamily: 'DejaVu Sans',
|
||||||
|
fontFamilyFallback: _fontFallbacks,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wraps [child] as a full screen for capture: the real green theme with
|
||||||
|
/// screenshot fonts, the given [locale], repositories and an [InventoryCubit].
|
||||||
|
/// Pass [textDirection] to force RTL for the layout demo.
|
||||||
|
Widget screenshotApp({
|
||||||
|
required VarietyRepository repository,
|
||||||
|
required SpeciesRepository species,
|
||||||
|
required Widget child,
|
||||||
|
AppLocale locale = AppLocale.en,
|
||||||
|
TextDirection? textDirection,
|
||||||
|
}) {
|
||||||
|
LocaleSettings.setLocaleSync(locale);
|
||||||
|
return TranslationProvider(
|
||||||
|
child: MultiRepositoryProvider(
|
||||||
|
providers: [
|
||||||
|
RepositoryProvider.value(value: repository),
|
||||||
|
RepositoryProvider.value(value: species),
|
||||||
|
],
|
||||||
|
child: BlocProvider(
|
||||||
|
create: (_) => InventoryCubit(repository),
|
||||||
|
child: MaterialApp(
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
|
theme: screenshotTheme(),
|
||||||
|
locale: materialLocaleFor(locale.flutterLocale),
|
||||||
|
supportedLocales: AppLocaleUtils.supportedLocales,
|
||||||
|
localizationsDelegates: const [
|
||||||
|
GlobalMaterialLocalizations.delegate,
|
||||||
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
],
|
||||||
|
home: textDirection == null
|
||||||
|
? child
|
||||||
|
: Directionality(textDirection: textDirection, child: child),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One seeded variety plus the id of a "hero" variety used for the detail shot.
|
||||||
|
class SeededInventory {
|
||||||
|
const SeededInventory(this.heroVarietyId);
|
||||||
|
final String heroVarietyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Seeds an attractive, internationally-neutral inventory: a spread of
|
||||||
|
/// colour-coded botanical families, lots with years/quantities/origins, photo
|
||||||
|
/// avatars, and crop-calendar month masks so the calendar screen is populated.
|
||||||
|
/// Returns the hero variety (a cherry tomato with several lots) for detail.
|
||||||
|
Future<SeededInventory> seedShowcase(
|
||||||
|
AppDatabase db,
|
||||||
|
VarietyRepository repo,
|
||||||
|
) async {
|
||||||
|
// Sow in spring, harvest seed in late summer — visible in the calendar.
|
||||||
|
final sow = monthsToMask(const [3, 4, 5]);
|
||||||
|
final harvest = monthsToMask(const [8, 9]);
|
||||||
|
|
||||||
|
// Varieties render with the app's default coloured-initial disc avatar —
|
||||||
|
// the on-brand default when no photo is set — so no image decoding is needed.
|
||||||
|
Future<String> add(String label, String family) async {
|
||||||
|
final id = await repo.addQuickVariety(label: label, category: family);
|
||||||
|
await (db.update(db.varieties)..where((v) => v.id.equals(id))).write(
|
||||||
|
VarietiesCompanion(
|
||||||
|
sowMonths: Value(sow),
|
||||||
|
seedHarvestMonths: Value(harvest),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
final tomato = await add('Cherry tomato', 'Solanaceae');
|
||||||
|
await repo.addLot(
|
||||||
|
varietyId: tomato,
|
||||||
|
harvestYear: 2024,
|
||||||
|
quantity: const Quantity(kind: QuantityKind.handful),
|
||||||
|
originName: 'Community seed swap',
|
||||||
|
abundance: Abundance.plentyToShare,
|
||||||
|
offerStatus: OfferStatus.shared,
|
||||||
|
);
|
||||||
|
await repo.addLot(
|
||||||
|
varietyId: tomato,
|
||||||
|
harvestYear: 2023,
|
||||||
|
quantity: const Quantity(kind: QuantityKind.packet, count: 1),
|
||||||
|
offerStatus: OfferStatus.exchange,
|
||||||
|
);
|
||||||
|
|
||||||
|
await add('Climbing bean', 'Fabaceae');
|
||||||
|
await add('Sunflower', 'Asteraceae');
|
||||||
|
await add('Sweet basil', 'Lamiaceae');
|
||||||
|
await add('Rainbow chard', 'Amaranthaceae');
|
||||||
|
await add('Purple carrot', 'Apiaceae');
|
||||||
|
|
||||||
|
return SeededInventory(tomato);
|
||||||
|
}
|
||||||
161
apps/app_seeds/test/screenshots/screenshots_test.dart
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
@Tags(['screenshots'])
|
||||||
|
library;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:commons_core/commons_core.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:tane/data/species_repository.dart';
|
||||||
|
import 'package:tane/data/variety_repository.dart';
|
||||||
|
import 'package:tane/db/database.dart';
|
||||||
|
import 'package:tane/i18n/strings.g.dart';
|
||||||
|
import 'package:tane/state/offers_cubit.dart';
|
||||||
|
import 'package:tane/state/variety_detail_cubit.dart';
|
||||||
|
import 'package:tane/ui/calendar_screen.dart';
|
||||||
|
import 'package:tane/ui/home_screen.dart';
|
||||||
|
import 'package:tane/ui/inventory_list_screen.dart';
|
||||||
|
import 'package:tane/ui/market_screen.dart';
|
||||||
|
import 'package:tane/ui/variety_detail_screen.dart';
|
||||||
|
|
||||||
|
import '../support/test_support.dart';
|
||||||
|
import 'screenshot_support.dart';
|
||||||
|
|
||||||
|
/// Generates localized marketing/store screenshots as golden PNGs.
|
||||||
|
///
|
||||||
|
/// Run (writes the PNGs) — the `screenshots` tag is skipped by default
|
||||||
|
/// (dart_test.yaml), so `--run-skipped` is required:
|
||||||
|
/// flutter test --update-goldens --run-skipped --tags screenshots test/screenshots/screenshots_test.dart
|
||||||
|
///
|
||||||
|
/// Output: test/screenshots/goldens/`<locale>`/`<screen>`.png for en/es/fr/de/pt/ja,
|
||||||
|
/// plus goldens/rtl/ — a right-to-left *layout* demo (English strings mirrored;
|
||||||
|
/// the app ships no Arabic translation yet, so this proves the design is RTL-safe
|
||||||
|
/// rather than claiming an Arabic locale). These are raw device-canvas frames
|
||||||
|
/// with no marketing chrome; add captions/device frames downstream if a store
|
||||||
|
/// requires them. `tool/collect_screenshots.sh` copies them into site/ and fastlane/.
|
||||||
|
void main() {
|
||||||
|
setUpAll(() async {
|
||||||
|
await loadScreenshotFonts();
|
||||||
|
// Touch mode never draws focus rings, so no highlight frames the FAB/fields.
|
||||||
|
FocusManager.instance.highlightStrategy =
|
||||||
|
FocusHighlightStrategy.alwaysTouch;
|
||||||
|
});
|
||||||
|
|
||||||
|
late AppDatabase db;
|
||||||
|
late VarietyRepository repo;
|
||||||
|
late SpeciesRepository species;
|
||||||
|
late SeededInventory seeded;
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
db = newTestDatabase();
|
||||||
|
repo = newTestRepository(db);
|
||||||
|
species = newTestSpeciesRepository(db);
|
||||||
|
seeded = await seedShowcase(db, repo);
|
||||||
|
});
|
||||||
|
tearDown(() => db.close());
|
||||||
|
|
||||||
|
Future<void> capture(WidgetTester tester, String dir, String name) async {
|
||||||
|
tester.view.physicalSize = screenshotSize;
|
||||||
|
tester.view.devicePixelRatio = screenshotPixelRatio;
|
||||||
|
addTearDown(tester.view.resetPhysicalSize);
|
||||||
|
addTearDown(tester.view.resetDevicePixelRatio);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await expectLater(
|
||||||
|
find.byType(MaterialApp),
|
||||||
|
matchesGoldenFile('goldens/$dir/$name.png'),
|
||||||
|
);
|
||||||
|
await disposeTree(tester);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A POPULATED market: a seeded OffersCubit (no transport) feeding MarketBody,
|
||||||
|
// so the offer list renders with no live discovery stream to hang
|
||||||
|
// pumpAndSettle. (Live discovery is exercised by offers_cubit_test.dart; see
|
||||||
|
// the note in market_screen_test.dart on why we don't drive it via widgets.)
|
||||||
|
Future<Widget> market() async => Builder(
|
||||||
|
builder: (context) => Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(context.t.market.title),
|
||||||
|
actions: [IconButton(icon: const Icon(Icons.tune), onPressed: () {})],
|
||||||
|
),
|
||||||
|
body: BlocProvider<OffersCubit>(
|
||||||
|
create: (_) => _SeededOffersCubit(_sampleOffers()),
|
||||||
|
child: MarketBody(onConfigure: () {}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Each entry renders one screen; the runner re-seeds and re-locales per shot.
|
||||||
|
final screens = <String, Future<Widget> Function()>{
|
||||||
|
'home': () async => const HomeScreen(marketEnabled: true),
|
||||||
|
'inventory': () async => const InventoryListScreen(),
|
||||||
|
'market': market,
|
||||||
|
'calendar': () async => const CalendarScreen(initialMonth: 4),
|
||||||
|
'detail': () async => BlocProvider(
|
||||||
|
create: (_) => VarietyDetailCubit(repo, seeded.heroVarietyId),
|
||||||
|
child: const VarietyDetailScreen(),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (final locale in screenshotLocales) {
|
||||||
|
for (final entry in screens.entries) {
|
||||||
|
testWidgets('${locale.languageCode}/${entry.key}', (tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
screenshotApp(
|
||||||
|
repository: repo,
|
||||||
|
species: species,
|
||||||
|
locale: locale,
|
||||||
|
child: await entry.value(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await capture(tester, locale.languageCode, entry.key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RTL layout demo: base (English) strings, forced right-to-left, so the
|
||||||
|
// mirrored inventory proves the design is RTL-safe. Not a translated locale.
|
||||||
|
testWidgets('rtl/inventory', (tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
screenshotApp(
|
||||||
|
repository: repo,
|
||||||
|
species: species,
|
||||||
|
locale: AppLocale.en,
|
||||||
|
textDirection: TextDirection.rtl,
|
||||||
|
child: const InventoryListScreen(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await capture(tester, 'rtl', 'inventory');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An [OffersCubit] pre-loaded with a fixed offer list. It carries a no-op
|
||||||
|
/// transport (never called — offers are pre-seeded) purely so `isOnline` is
|
||||||
|
/// true and MarketBody renders the list instead of the "can't reach" state.
|
||||||
|
class _SeededOffersCubit extends OffersCubit {
|
||||||
|
_SeededOffersCubit(List<Offer> offers) : super(_NoopOfferTransport()) {
|
||||||
|
emit(state.copyWith(offers: offers, hasSearched: true, areaGeohash: 'sp3e'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A transport that is present (so the market reads as online) but never used.
|
||||||
|
class _NoopOfferTransport implements OfferTransport {
|
||||||
|
@override
|
||||||
|
Stream<Offer> discover(DiscoveryQuery query) => const Stream.empty();
|
||||||
|
@override
|
||||||
|
Future<PublishResult> publish(Offer offer) => throw UnimplementedError();
|
||||||
|
@override
|
||||||
|
Future<void> retract(String offerId) async {}
|
||||||
|
@override
|
||||||
|
Future<void> close() async {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A small, internationally-neutral set of nearby offers across gift / swap /
|
||||||
|
/// sale and colour-coded botanical families.
|
||||||
|
List<Offer> _sampleOffers() => [
|
||||||
|
Offer(id: 'o1', authorPubkeyHex: 'a1' * 32, summary: 'Cherry tomato', type: OfferType.gift, category: 'Solanaceae', approxGeohash: 'sp3e9', isOrganic: true),
|
||||||
|
Offer(id: 'o2', authorPubkeyHex: 'a2' * 32, summary: 'Climbing bean', type: OfferType.exchange, category: 'Fabaceae', approxGeohash: 'sp3e8'),
|
||||||
|
Offer(id: 'o3', authorPubkeyHex: 'a3' * 32, summary: 'Sunflower', type: OfferType.sale, category: 'Asteraceae', approxGeohash: 'sp3ec', priceAmount: 2, priceCurrency: '€'),
|
||||||
|
Offer(id: 'o4', authorPubkeyHex: 'a4' * 32, summary: 'Sweet basil', type: OfferType.gift, category: 'Lamiaceae', approxGeohash: 'sp3e2', isOrganic: true),
|
||||||
|
Offer(id: 'o5', authorPubkeyHex: 'a5' * 32, summary: 'Purple carrot', type: OfferType.exchange, category: 'Apiaceae', approxGeohash: 'sp3ef'),
|
||||||
|
];
|
||||||
54
apps/app_seeds/tool/collect_screenshots.sh
Executable file
|
|
@ -0,0 +1,54 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Copies the localized golden screenshots into the two places that consume them:
|
||||||
|
# 1. the Hugo landing site (site/assets/screenshots/<locale>/)
|
||||||
|
# 2. the Play Store metadata (fastlane/metadata/android/<play-locale>/images/phoneScreenshots/)
|
||||||
|
#
|
||||||
|
# Regenerate the PNGs first:
|
||||||
|
# flutter test --update-goldens --tags screenshots test/screenshots/screenshots_test.dart
|
||||||
|
# then run this from apps/app_seeds/:
|
||||||
|
# tool/collect_screenshots.sh
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
here="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" # apps/app_seeds
|
||||||
|
repo_root="$(cd "$here/../.." && pwd)"
|
||||||
|
goldens="$here/test/screenshots/goldens"
|
||||||
|
site_dir="$repo_root/site/assets/screenshots"
|
||||||
|
fastlane_dir="$here/fastlane/metadata/android"
|
||||||
|
|
||||||
|
if [[ ! -d "$goldens" ]]; then
|
||||||
|
echo "No goldens at $goldens — run the screenshots test with --update-goldens first." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# App locale (golden dir) -> Play Store locale (fastlane dir). 'rtl' is a layout
|
||||||
|
# demo only (not a real locale) and goes to the site, not the stores.
|
||||||
|
declare -A play_locale=(
|
||||||
|
[en]=en-US [es]=es-ES [fr]=fr-FR [de]=de-DE [pt]=pt-PT [ja]=ja-JP
|
||||||
|
)
|
||||||
|
|
||||||
|
for dir in "$goldens"/*/; do
|
||||||
|
locale="$(basename "$dir")"
|
||||||
|
|
||||||
|
# 1) Landing site: every locale, including the RTL demo.
|
||||||
|
mkdir -p "$site_dir/$locale"
|
||||||
|
cp "$dir"*.png "$site_dir/$locale/"
|
||||||
|
|
||||||
|
# 2) Play Store: only locales that ALREADY have text metadata (a title.txt).
|
||||||
|
# A locale dir with images but no store text is invalid for `fastlane supply`,
|
||||||
|
# so we don't create image-only locales — add the text first, then it's picked up.
|
||||||
|
play="${play_locale[$locale]:-}"
|
||||||
|
if [[ -n "$play" && -f "$fastlane_dir/$play/title.txt" ]]; then
|
||||||
|
shots="$fastlane_dir/$play/images/phoneScreenshots"
|
||||||
|
mkdir -p "$shots"
|
||||||
|
# Number them so the store shows them in a deliberate order.
|
||||||
|
i=1
|
||||||
|
for name in home inventory market calendar detail; do
|
||||||
|
[[ -f "$dir$name.png" ]] && cp "$dir$name.png" "$shots/$i.png" && i=$((i + 1))
|
||||||
|
done
|
||||||
|
echo " store: $play ($((i - 1)) shots)"
|
||||||
|
fi
|
||||||
|
echo "site: $locale"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Done. To add a Play locale beyond en-US/es-ES, create its title/short/full"
|
||||||
|
echo "description text under fastlane/metadata/android/<locale>/ first, then re-run."
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
# Tane — qué es
|
# Tane — qué es
|
||||||
|
|
||||||
> *Texto divulgativo para todos los públicos, sin dar por sabidos los conceptos.*
|
|
||||||
|
|
||||||
**Tane** (種) es «semilla» en japonés; el nombre completo, *tanemaki* (種まき), es «sembrar / esparcir semillas».
|
**Tane** (種) es «semilla» en japonés; el nombre completo, *tanemaki* (種まき), es «sembrar / esparcir semillas».
|
||||||
|
|
||||||
## En una frase
|
## En una frase
|
||||||
|
|
@ -33,8 +31,6 @@ con libretas y hojas de cálculo.
|
||||||
|
|
||||||
## Por qué es distinta
|
## Por qué es distinta
|
||||||
|
|
||||||
*(cada idea, explicada)*
|
|
||||||
|
|
||||||
- **Es libre y de todos.** No es de ninguna empresa: cualquiera puede usarla, copiarla,
|
- **Es libre y de todos.** No es de ninguna empresa: cualquiera puede usarla, copiarla,
|
||||||
traducirla o mejorarla, gratis y para siempre. Es más una *receta que se comparte* que un
|
traducirla o mejorarla, gratis y para siempre. Es más una *receta que se comparte* que un
|
||||||
producto cerrado — a esto se le llama **software libre**. Y quien la mejore está obligado
|
producto cerrado — a esto se le llama **software libre**. Y quien la mejore está obligado
|
||||||
|
|
|
||||||
72
docs/what-is-tane.md
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
# Tane — what it is
|
||||||
|
|
||||||
|
**Tane** (種) means "seed" in Japanese; the full name, *tanemaki* (種まき), means "to sow / scatter seeds".
|
||||||
|
|
||||||
|
## In one sentence
|
||||||
|
|
||||||
|
A simple, free app to **keep your seeds and share them** with whoever you like. It works
|
||||||
|
**without internet** and with no company in the middle.
|
||||||
|
|
||||||
|
## Why it's needed
|
||||||
|
|
||||||
|
Today a handful of companies control much of the world's seed. Many seeds are made so they
|
||||||
|
won't serve again: they give one harvest, but their seeds don't germinate well, so you have
|
||||||
|
to **buy them again every year**. Meanwhile the traditional varieties — grown and improved
|
||||||
|
over thousands of years — are being lost, and with them the knowledge of how to grow them.
|
||||||
|
|
||||||
|
Sharing seeds is one of the oldest and most natural things there is, yet in some places it
|
||||||
|
has become complicated or even fined (in France an association was penalised for handing out
|
||||||
|
varieties that weren't on the official approved list). And the people who care for seeds
|
||||||
|
still do it with notebooks and spreadsheets.
|
||||||
|
|
||||||
|
## What it does, made easy
|
||||||
|
|
||||||
|
- **Keep** — your seed inventory: what you have, from which year, how much, where it came
|
||||||
|
from, under **the name you use** (no Latin required). The same whether you have four
|
||||||
|
packets in a drawer or a group with hundreds of varieties.
|
||||||
|
- **Share** — you say what you offer; someone nearby sees it and writes to you, to **give
|
||||||
|
away, swap or sell** if you want. You close the deal yourself, directly. No company steps
|
||||||
|
in or takes a commission.
|
||||||
|
|
||||||
|
## Why it's different
|
||||||
|
|
||||||
|
- **It's free and belongs to everyone.** It isn't owned by any company: anyone can use,
|
||||||
|
copy, translate or improve it, free and forever. It's more a *recipe that gets shared*
|
||||||
|
than a closed product — this is called **free software**. And whoever improves it is
|
||||||
|
bound to share those improvements just as freely, so no one can "close" it later (that's
|
||||||
|
**copyleft**).
|
||||||
|
- **It works offline.** The basics run even out in the field with no signal.
|
||||||
|
- **Your data is yours.** Your information is kept on **your own phone**, protected with a
|
||||||
|
key, not on some company's computers. We don't sell it or upload it anywhere. No ads.
|
||||||
|
- **No one can shut it down or control it.** There's no central computer that everything
|
||||||
|
passes through: people communicate **directly with each other**, like passing seeds from
|
||||||
|
hand to hand. So no one can censor it or charge you to use it (that's what being
|
||||||
|
**decentralized** means).
|
||||||
|
- **You trust by word of mouth.** You deal with people you know and with those vouched for
|
||||||
|
by people you trust — the way it's always worked in a village or at a fair — without
|
||||||
|
having to give your real name or phone number unless you want to.
|
||||||
|
- **In many languages.** For anywhere in the world, not for a single country. Volunteers
|
||||||
|
translate it.
|
||||||
|
|
||||||
|
## Who it's for
|
||||||
|
|
||||||
|
Seed groups and networks, gardens and collectives, growers… and anyone with a few packets
|
||||||
|
put away. For all ages, from 10 to 80.
|
||||||
|
|
||||||
|
## The name
|
||||||
|
|
||||||
|
**Tane** means "seed" (種). It comes from *tanemaki* (種まき), "to sow seeds" in Japanese, a
|
||||||
|
nod to old customs of mutual aid between neighbours (sharing the work, saving together).
|
||||||
|
|
||||||
|
## Where it stands and how to help
|
||||||
|
|
||||||
|
In its early days. You can help by trying it out in a real seed group, translating it,
|
||||||
|
sharing what you know about the varieties, or coding. It has no price and never will: it's
|
||||||
|
sustained by volunteers, by the communities themselves, and by public funding for projects
|
||||||
|
of common interest.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> *The idea underneath: traditional seeds are a heritage of humankind — everyone's and no
|
||||||
|
> one's. Against those who would privatise them, Tane aims to make it easy for you to keep
|
||||||
|
> them, multiply them and keep them moving.*
|
||||||
4
site/.dockerignore
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
public/
|
||||||
|
resources/
|
||||||
|
.hugo_build.lock
|
||||||
|
.git/
|
||||||
3
site/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
public/
|
||||||
|
resources/
|
||||||
|
.hugo_build.lock
|
||||||
13
site/Dockerfile
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Multi-stage: build the static site with Hugo, serve it with nginx.
|
||||||
|
# Build context is site/ only — legal pages are pre-generated (build-legal.sh)
|
||||||
|
# and committed, and screenshots are copied in by collect_screenshots.sh, so no
|
||||||
|
# other part of the repo is needed at image-build time.
|
||||||
|
FROM hugomods/hugo:exts-0.140.2 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
COPY . .
|
||||||
|
RUN hugo --minify --gc
|
||||||
|
|
||||||
|
FROM nginx:alpine
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
COPY --from=build /src/public /usr/share/nginx/html
|
||||||
|
EXPOSE 80
|
||||||
198
site/assets/css/main.css
Normal file
|
|
@ -0,0 +1,198 @@
|
||||||
|
:root {
|
||||||
|
--green: #2f7d34;
|
||||||
|
--green-dark: #22521e;
|
||||||
|
--appbar: #3a8434;
|
||||||
|
--canvas: #e7f1e0;
|
||||||
|
--container: #d3e8c8;
|
||||||
|
--on-container: #2f6d2a;
|
||||||
|
--title: #1b2416;
|
||||||
|
--muted: #5c6b52;
|
||||||
|
--field: #f3f7ee;
|
||||||
|
--outline: #c3cbb6;
|
||||||
|
--white: #fff;
|
||||||
|
--maxw: 1060px;
|
||||||
|
--radius: 18px;
|
||||||
|
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
html { scroll-behavior: smooth; }
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--title);
|
||||||
|
background: var(--canvas);
|
||||||
|
line-height: 1.55;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
img { max-width: 100%; display: block; }
|
||||||
|
a { color: var(--green); }
|
||||||
|
|
||||||
|
.skip-link {
|
||||||
|
position: absolute;
|
||||||
|
inset-inline-start: -999px;
|
||||||
|
background: var(--green);
|
||||||
|
color: #fff;
|
||||||
|
padding: .6rem 1rem;
|
||||||
|
border-radius: 0 0 8px 0;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
.skip-link:focus { inset-inline-start: 0; }
|
||||||
|
|
||||||
|
/* Header */
|
||||||
|
.site-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: .4rem 1.2rem;
|
||||||
|
padding: .7rem clamp(1rem, 4vw, 2rem);
|
||||||
|
background: var(--appbar);
|
||||||
|
color: #fff;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
.brand {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: .55rem;
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.brand img { border-radius: 8px; }
|
||||||
|
.site-nav {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: .5rem 1.1rem;
|
||||||
|
}
|
||||||
|
.site-nav a { color: #fff; text-decoration: none; opacity: .95; white-space: nowrap; }
|
||||||
|
.site-nav a:hover { text-decoration: underline; }
|
||||||
|
.lang-switch { display: inline-flex; gap: .6rem; font-size: .92rem; white-space: nowrap; }
|
||||||
|
.lang-switch .current { opacity: .7; }
|
||||||
|
.lang-switch a.lang { color: #fff; }
|
||||||
|
|
||||||
|
/* Narrow phones: brand on its own row, nav wraps beneath it, left-aligned. */
|
||||||
|
@media (max-width: 560px) {
|
||||||
|
.site-header { gap: .5rem; }
|
||||||
|
.brand { font-size: 1.15rem; }
|
||||||
|
.site-nav { width: 100%; gap: .4rem 1rem; font-size: .95rem; }
|
||||||
|
}
|
||||||
|
|
||||||
|
main { max-width: var(--maxw); margin: 0 auto; padding: 0 clamp(1rem, 4vw, 2rem); }
|
||||||
|
|
||||||
|
/* Hero */
|
||||||
|
.hero {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1.1fr .9fr;
|
||||||
|
gap: clamp(1.5rem, 4vw, 3rem);
|
||||||
|
align-items: center;
|
||||||
|
padding: clamp(2rem, 6vw, 4rem) 0 2rem;
|
||||||
|
}
|
||||||
|
.hero h1 {
|
||||||
|
font-size: clamp(2rem, 5vw, 3.1rem);
|
||||||
|
line-height: 1.1;
|
||||||
|
margin: 0 0 .6rem;
|
||||||
|
color: var(--green-dark);
|
||||||
|
}
|
||||||
|
.hero .tagline { font-size: 1.25rem; font-weight: 600; color: var(--on-container); margin: 0 0 1rem; }
|
||||||
|
.hero .lead { font-size: 1.05rem; margin: 0 0 1rem; }
|
||||||
|
.hero-shot { display: flex; justify-content: center; }
|
||||||
|
|
||||||
|
/* Phone frame */
|
||||||
|
.phone {
|
||||||
|
background: #12261a;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 28px;
|
||||||
|
box-shadow: 0 18px 40px rgba(20, 50, 25, .22);
|
||||||
|
width: 232px;
|
||||||
|
max-width: 62vw;
|
||||||
|
}
|
||||||
|
.phone img { border-radius: 20px; }
|
||||||
|
|
||||||
|
/* Sections */
|
||||||
|
section { margin: 2.5rem 0; }
|
||||||
|
h2 { font-size: clamp(1.4rem, 3vw, 1.9rem); color: var(--green-dark); }
|
||||||
|
|
||||||
|
.pillars {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 1.2rem;
|
||||||
|
}
|
||||||
|
.pillar {
|
||||||
|
background: var(--white);
|
||||||
|
border: 1px solid var(--outline);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
padding: 1.4rem;
|
||||||
|
}
|
||||||
|
.pillar-icon { font-size: 1.8rem; }
|
||||||
|
.pillar h2 { font-size: 1.2rem; margin: .5rem 0; }
|
||||||
|
.pillar p { margin: 0; color: #2c3826; }
|
||||||
|
|
||||||
|
.shots .shot-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 1.2rem;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.shots h2 { text-align: center; }
|
||||||
|
|
||||||
|
.values {
|
||||||
|
background: var(--container);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
padding: 1.6rem clamp(1.2rem, 4vw, 2.2rem);
|
||||||
|
}
|
||||||
|
.values ul { margin: .5rem 0 0; padding-inline-start: 1.2rem; }
|
||||||
|
.values li { margin: .45rem 0; }
|
||||||
|
|
||||||
|
.get-it { text-align: center; }
|
||||||
|
.store-badges { margin-top: 1.2rem; }
|
||||||
|
.coming-soon { color: var(--muted); font-style: italic; }
|
||||||
|
.badge {
|
||||||
|
display: inline-block;
|
||||||
|
background: var(--green);
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: .7rem 1.4rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
margin: .3rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Legal docs */
|
||||||
|
.doc { padding: 2rem 0 3rem; max-width: 760px; }
|
||||||
|
.doc h1 { color: var(--green-dark); }
|
||||||
|
.doc-body h2 { font-size: 1.3rem; margin-top: 1.8rem; }
|
||||||
|
.doc-body { font-size: 1.02rem; }
|
||||||
|
.crumb { color: var(--muted); }
|
||||||
|
.legal-index { list-style: none; padding: 0; }
|
||||||
|
.legal-index li {
|
||||||
|
padding: 1rem 1.2rem;
|
||||||
|
background: var(--white);
|
||||||
|
border: 1px solid var(--outline);
|
||||||
|
border-radius: 12px;
|
||||||
|
margin: .7rem 0;
|
||||||
|
}
|
||||||
|
.legal-index a { font-weight: 600; font-size: 1.1rem; text-decoration: none; }
|
||||||
|
.legal-index .desc { display: block; color: var(--muted); font-size: .95rem; margin-top: .2rem; }
|
||||||
|
|
||||||
|
/* Footer */
|
||||||
|
.site-footer {
|
||||||
|
background: var(--green-dark);
|
||||||
|
color: #dfeeda;
|
||||||
|
margin-top: 3rem;
|
||||||
|
}
|
||||||
|
.foot-inner { max-width: var(--maxw); margin: 0 auto; padding: 2rem clamp(1rem, 4vw, 2rem); }
|
||||||
|
.foot-legal { display: flex; flex-wrap: wrap; gap: 1rem; margin-bottom: 1rem; }
|
||||||
|
.foot-legal a { color: #fff; }
|
||||||
|
.foot-note { margin: .3rem 0; font-size: .92rem; }
|
||||||
|
.site-footer a { color: #cfe7c3; }
|
||||||
|
|
||||||
|
@media (max-width: 820px) {
|
||||||
|
.hero { grid-template-columns: 1fr; }
|
||||||
|
.hero-shot { order: -1; }
|
||||||
|
.pillars { grid-template-columns: 1fr; }
|
||||||
|
}
|
||||||
BIN
site/assets/img/logo.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
site/assets/screenshots/de/calendar.png
Normal file
|
After Width: | Height: | Size: 146 KiB |
BIN
site/assets/screenshots/de/detail.png
Normal file
|
After Width: | Height: | Size: 180 KiB |
BIN
site/assets/screenshots/de/home.png
Normal file
|
After Width: | Height: | Size: 119 KiB |
BIN
site/assets/screenshots/de/inventory.png
Normal file
|
After Width: | Height: | Size: 164 KiB |
BIN
site/assets/screenshots/de/market.png
Normal file
|
After Width: | Height: | Size: 185 KiB |
BIN
site/assets/screenshots/en/calendar.png
Normal file
|
After Width: | Height: | Size: 143 KiB |
BIN
site/assets/screenshots/en/detail.png
Normal file
|
After Width: | Height: | Size: 174 KiB |
BIN
site/assets/screenshots/en/home.png
Normal file
|
After Width: | Height: | Size: 116 KiB |
BIN
site/assets/screenshots/en/inventory.png
Normal file
|
After Width: | Height: | Size: 163 KiB |
BIN
site/assets/screenshots/en/market.png
Normal file
|
After Width: | Height: | Size: 175 KiB |
BIN
site/assets/screenshots/es/calendar.png
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
site/assets/screenshots/es/detail.png
Normal file
|
After Width: | Height: | Size: 184 KiB |
BIN
site/assets/screenshots/es/home.png
Normal file
|
After Width: | Height: | Size: 125 KiB |
BIN
site/assets/screenshots/es/inventory.png
Normal file
|
After Width: | Height: | Size: 165 KiB |
BIN
site/assets/screenshots/es/market.png
Normal file
|
After Width: | Height: | Size: 179 KiB |
BIN
site/assets/screenshots/fr/calendar.png
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
site/assets/screenshots/fr/detail.png
Normal file
|
After Width: | Height: | Size: 181 KiB |
BIN
site/assets/screenshots/fr/home.png
Normal file
|
After Width: | Height: | Size: 129 KiB |
BIN
site/assets/screenshots/fr/inventory.png
Normal file
|
After Width: | Height: | Size: 167 KiB |
BIN
site/assets/screenshots/fr/market.png
Normal file
|
After Width: | Height: | Size: 186 KiB |
BIN
site/assets/screenshots/ja/calendar.png
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
site/assets/screenshots/ja/detail.png
Normal file
|
After Width: | Height: | Size: 174 KiB |
BIN
site/assets/screenshots/ja/home.png
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
site/assets/screenshots/ja/inventory.png
Normal file
|
After Width: | Height: | Size: 163 KiB |
BIN
site/assets/screenshots/ja/market.png
Normal file
|
After Width: | Height: | Size: 175 KiB |
BIN
site/assets/screenshots/pt/calendar.png
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
site/assets/screenshots/pt/detail.png
Normal file
|
After Width: | Height: | Size: 185 KiB |
BIN
site/assets/screenshots/pt/home.png
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
site/assets/screenshots/pt/inventory.png
Normal file
|
After Width: | Height: | Size: 166 KiB |
BIN
site/assets/screenshots/pt/market.png
Normal file
|
After Width: | Height: | Size: 172 KiB |
BIN
site/assets/screenshots/rtl/inventory.png
Normal file
|
After Width: | Height: | Size: 165 KiB |
71
site/build-legal.sh
Executable file
|
|
@ -0,0 +1,71 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Generates the Hugo legal pages from docs/legal/ — the single source of truth.
|
||||||
|
# Each doc's first "# Heading" becomes the page title (and is stripped from the
|
||||||
|
# body so it isn't rendered twice). Run from site/ whenever the legal docs change:
|
||||||
|
# ./build-legal.sh
|
||||||
|
# The generated content/legal/<slug>.<lang>.md files are committed so the Docker
|
||||||
|
# build only needs site/.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
site_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
legal_src="$(cd "$site_dir/../docs/legal" && pwd)"
|
||||||
|
out="$site_dir/content/legal"
|
||||||
|
|
||||||
|
# slug | English master | Spanish mirror | menu weight
|
||||||
|
rows=(
|
||||||
|
"privacy|privacy-policy.md|politica-de-privacidad.md|1"
|
||||||
|
"terms|terms-of-use.md|condiciones-de-uso.md|2"
|
||||||
|
"rules|community-rules.md|normas-de-la-comunidad.md|3"
|
||||||
|
"seeds|seed-legality-notice.md|aviso-sobre-semillas.md|4"
|
||||||
|
)
|
||||||
|
|
||||||
|
emit() { # <src-md> <out-md> <slug> <weight>
|
||||||
|
local src="$1" dst="$2" slug="$3" weight="$4"
|
||||||
|
# First "# ..." line -> title; everything after that line -> body. Strip a
|
||||||
|
# leading "Tane — " so it doesn't collide with the site title suffix in <title>.
|
||||||
|
local title
|
||||||
|
title="$(sed -n 's/^# \(.*\)/\1/p' "$src" | head -n1 \
|
||||||
|
| sed -E 's/^Tane[[:space:]]*(—|–|-)[[:space:]]*//')"
|
||||||
|
{
|
||||||
|
printf -- '---\n'
|
||||||
|
printf 'title: "%s"\n' "${title//\"/\\\"}"
|
||||||
|
printf 'slug: "%s"\n' "$slug"
|
||||||
|
printf 'weight: %s\n' "$weight"
|
||||||
|
printf 'translationKey: "legal-%s"\n' "$slug"
|
||||||
|
printf -- '---\n\n'
|
||||||
|
# Drop the first H1 line, keep the rest verbatim.
|
||||||
|
awk 'NR==1 && /^# /{next} {print}' "$src"
|
||||||
|
} > "$dst"
|
||||||
|
echo " $(basename "$dst") <- $(basename "$src")"
|
||||||
|
}
|
||||||
|
|
||||||
|
for row in "${rows[@]}"; do
|
||||||
|
IFS='|' read -r slug en es weight <<<"$row"
|
||||||
|
emit "$legal_src/$en" "$out/$slug.en.md" "$slug" "$weight"
|
||||||
|
emit "$legal_src/es/$es" "$out/$slug.es.md" "$slug" "$weight"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Generated legal pages in $out"
|
||||||
|
|
||||||
|
# --- Plain-language "About" page, generated from the docs/ explainers ---------
|
||||||
|
# Each page uses its own fixed title (not the doc's H1) and drops the first H1.
|
||||||
|
docs_dir="$(cd "$site_dir/../docs" && pwd)"
|
||||||
|
emit_about() { # <src-md> <out-md> <title> <description>
|
||||||
|
local src="$1" dst="$2" title="$3" desc="$4"
|
||||||
|
[[ -f "$src" ]] || return 0
|
||||||
|
{
|
||||||
|
printf -- '---\n'
|
||||||
|
printf 'title: "%s"\n' "$title"
|
||||||
|
printf 'slug: "about"\n'
|
||||||
|
printf 'translationKey: "about"\n'
|
||||||
|
printf 'description: "%s"\n' "$desc"
|
||||||
|
printf -- '---\n\n'
|
||||||
|
awk 'NR==1 && /^# /{next} {print}' "$src"
|
||||||
|
} > "$dst"
|
||||||
|
echo "Generated $(basename "$dst") <- $(basename "$src")"
|
||||||
|
}
|
||||||
|
|
||||||
|
emit_about "$docs_dir/que-es-tane.md" "$site_dir/content/about.es.md" \
|
||||||
|
"Qué es Tane" "Tane explicada para todos los públicos."
|
||||||
|
emit_about "$docs_dir/what-is-tane.md" "$site_dir/content/about.en.md" \
|
||||||
|
"What is Tane" "Tane explained for everyone."
|
||||||
36
site/config.toml
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
baseURL = "https://tane.comunes.org/"
|
||||||
|
title = "Tane"
|
||||||
|
defaultContentLanguage = "en"
|
||||||
|
defaultContentLanguageInSubdir = false
|
||||||
|
enableRobotsTXT = true
|
||||||
|
enableGitInfo = false
|
||||||
|
|
||||||
|
# Static, no-JS marketing site. Kept intentionally light: one page + a small
|
||||||
|
# legal section, English/Spanish. Screenshots come from the app's golden
|
||||||
|
# harness (apps/app_seeds/test/screenshots), copied in by collect_screenshots.sh.
|
||||||
|
|
||||||
|
[params]
|
||||||
|
email = "info@comunes.org"
|
||||||
|
publisher = "Asociación Comunes"
|
||||||
|
publisherURL = "https://comunes.org"
|
||||||
|
license = "AGPL-3.0"
|
||||||
|
# Store links are empty until the app is published; the badges partial renders
|
||||||
|
# a link only when its URL is set here, so nothing broken shows before launch.
|
||||||
|
playStoreURL = ""
|
||||||
|
fdroidURL = ""
|
||||||
|
|
||||||
|
[languages]
|
||||||
|
[languages.en]
|
||||||
|
languageName = "English"
|
||||||
|
languageCode = "en"
|
||||||
|
weight = 1
|
||||||
|
[languages.es]
|
||||||
|
languageName = "Español"
|
||||||
|
languageCode = "es"
|
||||||
|
weight = 2
|
||||||
|
|
||||||
|
[markup.goldmark.renderer]
|
||||||
|
unsafe = false
|
||||||
|
|
||||||
|
[minify]
|
||||||
|
disableJSON = true
|
||||||
34
site/content/_index.en.md
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
---
|
||||||
|
title: "Your seed bank in your pocket"
|
||||||
|
description: "A local-first, decentralized app for keeping and sharing traditional seeds. Offline, encrypted, no account."
|
||||||
|
hero:
|
||||||
|
title: "Your seed bank in your pocket"
|
||||||
|
tagline: "Keep and share traditional seeds — offline, encrypted, no account."
|
||||||
|
lead: >
|
||||||
|
Every traditional seed is a letter written by thousands of generations,
|
||||||
|
passed from hand to hand. Tane helps people and seed-saver collectives keep
|
||||||
|
a friendly inventory of their bank, decide what they offer, and share it
|
||||||
|
locally — without a central intermediary that could control it, censor it,
|
||||||
|
or be fined for it.
|
||||||
|
pillars:
|
||||||
|
- icon: "🌱"
|
||||||
|
title: "Manage your bank"
|
||||||
|
body: "Add a seed with just a photo and a name — no Latin required. Note the year, how much you have, where it came from, germination tests. Search, filter, snap now and name later."
|
||||||
|
- icon: "🔒"
|
||||||
|
title: "Yours, and only yours"
|
||||||
|
body: "Works fully offline. No account, no server, no trackers. Everything is encrypted on your device. Keep an encrypted backup and a printed recovery sheet to restore your whole bank on a new device."
|
||||||
|
- icon: "🤝"
|
||||||
|
title: "Share, as it's always been done"
|
||||||
|
body: "Mark what you have spare to give away, swap or sell. Someone nearby finds it and contacts you — no middleman. Print a catalog of what you share to take to a seed fair."
|
||||||
|
values:
|
||||||
|
title: "Why Tane is different"
|
||||||
|
points:
|
||||||
|
- "No center: no company can shut it down, censor it, or be fined for it."
|
||||||
|
- "Anti-monopoly by design — sharing seeds multiplies the commons instead of depleting it."
|
||||||
|
- "Spreads like a seed, person to person, with no server in the middle."
|
||||||
|
- "Free software (AGPL-3.0). No ads, no commissions, no business model."
|
||||||
|
---
|
||||||
|
|
||||||
|
Tane (種, "seed"; from *tanemaki* 種まき, "to sow seeds") is a local-first,
|
||||||
|
decentralized app for keeping and sharing traditional seeds. It works with zero
|
||||||
|
network — and connects you to a neighborly seed-sharing network when you want it.
|
||||||
35
site/content/_index.es.md
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
---
|
||||||
|
title: "Tu banco de semillas en el bolsillo"
|
||||||
|
description: "Una app local-first y descentralizada para guardar y compartir semillas tradicionales. Sin conexión, cifrada, sin cuenta."
|
||||||
|
hero:
|
||||||
|
title: "Tu banco de semillas en el bolsillo"
|
||||||
|
tagline: "Guarda y comparte semillas tradicionales: sin conexión, cifrado, sin cuenta."
|
||||||
|
lead: >
|
||||||
|
Cada semilla tradicional es una carta escrita por miles de generaciones, que
|
||||||
|
pasa de mano en mano. Tane ayuda a personas y colectivos de guardianas de
|
||||||
|
semillas a llevar un inventario amable de su banco, decidir qué ofrecen y
|
||||||
|
compartirlo localmente, sin un intermediario central que pueda controlarlo,
|
||||||
|
censurarlo o ser multado por ello.
|
||||||
|
pillars:
|
||||||
|
- icon: "🌱"
|
||||||
|
title: "Gestiona tu banco"
|
||||||
|
body: "Añade una semilla con solo una foto y un nombre; sin latín. Anota el año, cuánta tienes, de dónde viene, pruebas de germinación. Busca, filtra, haz la foto ahora y ponle nombre después."
|
||||||
|
- icon: "🔒"
|
||||||
|
title: "Tuyo, y solo tuyo"
|
||||||
|
body: "Funciona totalmente sin conexión. Sin cuenta, sin servidor, sin rastreadores. Todo va cifrado en tu dispositivo. Guarda una copia cifrada y una hoja de recuperación impresa para restaurar todo tu banco en un dispositivo nuevo."
|
||||||
|
- icon: "🤝"
|
||||||
|
title: "Compartir, como se ha hecho siempre"
|
||||||
|
body: "Marca lo que te sobra para regalar, intercambiar o vender. Alguien cerca lo encuentra y te contacta, sin intermediarios. Imprime un catálogo de lo que compartes para llevar a una feria de semillas."
|
||||||
|
values:
|
||||||
|
title: "Por qué Tane es diferente"
|
||||||
|
points:
|
||||||
|
- "Sin centro: ninguna empresa puede apagarlo, censurarlo ni ser multada por ello."
|
||||||
|
- "Anti-monopolio por diseño: compartir semillas multiplica el común en lugar de agotarlo."
|
||||||
|
- "Se propaga como una semilla, de persona a persona, sin servidor en medio."
|
||||||
|
- "Software libre (AGPL-3.0). Sin anuncios, sin comisiones, sin modelo de negocio."
|
||||||
|
---
|
||||||
|
|
||||||
|
Tane (種, "semilla"; de *tanemaki* 種まき, "sembrar semillas") es una app
|
||||||
|
local-first y descentralizada para guardar y compartir semillas tradicionales.
|
||||||
|
Funciona sin ninguna red, y te conecta a una red vecinal de intercambio de
|
||||||
|
semillas cuando tú quieras.
|
||||||
78
site/content/about.en.md
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
---
|
||||||
|
title: "What is Tane"
|
||||||
|
slug: "about"
|
||||||
|
translationKey: "about"
|
||||||
|
description: "Tane explained for everyone."
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Tane** (種) means "seed" in Japanese; the full name, *tanemaki* (種まき), means "to sow / scatter seeds".
|
||||||
|
|
||||||
|
## In one sentence
|
||||||
|
|
||||||
|
A simple, free app to **keep your seeds and share them** with whoever you like. It works
|
||||||
|
**without internet** and with no company in the middle.
|
||||||
|
|
||||||
|
## Why it's needed
|
||||||
|
|
||||||
|
Today a handful of companies control much of the world's seed. Many seeds are made so they
|
||||||
|
won't serve again: they give one harvest, but their seeds don't germinate well, so you have
|
||||||
|
to **buy them again every year**. Meanwhile the traditional varieties — grown and improved
|
||||||
|
over thousands of years — are being lost, and with them the knowledge of how to grow them.
|
||||||
|
|
||||||
|
Sharing seeds is one of the oldest and most natural things there is, yet in some places it
|
||||||
|
has become complicated or even fined (in France an association was penalised for handing out
|
||||||
|
varieties that weren't on the official approved list). And the people who care for seeds
|
||||||
|
still do it with notebooks and spreadsheets.
|
||||||
|
|
||||||
|
## What it does, made easy
|
||||||
|
|
||||||
|
- **Keep** — your seed inventory: what you have, from which year, how much, where it came
|
||||||
|
from, under **the name you use** (no Latin required). The same whether you have four
|
||||||
|
packets in a drawer or a group with hundreds of varieties.
|
||||||
|
- **Share** — you say what you offer; someone nearby sees it and writes to you, to **give
|
||||||
|
away, swap or sell** if you want. You close the deal yourself, directly. No company steps
|
||||||
|
in or takes a commission.
|
||||||
|
|
||||||
|
## Why it's different
|
||||||
|
|
||||||
|
- **It's free and belongs to everyone.** It isn't owned by any company: anyone can use,
|
||||||
|
copy, translate or improve it, free and forever. It's more a *recipe that gets shared*
|
||||||
|
than a closed product — this is called **free software**. And whoever improves it is
|
||||||
|
bound to share those improvements just as freely, so no one can "close" it later (that's
|
||||||
|
**copyleft**).
|
||||||
|
- **It works offline.** The basics run even out in the field with no signal.
|
||||||
|
- **Your data is yours.** Your information is kept on **your own phone**, protected with a
|
||||||
|
key, not on some company's computers. We don't sell it or upload it anywhere. No ads.
|
||||||
|
- **No one can shut it down or control it.** There's no central computer that everything
|
||||||
|
passes through: people communicate **directly with each other**, like passing seeds from
|
||||||
|
hand to hand. So no one can censor it or charge you to use it (that's what being
|
||||||
|
**decentralized** means).
|
||||||
|
- **You trust by word of mouth.** You deal with people you know and with those vouched for
|
||||||
|
by people you trust — the way it's always worked in a village or at a fair — without
|
||||||
|
having to give your real name or phone number unless you want to.
|
||||||
|
- **In many languages.** For anywhere in the world, not for a single country. Volunteers
|
||||||
|
translate it.
|
||||||
|
|
||||||
|
## Who it's for
|
||||||
|
|
||||||
|
Seed groups and networks, gardens and collectives, growers… and anyone with a few packets
|
||||||
|
put away. For all ages, from 10 to 80.
|
||||||
|
|
||||||
|
## The name
|
||||||
|
|
||||||
|
**Tane** means "seed" (種). It comes from *tanemaki* (種まき), "to sow seeds" in Japanese, a
|
||||||
|
nod to old customs of mutual aid between neighbours (sharing the work, saving together).
|
||||||
|
|
||||||
|
## Where it stands and how to help
|
||||||
|
|
||||||
|
In its early days. You can help by trying it out in a real seed group, translating it,
|
||||||
|
sharing what you know about the varieties, or coding. It has no price and never will: it's
|
||||||
|
sustained by volunteers, by the communities themselves, and by public funding for projects
|
||||||
|
of common interest.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> *The idea underneath: traditional seeds are a heritage of humankind — everyone's and no
|
||||||
|
> one's. Against those who would privatise them, Tane aims to make it easy for you to keep
|
||||||
|
> them, multiply them and keep them moving.*
|
||||||
81
site/content/about.es.md
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
---
|
||||||
|
title: "Qué es Tane"
|
||||||
|
slug: "about"
|
||||||
|
translationKey: "about"
|
||||||
|
description: "Tane explicada para todos los públicos."
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Tane** (種) es «semilla» en japonés; el nombre completo, *tanemaki* (種まき), es «sembrar / esparcir semillas».
|
||||||
|
|
||||||
|
## En una frase
|
||||||
|
|
||||||
|
Una app sencilla y gratuita para **guardar tus semillas y compartirlas** con quien
|
||||||
|
quieras. Funciona **sin internet** y sin ninguna empresa de por medio.
|
||||||
|
|
||||||
|
## Por qué hace falta
|
||||||
|
|
||||||
|
Hoy unas pocas empresas controlan gran parte de las semillas del mundo. Muchas están
|
||||||
|
hechas para no volver a servir: dan una cosecha, pero sus semillas no germinan bien, así
|
||||||
|
que hay que **comprarlas otra vez cada año**. Mientras tanto, las variedades de toda la
|
||||||
|
vida —cultivadas y mejoradas durante miles de años— se están perdiendo, y con ellas el
|
||||||
|
saber cómo cultivarlas.
|
||||||
|
|
||||||
|
Compartir semillas es de lo más viejo y natural que hay, pero en algunos sitios se ha
|
||||||
|
vuelto complicado o hasta se multa (en Francia sancionaron a una asociación por repartir
|
||||||
|
variedades que no estaban en la lista oficial permitida). Y quien las cuida lo hace hoy
|
||||||
|
con libretas y hojas de cálculo.
|
||||||
|
|
||||||
|
## Qué hace, y fácil
|
||||||
|
|
||||||
|
- **Guardar** — tu inventario de semillas: qué tienes, de qué año, cuánta, de dónde vino,
|
||||||
|
con **el nombre que tú usas** (no hace falta saber latín). Igual para quien tiene cuatro
|
||||||
|
sobres en un cajón que para un grupo con cientos de variedades.
|
||||||
|
- **Compartir** — dices qué ofreces; alguien cerca lo ve y te escribe, para **regalar,
|
||||||
|
intercambiar o vender** si quieres. El trato lo cierras tú, directamente. Ninguna empresa
|
||||||
|
se mete ni se lleva comisión.
|
||||||
|
|
||||||
|
## Por qué es distinta
|
||||||
|
|
||||||
|
- **Es libre y de todos.** No es de ninguna empresa: cualquiera puede usarla, copiarla,
|
||||||
|
traducirla o mejorarla, gratis y para siempre. Es más una *receta que se comparte* que un
|
||||||
|
producto cerrado — a esto se le llama **software libre**. Y quien la mejore está obligado
|
||||||
|
a compartir esas mejoras igual de libres, para que nadie pueda «cerrarla» después (eso es
|
||||||
|
el **copyleft**).
|
||||||
|
- **Funciona sin internet.** Lo básico va aunque estés en el campo sin cobertura.
|
||||||
|
- **Tus datos son tuyos.** Tu información se guarda en **tu propio móvil**, protegida con una
|
||||||
|
clave, no en los ordenadores de una empresa. No la vendemos ni la subimos a ningún sitio.
|
||||||
|
Sin publicidad.
|
||||||
|
- **Nadie la puede apagar ni controlar.** No hay un ordenador central por el que pase todo:
|
||||||
|
las personas se comunican **directamente entre sí**, como quien se pasa unas semillas de
|
||||||
|
mano en mano. Por eso nadie puede censurarla ni cobrarte por usarla (eso es que sea
|
||||||
|
**descentralizada**).
|
||||||
|
- **Te fías por el boca a boca.** Te relacionas con gente que conoces y con quien te
|
||||||
|
recomienda gente de confianza —como siempre se ha hecho en un pueblo o en una feria— sin
|
||||||
|
tener que dar tu nombre real ni tu teléfono si no quieres.
|
||||||
|
- **En muchos idiomas.** Para cualquier lugar del mundo, no para un solo país. La traduce
|
||||||
|
gente voluntaria.
|
||||||
|
|
||||||
|
## Para quién
|
||||||
|
|
||||||
|
Grupos y redes de semillas, huertos y colectivos, agricultores y agricultoras… y cualquier
|
||||||
|
persona con unos sobres guardados. Para todas las edades, de 10 a 80.
|
||||||
|
|
||||||
|
## El nombre
|
||||||
|
|
||||||
|
**Tane** quiere decir «semilla» (種). Viene de *tanemaki* (種まき), «sembrar semillas» en
|
||||||
|
japonés, un guiño a viejas costumbres de ayuda mutua entre vecinos (compartir el trabajo,
|
||||||
|
ahorrar juntos).
|
||||||
|
|
||||||
|
## En qué punto está y cómo ayudar
|
||||||
|
|
||||||
|
En sus primeros pasos. Se puede ayudar probándola en un grupo de semillas de verdad,
|
||||||
|
traduciéndola, aportando lo que sabes de las variedades, o programando. No tiene ni tendrá
|
||||||
|
precio: se sostiene con gente voluntaria, con las propias comunidades y con ayudas públicas
|
||||||
|
a proyectos de interés común.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> *Idea de fondo: las semillas de toda la vida son un patrimonio de la humanidad —de todos
|
||||||
|
> y de nadie—. Frente a quien quiere privatizarlas, Tane busca ponértelo fácil para
|
||||||
|
> guardarlas, multiplicarlas y hacerlas circular.*
|
||||||
4
site/content/legal/_index.en.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
title: "Legal"
|
||||||
|
description: "Tane's privacy policy, terms of use, community rules and seed-legality notice."
|
||||||
|
---
|
||||||
4
site/content/legal/_index.es.md
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
title: "Legal"
|
||||||
|
description: "Política de privacidad, condiciones de uso, normas de la comunidad y aviso sobre semillas de Tane."
|
||||||
|
---
|
||||||
96
site/content/legal/privacy.en.md
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
---
|
||||||
|
title: "Privacy Policy"
|
||||||
|
slug: "privacy"
|
||||||
|
weight: 1
|
||||||
|
translationKey: "legal-privacy"
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Version 1.0 — 13 July 2026**
|
||||||
|
|
||||||
|
Tane is published by **Asociación Comunes** (Spain) — <https://comunes.org> —
|
||||||
|
contact: <info@comunes.org>. Tane is free software (AGPL-3.0); its source code is
|
||||||
|
public, so everything described here can be verified.
|
||||||
|
|
||||||
|
## The short version
|
||||||
|
|
||||||
|
- Tane works **without an account**. We don't know who you are.
|
||||||
|
- Everything you record stays **on your device, encrypted**. We run no servers that
|
||||||
|
receive it, and we have no way to read it.
|
||||||
|
- Nothing leaves your device unless **you** choose to share it (an offer, a message,
|
||||||
|
your profile). What you share travels through community-run servers we don't control.
|
||||||
|
- **No analytics, no ads, no trackers.** Tane collects nothing about you.
|
||||||
|
|
||||||
|
## 1. No accounts, no collection by us
|
||||||
|
|
||||||
|
Tane does not ask for your name, email, or phone number. There is no registration and
|
||||||
|
no central Tane server. Your identity in the app is a cryptographic key created on your
|
||||||
|
device and stored in your device's secure keystore. Asociación Comunes does not collect,
|
||||||
|
receive, or process your personal data through the app.
|
||||||
|
|
||||||
|
## 2. Data stored on your device
|
||||||
|
|
||||||
|
Your seed inventory (varieties, quantities, notes, photos), your messages, your
|
||||||
|
contacts' vouches and ratings, and your keys are stored **only on your device**, in an
|
||||||
|
encrypted database (SQLCipher). The encryption key lives in your device's system
|
||||||
|
keystore. Backups you create are also encrypted; you choose where to keep them, and
|
||||||
|
they never pass through us.
|
||||||
|
|
||||||
|
## 3. Data that leaves your device — only when you choose
|
||||||
|
|
||||||
|
Nothing is published automatically. Each of these happens only by your explicit action:
|
||||||
|
|
||||||
|
- **Offers.** When you offer seeds, the app publishes the offer's title, description,
|
||||||
|
optional photo, optional price, and an **approximate area** (a zone of roughly 2–80 km,
|
||||||
|
as you configure it — never your address or precise location). Offers are public.
|
||||||
|
- **Profile.** If you fill it in, your chosen name, bio, and avatar are public.
|
||||||
|
- **Messages.** Direct messages are **end-to-end encrypted**: only you and the person
|
||||||
|
you write to can read them. The servers that carry them see only encrypted envelopes.
|
||||||
|
- **Vouches and ratings.** If you vouch for or rate someone, that statement is public
|
||||||
|
and signed by your key.
|
||||||
|
- **Sync between your devices.** If you link a second device, your data travels between
|
||||||
|
them encrypted so that only your devices can read it.
|
||||||
|
|
||||||
|
## 4. Where shared data goes: community relays
|
||||||
|
|
||||||
|
Tane's social features use open, community-run servers ("relays"). By default the app
|
||||||
|
uses a small set that includes `relay.comunes.org` (operated by Asociación Comunes) and
|
||||||
|
well-known public relays; you can change this list, or empty it to turn the network off
|
||||||
|
entirely. Relays other than `relay.comunes.org` are **third-party infrastructure**:
|
||||||
|
their operators decide their own retention and policies, and this policy does not cover
|
||||||
|
them.
|
||||||
|
|
||||||
|
## 5. Deletion — an honest note
|
||||||
|
|
||||||
|
You can delete anything local instantly, and you can withdraw an offer at any time.
|
||||||
|
When you withdraw or delete something you had published, the app asks the relays to
|
||||||
|
delete it too. Relays we don't operate may keep copies despite that request, and public
|
||||||
|
posts may have been copied elsewhere while they were visible. **Treat anything you
|
||||||
|
publish as potentially permanent.** On `relay.comunes.org` we honour deletion requests.
|
||||||
|
|
||||||
|
## 6. Device permissions
|
||||||
|
|
||||||
|
- **Photos / camera** — only when you attach a picture to a variety, offer, or profile.
|
||||||
|
- **Approximate location** (optional) — only if you use "set my area from where I am",
|
||||||
|
to pick your coarse sharing zone. Tane never requests precise location.
|
||||||
|
- **Notifications** (optional) — to tell you about new messages.
|
||||||
|
|
||||||
|
## 7. Your rights
|
||||||
|
|
||||||
|
Because your data lives on your device under your control, you exercise most rights
|
||||||
|
yourself: read, correct, export, or delete everything from within the app. For data on
|
||||||
|
`relay.comunes.org`, or any question about this policy, write to <info@comunes.org>.
|
||||||
|
If you are in the EU, you also have the right to complain to your data protection
|
||||||
|
authority (in Spain, the AEPD).
|
||||||
|
|
||||||
|
## 8. Children
|
||||||
|
|
||||||
|
The seed inventory can be used by anyone. The market and messaging features are not
|
||||||
|
directed at children; by using them you confirm you are old enough to use social
|
||||||
|
features under the laws of your country.
|
||||||
|
|
||||||
|
## 9. Changes
|
||||||
|
|
||||||
|
We will update this policy if Tane's behaviour changes, and note the changes in the
|
||||||
|
app's release notes. The current version always lives at
|
||||||
|
<https://tane.comunes.org/legal/privacy>, with history in the public repository.
|
||||||
102
site/content/legal/privacy.es.md
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
---
|
||||||
|
title: "Política de privacidad"
|
||||||
|
slug: "privacy"
|
||||||
|
weight: 1
|
||||||
|
translationKey: "legal-privacy"
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Versión 1.0 — 13 de julio de 2026**
|
||||||
|
|
||||||
|
Tane está publicada por la **Asociación Comunes** (España) — <https://comunes.org> —
|
||||||
|
contacto: <info@comunes.org>. Tane es software libre (AGPL-3.0); su código fuente es
|
||||||
|
público, así que todo lo que se describe aquí puede verificarse.
|
||||||
|
|
||||||
|
## La versión corta
|
||||||
|
|
||||||
|
- Tane funciona **sin cuenta**. No sabemos quién eres.
|
||||||
|
- Todo lo que registras se queda **en tu dispositivo, cifrado**. No tenemos servidores
|
||||||
|
que lo reciban ni forma de leerlo.
|
||||||
|
- Nada sale de tu dispositivo salvo que **tú** decidas compartirlo (una oferta, un
|
||||||
|
mensaje, tu perfil). Lo que compartes viaja por servidores comunitarios que no
|
||||||
|
controlamos.
|
||||||
|
- **Sin analíticas, sin publicidad, sin rastreadores.** Tane no recoge nada sobre ti.
|
||||||
|
|
||||||
|
## 1. Sin cuentas, sin recogida por nuestra parte
|
||||||
|
|
||||||
|
Tane no pide tu nombre, correo ni teléfono. No hay registro ni servidor central de
|
||||||
|
Tane. Tu identidad en la aplicación es una clave criptográfica creada en tu dispositivo
|
||||||
|
y guardada en el almacén seguro del sistema. La Asociación Comunes no recoge, recibe ni
|
||||||
|
trata tus datos personales a través de la aplicación.
|
||||||
|
|
||||||
|
## 2. Datos guardados en tu dispositivo
|
||||||
|
|
||||||
|
Tu inventario de semillas (variedades, cantidades, notas, fotos), tus mensajes, los
|
||||||
|
avales y valoraciones de tus contactos y tus claves se guardan **solo en tu
|
||||||
|
dispositivo**, en una base de datos cifrada (SQLCipher). La clave de cifrado vive en el
|
||||||
|
almacén seguro del sistema. Las copias de seguridad que crees también van cifradas; tú
|
||||||
|
eliges dónde guardarlas y nunca pasan por nosotros.
|
||||||
|
|
||||||
|
## 3. Datos que salen de tu dispositivo — solo cuando tú quieres
|
||||||
|
|
||||||
|
Nada se publica automáticamente. Cada una de estas cosas ocurre solo por tu acción
|
||||||
|
explícita:
|
||||||
|
|
||||||
|
- **Ofertas.** Cuando ofreces semillas, la aplicación publica el título, la
|
||||||
|
descripción, una foto opcional, un precio opcional y una **zona aproximada** (un área
|
||||||
|
de entre 2 y 80 km, según la configures — nunca tu dirección ni tu ubicación
|
||||||
|
precisa). Las ofertas son públicas.
|
||||||
|
- **Perfil.** Si lo rellenas, el nombre que elijas, tu biografía y tu avatar son
|
||||||
|
públicos.
|
||||||
|
- **Mensajes.** Los mensajes directos van **cifrados de extremo a extremo**: solo tú y
|
||||||
|
la persona a la que escribes podéis leerlos. Los servidores que los transportan solo
|
||||||
|
ven sobres cifrados.
|
||||||
|
- **Avales y valoraciones.** Si avalas o valoras a alguien, esa declaración es pública
|
||||||
|
y va firmada con tu clave.
|
||||||
|
- **Sincronización entre tus dispositivos.** Si enlazas un segundo dispositivo, tus
|
||||||
|
datos viajan entre ellos cifrados de forma que solo tus dispositivos pueden leerlos.
|
||||||
|
|
||||||
|
## 4. A dónde van los datos compartidos: servidores comunitarios
|
||||||
|
|
||||||
|
Las funciones sociales de Tane usan servidores abiertos gestionados por comunidades
|
||||||
|
("relés"). Por defecto la aplicación usa un pequeño conjunto que incluye
|
||||||
|
`relay.comunes.org` (operado por la Asociación Comunes) y relés públicos conocidos;
|
||||||
|
puedes cambiar esa lista, o vaciarla para desconectar la red por completo. Los relés
|
||||||
|
distintos de `relay.comunes.org` son **infraestructura de terceros**: sus operadores
|
||||||
|
deciden su propia retención y sus políticas, y esta política no los cubre.
|
||||||
|
|
||||||
|
## 5. Borrado — una nota honesta
|
||||||
|
|
||||||
|
Puedes borrar al instante todo lo local, y puedes retirar una oferta en cualquier
|
||||||
|
momento. Cuando retiras o borras algo que habías publicado, la aplicación pide a los
|
||||||
|
relés que también lo borren. Los relés que no operamos pueden conservar copias pese a
|
||||||
|
esa petición, y lo publicado pudo copiarse a otros sitios mientras fue visible.
|
||||||
|
**Trata todo lo que publiques como potencialmente permanente.** En `relay.comunes.org`
|
||||||
|
atendemos las peticiones de borrado.
|
||||||
|
|
||||||
|
## 6. Permisos del dispositivo
|
||||||
|
|
||||||
|
- **Fotos / cámara** — solo cuando adjuntas una imagen a una variedad, oferta o perfil.
|
||||||
|
- **Ubicación aproximada** (opcional) — solo si usas "fijar mi zona desde donde estoy",
|
||||||
|
para elegir tu zona amplia de intercambio. Tane nunca pide la ubicación precisa.
|
||||||
|
- **Notificaciones** (opcional) — para avisarte de mensajes nuevos.
|
||||||
|
|
||||||
|
## 7. Tus derechos
|
||||||
|
|
||||||
|
Como tus datos viven en tu dispositivo y bajo tu control, la mayoría de derechos los
|
||||||
|
ejerces tú directamente: consultar, corregir, exportar o borrar todo desde la propia
|
||||||
|
aplicación. Para los datos en `relay.comunes.org`, o cualquier duda sobre esta
|
||||||
|
política, escribe a <info@comunes.org>. Si estás en la UE, también tienes derecho a
|
||||||
|
reclamar ante tu autoridad de protección de datos (en España, la AEPD).
|
||||||
|
|
||||||
|
## 8. Menores
|
||||||
|
|
||||||
|
El inventario de semillas puede usarlo cualquiera. El mercado y la mensajería no están
|
||||||
|
dirigidos a menores; al usarlos confirmas que tienes edad suficiente para usar
|
||||||
|
funciones sociales según las leyes de tu país.
|
||||||
|
|
||||||
|
## 9. Cambios
|
||||||
|
|
||||||
|
Actualizaremos esta política si cambia el comportamiento de Tane, y lo señalaremos en
|
||||||
|
las notas de cada versión. La versión vigente está siempre en
|
||||||
|
<https://tane.comunes.org/legal/privacy>, con su historial en el repositorio público.
|
||||||
47
site/content/legal/rules.en.md
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
---
|
||||||
|
title: "Community Rules"
|
||||||
|
slug: "rules"
|
||||||
|
weight: 3
|
||||||
|
translationKey: "legal-rules"
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Version 1.0 — 13 July 2026**
|
||||||
|
|
||||||
|
The market and messages in Tane are shared spaces built on trust between neighbours.
|
||||||
|
These rules are what everyone accepts before joining. They also define what counts as
|
||||||
|
content that can be reported and removed from the servers we operate.
|
||||||
|
|
||||||
|
## The rules
|
||||||
|
|
||||||
|
1. **Be honest about your seeds.** Describe what you actually have: the variety as you
|
||||||
|
know it, the harvest year, anything relevant about how it was grown. Don't invent
|
||||||
|
provenance.
|
||||||
|
2. **Share only what you may share.** Don't offer:
|
||||||
|
- seed of **commercially protected varieties** (plant variety rights, patents)
|
||||||
|
unless you are authorized;
|
||||||
|
- **invasive or protected species** where their exchange is prohibited;
|
||||||
|
- anything else that is illegal to share or sell where you or the recipient live.
|
||||||
|
3. **Seeds, not anything else.** The market is for seeds and plant reproductive
|
||||||
|
material in the spirit of the app. It is not a general classifieds board.
|
||||||
|
4. **Respect people.** No harassment, threats, hate, or discrimination — in offers,
|
||||||
|
profiles, ratings, or messages.
|
||||||
|
5. **No spam or scams.** No repeated postings, misleading offers, phishing, or
|
||||||
|
pressure to move people onto shady deals.
|
||||||
|
6. **Honest reputation.** Vouch only for people you have actually met or exchanged
|
||||||
|
with; rate only real experiences. Don't game the trust system.
|
||||||
|
|
||||||
|
## What happens if someone breaks them
|
||||||
|
|
||||||
|
- Anyone can **block** you — your offers and messages disappear for them.
|
||||||
|
- Anyone can **report** an offer or a person. Reports travel to the servers that carry
|
||||||
|
the content; on the relay operated by Comunes (`relay.comunes.org`), content and keys
|
||||||
|
that break these rules are removed. Other servers apply their own policies.
|
||||||
|
- There are no appeals or account suspensions because there are no accounts: the
|
||||||
|
network simply stops carrying and showing what breaks the rules.
|
||||||
|
|
||||||
|
## A note on good faith
|
||||||
|
|
||||||
|
Most seed sharing is between amateurs, in small quantities, for the joy of keeping
|
||||||
|
varieties alive. When in doubt, prefer **gifting and swapping** over selling, label
|
||||||
|
things clearly, and ask. That's how it's always been done.
|
||||||
54
site/content/legal/rules.es.md
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
---
|
||||||
|
title: "Normas de la comunidad"
|
||||||
|
slug: "rules"
|
||||||
|
weight: 3
|
||||||
|
translationKey: "legal-rules"
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Versión 1.0 — 13 de julio de 2026**
|
||||||
|
|
||||||
|
El mercado y los mensajes de Tane son espacios compartidos construidos sobre la
|
||||||
|
confianza entre vecinas y vecinos. Estas normas son lo que todo el mundo acepta antes
|
||||||
|
de entrar. También definen qué contenido puede denunciarse y eliminarse de los
|
||||||
|
servidores que operamos.
|
||||||
|
|
||||||
|
## Las normas
|
||||||
|
|
||||||
|
1. **Sé honesto con tus semillas.** Describe lo que realmente tienes: la variedad tal
|
||||||
|
y como la conoces, el año de cosecha, lo relevante sobre cómo se cultivó. No
|
||||||
|
inventes procedencias.
|
||||||
|
2. **Comparte solo lo que puedas compartir.** No ofrezcas:
|
||||||
|
- semilla de **variedades protegidas comercialmente** (obtenciones vegetales,
|
||||||
|
patentes) salvo que tengas autorización;
|
||||||
|
- **especies invasoras o protegidas** allí donde su intercambio esté prohibido;
|
||||||
|
- cualquier otra cosa que sea ilegal compartir o vender donde vives tú o donde vive
|
||||||
|
quien la recibe.
|
||||||
|
3. **Semillas, no cualquier cosa.** El mercado es para semillas y material de
|
||||||
|
reproducción vegetal, en el espíritu de la aplicación. No es un tablón de anuncios
|
||||||
|
general.
|
||||||
|
4. **Respeta a las personas.** Nada de acoso, amenazas, odio o discriminación — ni en
|
||||||
|
ofertas, ni en perfiles, ni en valoraciones, ni en mensajes.
|
||||||
|
5. **Sin spam ni engaños.** Nada de publicaciones repetidas, ofertas engañosas,
|
||||||
|
phishing o presiones para llevar a la gente a tratos turbios.
|
||||||
|
6. **Reputación honesta.** Avala solo a personas que conozcas de verdad o con las que
|
||||||
|
hayas intercambiado; valora solo experiencias reales. No hagas trampas al sistema de
|
||||||
|
confianza.
|
||||||
|
|
||||||
|
## Qué pasa si alguien las incumple
|
||||||
|
|
||||||
|
- Cualquiera puede **bloquearte** — tus ofertas y mensajes desaparecen para esa
|
||||||
|
persona.
|
||||||
|
- Cualquiera puede **denunciar** una oferta o a una persona. Las denuncias llegan a los
|
||||||
|
servidores que transportan el contenido; en el relé operado por Comunes
|
||||||
|
(`relay.comunes.org`), el contenido y las claves que incumplen estas normas se
|
||||||
|
eliminan. Otros servidores aplican sus propias políticas.
|
||||||
|
- No hay apelaciones ni suspensiones de cuenta porque no hay cuentas: sencillamente, la
|
||||||
|
red deja de transportar y de mostrar lo que incumple las normas.
|
||||||
|
|
||||||
|
## Una nota sobre la buena fe
|
||||||
|
|
||||||
|
La mayor parte del intercambio de semillas ocurre entre aficionados, en pequeñas
|
||||||
|
cantidades, por el gusto de mantener vivas las variedades. Ante la duda, prefiere
|
||||||
|
**regalar e intercambiar** antes que vender, etiqueta las cosas con claridad y
|
||||||
|
pregunta. Así se ha hecho siempre.
|
||||||
66
site/content/legal/seeds.en.md
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
---
|
||||||
|
title: "Notice on the Legality of Exchanging Seeds"
|
||||||
|
slug: "seeds"
|
||||||
|
weight: 4
|
||||||
|
translationKey: "legal-seeds"
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Version 1.0 — 13 July 2026**
|
||||||
|
|
||||||
|
Tane helps people keep and share traditional seeds. Laws about seeds differ a lot
|
||||||
|
between countries, and they distinguish sharply between **giving or swapping** seeds
|
||||||
|
and **selling** them. This notice is general information, not legal advice; you are
|
||||||
|
responsible for following the law where you live and where your seeds are going.
|
||||||
|
|
||||||
|
## Gifting and swapping between amateurs
|
||||||
|
|
||||||
|
Exchanging seeds **without commercial purpose** — gifts, swaps, seed libraries,
|
||||||
|
conservation networks — is broadly recognized and lawful in most places:
|
||||||
|
|
||||||
|
- **European Union.** EU seed marketing law regulates *commercial marketing* of seed.
|
||||||
|
Exchange in kind between individuals, and the work of conservation networks and gene
|
||||||
|
banks, fall outside or are expressly exempted; the plant-reproductive-material reform
|
||||||
|
underway (proposal COM(2023) 414) keeps exemptions for amateurs and conservation
|
||||||
|
networks, provided the activity is not commercial.
|
||||||
|
- **Spain.** Ley 30/2006 regulates the *commercial* seed trade; non-commercial exchange
|
||||||
|
between amateurs and conservation-oriented exchange are recognized practice (art. 24).
|
||||||
|
- **United States.** Following the 2016 amendment to the Recommended Uniform State Seed
|
||||||
|
Law, most states exempt **non-commercial seed sharing** (seed libraries, swaps) from
|
||||||
|
commercial labeling and testing requirements.
|
||||||
|
|
||||||
|
## Selling seeds
|
||||||
|
|
||||||
|
Selling is different. In the EU and many other jurisdictions, **marketing seed of
|
||||||
|
varieties not registered in an official catalogue may be restricted or prohibited**,
|
||||||
|
and quantities, labeling, or species may be regulated even for small sellers.
|
||||||
|
Exemptions for conservation varieties and amateur quantities exist but vary. If you
|
||||||
|
use Tane's option to ask a price, **check your national rules first** — the app cannot
|
||||||
|
do this for you.
|
||||||
|
|
||||||
|
## Protected varieties
|
||||||
|
|
||||||
|
Varieties covered by **plant variety rights (PVR/PBR) or patents** may not be
|
||||||
|
propagated or sold without the holder's authorization — this can apply even to seed
|
||||||
|
you bought legally. Traditional and heirloom varieties are generally not protected,
|
||||||
|
but modern commercial varieties usually are. When in doubt, don't offer it.
|
||||||
|
|
||||||
|
## Sending seeds across borders
|
||||||
|
|
||||||
|
Most countries restrict importing seeds: **phytosanitary certificates**, prohibited
|
||||||
|
species lists, and quarantine rules commonly apply — even to small envelopes between
|
||||||
|
individuals, and also **within** the EU for certain species (plant passport rules).
|
||||||
|
Before mailing seeds to another country, check the rules of the destination country.
|
||||||
|
The safest exchanges are local ones — which is what Tane is designed for.
|
||||||
|
|
||||||
|
## Invasive and protected species
|
||||||
|
|
||||||
|
Some species may not be exchanged at all in some regions, either because they are
|
||||||
|
**invasive** there or because they are **protected**. Lists are regional; check yours.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Tane takes no commission, mediates no transaction, and verifies no offer: exchanges
|
||||||
|
are private dealings between the people involved (see the
|
||||||
|
[Terms of use](terms-of-use.md)). This notice exists so those dealings are informed
|
||||||
|
ones.*
|
||||||
72
site/content/legal/seeds.es.md
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
---
|
||||||
|
title: "Aviso sobre la legalidad del intercambio de semillas"
|
||||||
|
slug: "seeds"
|
||||||
|
weight: 4
|
||||||
|
translationKey: "legal-seeds"
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Versión 1.0 — 13 de julio de 2026**
|
||||||
|
|
||||||
|
Tane ayuda a conservar y compartir semillas tradicionales. Las leyes sobre semillas
|
||||||
|
varían mucho entre países, y distinguen con claridad entre **regalar o intercambiar**
|
||||||
|
semillas y **venderlas**. Este aviso es información general, no asesoramiento jurídico;
|
||||||
|
eres responsable de cumplir la ley allí donde vives y allí donde van tus semillas.
|
||||||
|
|
||||||
|
## Regalar e intercambiar entre aficionados
|
||||||
|
|
||||||
|
Intercambiar semillas **sin ánimo comercial** — regalos, trueques, bibliotecas de
|
||||||
|
semillas, redes de conservación — está ampliamente reconocido y es legal en la mayoría
|
||||||
|
de lugares:
|
||||||
|
|
||||||
|
- **Unión Europea.** La normativa de semillas de la UE regula la *comercialización* de
|
||||||
|
semilla. El intercambio en especie entre particulares y la labor de las redes de
|
||||||
|
conservación y bancos de germoplasma quedan fuera o están expresamente exentos; la
|
||||||
|
reforma en curso del material de reproducción vegetal (propuesta COM(2023) 414)
|
||||||
|
mantiene exenciones para aficionados y redes de conservación, siempre que la
|
||||||
|
actividad no sea comercial.
|
||||||
|
- **España.** La Ley 30/2006 regula el comercio de semillas; el intercambio no
|
||||||
|
comercial entre aficionados y el intercambio con fines de conservación son práctica
|
||||||
|
reconocida (art. 24).
|
||||||
|
- **Estados Unidos.** Tras la enmienda de 2016 a la ley uniforme estatal de semillas
|
||||||
|
(RUSSL), la mayoría de estados eximen el **intercambio no comercial** (bibliotecas de
|
||||||
|
semillas, trueques) de los requisitos comerciales de etiquetado y análisis.
|
||||||
|
|
||||||
|
## Vender semillas
|
||||||
|
|
||||||
|
Vender es distinto. En la UE y en muchas otras jurisdicciones, **comercializar semilla
|
||||||
|
de variedades no inscritas en un catálogo oficial puede estar restringido o
|
||||||
|
prohibido**, y las cantidades, el etiquetado o las especies pueden estar regulados
|
||||||
|
incluso para vendedores pequeños. Existen exenciones para variedades de conservación y
|
||||||
|
cantidades de aficionado, pero varían. Si usas la opción de Tane de pedir un precio,
|
||||||
|
**consulta antes tu normativa nacional** — la aplicación no puede hacerlo por ti.
|
||||||
|
|
||||||
|
## Variedades protegidas
|
||||||
|
|
||||||
|
Las variedades cubiertas por **títulos de obtención vegetal (PVR/PBR) o patentes** no
|
||||||
|
pueden propagarse ni venderse sin autorización del titular — y esto puede aplicarse
|
||||||
|
incluso a semilla comprada legalmente. Las variedades tradicionales y de herencia
|
||||||
|
normalmente no están protegidas; las variedades comerciales modernas casi siempre sí.
|
||||||
|
Ante la duda, no la ofrezcas.
|
||||||
|
|
||||||
|
## Enviar semillas entre países
|
||||||
|
|
||||||
|
La mayoría de países restringen la importación de semillas: **certificados
|
||||||
|
fitosanitarios**, listas de especies prohibidas y normas de cuarentena se aplican con
|
||||||
|
frecuencia — incluso a sobres pequeños entre particulares, y también **dentro** de la
|
||||||
|
UE para ciertas especies (pasaporte fitosanitario). Antes de enviar semillas a otro
|
||||||
|
país, consulta las normas del país de destino. Los intercambios más seguros son los
|
||||||
|
locales — que es justo para lo que está pensada Tane.
|
||||||
|
|
||||||
|
## Especies invasoras y protegidas
|
||||||
|
|
||||||
|
Algunas especies no pueden intercambiarse en absoluto en ciertas regiones, bien porque
|
||||||
|
allí son **invasoras**, bien porque están **protegidas**. Las listas son regionales;
|
||||||
|
consulta la tuya.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Tane no cobra comisiones, no media en transacciones y no verifica ofertas: los
|
||||||
|
intercambios son tratos privados entre las personas implicadas (ver las
|
||||||
|
[Condiciones de uso](condiciones-de-uso.md)). Este aviso existe para que esos tratos
|
||||||
|
sean informados.*
|
||||||
81
site/content/legal/terms.en.md
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
---
|
||||||
|
title: "Terms of Use"
|
||||||
|
slug: "terms"
|
||||||
|
weight: 2
|
||||||
|
translationKey: "legal-terms"
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Version 1.0 — 13 July 2026**
|
||||||
|
|
||||||
|
These terms cover the use of the Tane application, published by **Asociación Comunes**
|
||||||
|
(Spain) — <https://comunes.org>, contact <info@comunes.org>. By using Tane's sharing
|
||||||
|
features (the market and messaging) you accept these terms and the
|
||||||
|
[Community rules](community-rules.md).
|
||||||
|
|
||||||
|
## 1. What Tane is — and what it is not
|
||||||
|
|
||||||
|
Tane is a **personal tool**: a seed inventory that lives on your device, plus a way to
|
||||||
|
discover people near you who share seeds. Tane is **not a marketplace operator, not a
|
||||||
|
shop, and not a party to any exchange**:
|
||||||
|
|
||||||
|
- We host no listings — offers are published by you, under your own key, to
|
||||||
|
community-run relays.
|
||||||
|
- We take **no commission** and process no payments. Any exchange, gift, or sale is
|
||||||
|
agreed and carried out directly between the people involved.
|
||||||
|
- We do not verify seeds, users, or offers, and we cannot remove content from servers
|
||||||
|
we do not operate.
|
||||||
|
|
||||||
|
## 2. Your responsibilities
|
||||||
|
|
||||||
|
You are solely responsible for:
|
||||||
|
|
||||||
|
- **What you offer and exchange.** Make sure you are allowed to share or sell the
|
||||||
|
seeds you list. Rules differ by country — see the
|
||||||
|
[Seed legality notice](seed-legality-notice.md). In particular, selling seed of
|
||||||
|
varieties that are not registered may be restricted where you live, and propagating
|
||||||
|
commercially protected varieties without authorization is illegal in most countries.
|
||||||
|
- **Shipping.** Sending seeds across borders is often restricted (phytosanitary rules).
|
||||||
|
Check before you mail anything abroad.
|
||||||
|
- **What you publish.** Offers, your profile, vouches, and ratings are public and
|
||||||
|
signed by you. Follow the [Community rules](community-rules.md).
|
||||||
|
- **Your keys and backups.** There is no account recovery: if you lose your device and
|
||||||
|
your recovery code, we cannot restore anything.
|
||||||
|
|
||||||
|
## 3. No warranties
|
||||||
|
|
||||||
|
Tane is provided **"as is"**, without warranty of any kind, as free software under the
|
||||||
|
[AGPL-3.0 license](https://www.gnu.org/licenses/agpl-3.0.html). In particular, nobody —
|
||||||
|
not Comunes, not the people you exchange with through the app — guarantees the
|
||||||
|
**identity, purity, germination, health, or legality** of any seed exchanged. Growing
|
||||||
|
information shown in the app (calendars, seed-saving tips, viability estimates) is
|
||||||
|
general guidance, not professional advice.
|
||||||
|
|
||||||
|
## 4. Disputes between users
|
||||||
|
|
||||||
|
Exchanges are private dealings between the people involved. Comunes is not a mediator
|
||||||
|
and accepts no liability for failed exchanges, quality disputes, or losses arising from
|
||||||
|
them. The vouching and rating features exist so communities can build their own trust.
|
||||||
|
|
||||||
|
## 5. Moderation
|
||||||
|
|
||||||
|
There is no central moderation: your app filters what you see through the people you
|
||||||
|
trust, and you can **block** anyone and **report** offers or people from within the
|
||||||
|
app. Reports are shared with the servers involved; on the relay we operate
|
||||||
|
(`relay.comunes.org`), content that breaks the community rules is removed. Relays run
|
||||||
|
by others apply their own policies.
|
||||||
|
|
||||||
|
## 6. Liability
|
||||||
|
|
||||||
|
To the maximum extent permitted by law, Asociación Comunes' total liability arising
|
||||||
|
from the app is limited to the amount you paid for it (Tane is gratis: zero). Nothing
|
||||||
|
in these terms limits liability that cannot be limited by law, or your statutory rights
|
||||||
|
as a consumer.
|
||||||
|
|
||||||
|
## 7. General
|
||||||
|
|
||||||
|
If a part of these terms is found invalid, the rest remains in force. These terms are
|
||||||
|
governed by Spanish and EU law, without depriving you of protections of the law of
|
||||||
|
your country of residence. We may update these terms; material changes will be noted in
|
||||||
|
the app's release notes, and the current version always lives at
|
||||||
|
<https://tane.comunes.org/legal/terms>.
|
||||||
84
site/content/legal/terms.es.md
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
---
|
||||||
|
title: "Condiciones de uso"
|
||||||
|
slug: "terms"
|
||||||
|
weight: 2
|
||||||
|
translationKey: "legal-terms"
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Versión 1.0 — 13 de julio de 2026**
|
||||||
|
|
||||||
|
Estas condiciones regulan el uso de la aplicación Tane, publicada por la **Asociación
|
||||||
|
Comunes** (España) — <https://comunes.org>, contacto <info@comunes.org>. Al usar las
|
||||||
|
funciones de intercambio de Tane (el mercado y la mensajería) aceptas estas condiciones
|
||||||
|
y las [Normas de la comunidad](normas-de-la-comunidad.md).
|
||||||
|
|
||||||
|
## 1. Qué es Tane — y qué no es
|
||||||
|
|
||||||
|
Tane es una **herramienta personal**: un inventario de semillas que vive en tu
|
||||||
|
dispositivo, más una forma de descubrir personas cercanas que comparten semillas. Tane
|
||||||
|
**no es un operador de mercado, ni una tienda, ni parte de ningún intercambio**:
|
||||||
|
|
||||||
|
- No alojamos anuncios — las ofertas las publicas tú, con tu propia clave, en relés
|
||||||
|
gestionados por comunidades.
|
||||||
|
- No cobramos **ninguna comisión** ni procesamos pagos. Todo intercambio, regalo o
|
||||||
|
venta se acuerda y se realiza directamente entre las personas implicadas.
|
||||||
|
- No verificamos semillas, personas ni ofertas, y no podemos eliminar contenido de
|
||||||
|
servidores que no operamos.
|
||||||
|
|
||||||
|
## 2. Tus responsabilidades
|
||||||
|
|
||||||
|
Eres la única persona responsable de:
|
||||||
|
|
||||||
|
- **Lo que ofreces e intercambias.** Asegúrate de que puedes compartir o vender las
|
||||||
|
semillas que publicas. Las normas cambian según el país — mira el
|
||||||
|
[Aviso sobre semillas](aviso-sobre-semillas.md). En particular, vender semilla de
|
||||||
|
variedades no registradas puede estar restringido donde vives, y propagar variedades
|
||||||
|
protegidas comercialmente sin autorización es ilegal en la mayoría de países.
|
||||||
|
- **Los envíos.** Enviar semillas entre países suele estar restringido (normas
|
||||||
|
fitosanitarias). Infórmate antes de mandar nada al extranjero.
|
||||||
|
- **Lo que publicas.** Las ofertas, tu perfil, los avales y las valoraciones son
|
||||||
|
públicos y van firmados por ti. Sigue las
|
||||||
|
[Normas de la comunidad](normas-de-la-comunidad.md).
|
||||||
|
- **Tus claves y copias de seguridad.** No hay recuperación de cuenta: si pierdes tu
|
||||||
|
dispositivo y tu código de recuperación, no podemos restaurar nada.
|
||||||
|
|
||||||
|
## 3. Sin garantías
|
||||||
|
|
||||||
|
Tane se ofrece **"tal cual"**, sin garantía de ningún tipo, como software libre bajo la
|
||||||
|
[licencia AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html). En particular, nadie —
|
||||||
|
ni Comunes, ni las personas con las que intercambias a través de la aplicación —
|
||||||
|
garantiza la **identidad, pureza, germinación, sanidad o legalidad** de ninguna semilla
|
||||||
|
intercambiada. La información de cultivo que muestra la aplicación (calendarios,
|
||||||
|
consejos para extraer semilla, estimaciones de viabilidad) es orientación general, no
|
||||||
|
asesoramiento profesional.
|
||||||
|
|
||||||
|
## 4. Conflictos entre personas usuarias
|
||||||
|
|
||||||
|
Los intercambios son tratos privados entre las personas implicadas. Comunes no actúa
|
||||||
|
como mediadora y no acepta responsabilidad por intercambios fallidos, disputas de
|
||||||
|
calidad o pérdidas derivadas de ellos. Los avales y las valoraciones existen para que
|
||||||
|
cada comunidad construya su propia confianza.
|
||||||
|
|
||||||
|
## 5. Moderación
|
||||||
|
|
||||||
|
No hay moderación central: tu aplicación filtra lo que ves a través de la gente en la
|
||||||
|
que confías, y puedes **bloquear** a cualquiera y **denunciar** ofertas o personas
|
||||||
|
desde la propia aplicación. Las denuncias se comparten con los servidores implicados;
|
||||||
|
en el relé que operamos (`relay.comunes.org`), el contenido que incumple las normas de
|
||||||
|
la comunidad se elimina. Los relés de terceros aplican sus propias políticas.
|
||||||
|
|
||||||
|
## 6. Responsabilidad
|
||||||
|
|
||||||
|
En la máxima medida que permita la ley, la responsabilidad total de la Asociación
|
||||||
|
Comunes derivada de la aplicación se limita a lo que pagaste por ella (Tane es
|
||||||
|
gratuita: cero). Nada en estas condiciones limita la responsabilidad que no pueda
|
||||||
|
limitarse por ley, ni tus derechos como consumidor.
|
||||||
|
|
||||||
|
## 7. General
|
||||||
|
|
||||||
|
Si una parte de estas condiciones resultara inválida, el resto sigue en vigor. Estas
|
||||||
|
condiciones se rigen por el derecho español y de la UE, sin privarte de las
|
||||||
|
protecciones de la ley de tu país de residencia. Podemos actualizar estas condiciones;
|
||||||
|
los cambios relevantes se señalarán en las notas de versión de la aplicación, y la
|
||||||
|
versión vigente está siempre en <https://tane.comunes.org/legal/terms>.
|
||||||