feat(scaffold): create product workspace + Rust crates + Flutter app
Implements the canonical implementation directory layout adopted by
DEC-022 (register v0.9.5). Closes the scaffolding phase; no PoC code
has been promoted in yet (per proof-of-concept-plan.md §4 a PoC is
not product code unless explicitly promoted).
Rust workspace
==============
Top-level Cargo.toml declares seven workspace members:
core/chanora_core top-level Rust API + orchestration
crates/chanora_protocol tsclientlib isolation (SAD-067, SysDes-011/029)
crates/chanora_state state sync, reducers, deltas
crates/chanora_audio capture, DSP, Opus, jitter, mixer, playback
crates/chanora_storage non-secret DB + platform secure store
crates/chanora_diagnostics logs, redaction, export
crates/chanora_bridge typed Flutter/Rust DTOs
Workspace-wide pins:
license = "MIT OR Apache-2.0" (DEC-020)
rust-version = "1.95"
edition = "2021"
The Flutter app (apps/chanora_flutter) is NOT a Cargo workspace
member; it is owned by the Flutter / Gradle toolchain and is in the
workspace exclude array along with every poc/* spike.
Each crate ships:
* a Cargo.toml referring to workspace.dependencies pins;
* a lib.rs with #![forbid(unsafe_code)] + #![warn(missing_docs)],
a typed Error enum, and the public types relevant to the
subsystem's role per SAD §7.2;
* minimal unit tests so
running 1 test
test tests::defaults_match_decisions ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
running 1 test
test tests::it_compiles ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
running 1 test
test tests::session_can_be_constructed ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
running 1 test
test tests::marker_matches_poc ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
running 1 test
test tests::it_compiles ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
running 1 test
test tests::state_transitions_compile ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
running 1 test
test tests::it_compiles ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s is non-empty.
chanora_core's CoreError type-wraps every subsystem error via
#[from] so callers can match on origin without parsing strings.
chanora_audio's AudioEffects struct defaults all four effects to
true, matching DEC-007 (AEC), DEC-008 (AGC), DEC-009 (NS),
DEC-010 (HPF). A unit test pins this so a future regression that
flips a default fails immediately.
chanora_diagnostics exports REDACTION_MARKER = "[REDACTED]",
identical to the PoC's marker so audit grep patterns survive the
promotion.
chanora_bridge's BridgeError is Serialize + Deserialize so it can
flow across the FRB 2.x boundary (DEC-014).
Empirical verification: cargo check --workspace clean, cargo test
--workspace clean (7 unit tests + 7 doc-test runners, all passing)
against Rust 1.95.0 stable.
Flutter app
===========
apps/chanora_flutter created with .
DEC-004 applied: minSdk overridden to 28 in
android/app/build.gradle.kts with a comment that points back at the
decision register and forbids lowering it without re-opening DEC-004.
DEC-015 applied: shipped English + Simplified Chinese at MVP.
- pubspec.yaml gains flutter_localizations + intl + generate:true.
- l10n.yaml emits lib/l10n/generated/AppL10n (no synthetic package
— that was removed in Flutter 3.41+).
- lib/l10n/app_en.arb is the source of truth; lib/l10n/app_zh.arb
mirrors the key set in zh-Hans. ARB metadata explicitly
reaffirms ADR-008: server-provided content (channel names,
nicknames, welcome banners) is preserved verbatim and never
translated.
lib/main.dart and test/widget_test.dart were rewritten from the
template counter into a minimal localized scaffold that proves
both locales render correctly.
Empirical verification: reports no issues;
runs the two locale smoke tests and both pass.
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"@@locale": "en",
|
||||
"@@x-source-of-truth": "DEC-015 (register v0.9.5). Template ARB for English. Other locales must reference these keys. Server-provided content is NOT translated (ADR-008 / DEC-015).",
|
||||
|
||||
"appTitle": "Chanora",
|
||||
"@appTitle": {
|
||||
"description": "Application title shown in launchers and the app bar. The product name `Chanora` is fixed by DEC-018 and must not be translated."
|
||||
},
|
||||
|
||||
"homeGreeting": "Welcome to Chanora",
|
||||
"@homeGreeting": {
|
||||
"description": "Greeting shown on the empty home screen before any server is connected."
|
||||
},
|
||||
|
||||
"homeNotProductionReadyBanner": "Chanora is currently in early development. This build is not production-ready.",
|
||||
"@homeNotProductionReadyBanner": {
|
||||
"description": "Plain-language banner informing testers that this build is not for end-user use. Mirrors README.md's status line."
|
||||
},
|
||||
|
||||
"connectAction": "Connect to a server",
|
||||
"@connectAction": {
|
||||
"description": "Label for the primary action that opens the connect-to-server flow."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"@@locale": "zh",
|
||||
"@@x-source-of-truth": "DEC-015 (register v0.9.5). Simplified Chinese translations for the MVP key set. Keys must match app_en.arb; server-provided content is NOT translated (ADR-008 / DEC-015).",
|
||||
|
||||
"appTitle": "Chanora",
|
||||
"homeGreeting": "欢迎使用 Chanora",
|
||||
"homeNotProductionReadyBanner": "Chanora 目前处于早期开发阶段,此版本尚未达到正式发布质量。",
|
||||
"connectAction": "连接到服务器"
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
|
||||
import 'app_localizations_en.dart';
|
||||
import 'app_localizations_zh.dart';
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
|
||||
/// Callers can lookup localized strings with an instance of AppL10n
|
||||
/// returned by `AppL10n.of(context)`.
|
||||
///
|
||||
/// Applications need to include `AppL10n.delegate()` in their app's
|
||||
/// `localizationDelegates` list, and the locales they support in the app's
|
||||
/// `supportedLocales` list. For example:
|
||||
///
|
||||
/// ```dart
|
||||
/// import 'generated/app_localizations.dart';
|
||||
///
|
||||
/// return MaterialApp(
|
||||
/// localizationsDelegates: AppL10n.localizationsDelegates,
|
||||
/// supportedLocales: AppL10n.supportedLocales,
|
||||
/// home: MyApplicationHome(),
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// ## Update pubspec.yaml
|
||||
///
|
||||
/// Please make sure to update your pubspec.yaml to include the following
|
||||
/// packages:
|
||||
///
|
||||
/// ```yaml
|
||||
/// dependencies:
|
||||
/// # Internationalization support.
|
||||
/// flutter_localizations:
|
||||
/// sdk: flutter
|
||||
/// intl: any # Use the pinned version from flutter_localizations
|
||||
///
|
||||
/// # Rest of dependencies
|
||||
/// ```
|
||||
///
|
||||
/// ## iOS Applications
|
||||
///
|
||||
/// iOS applications define key application metadata, including supported
|
||||
/// locales, in an Info.plist file that is built into the application bundle.
|
||||
/// To configure the locales supported by your app, you’ll need to edit this
|
||||
/// file.
|
||||
///
|
||||
/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file.
|
||||
/// Then, in the Project Navigator, open the Info.plist file under the Runner
|
||||
/// project’s Runner folder.
|
||||
///
|
||||
/// Next, select the Information Property List item, select Add Item from the
|
||||
/// Editor menu, then select Localizations from the pop-up menu.
|
||||
///
|
||||
/// Select and expand the newly-created Localizations item then, for each
|
||||
/// locale your application supports, add a new item and select the locale
|
||||
/// you wish to add from the pop-up menu in the Value field. This list should
|
||||
/// be consistent with the languages listed in the AppL10n.supportedLocales
|
||||
/// property.
|
||||
abstract class AppL10n {
|
||||
AppL10n(String locale)
|
||||
: localeName = intl.Intl.canonicalizedLocale(locale.toString());
|
||||
|
||||
final String localeName;
|
||||
|
||||
static AppL10n of(BuildContext context) {
|
||||
return Localizations.of<AppL10n>(context, AppL10n)!;
|
||||
}
|
||||
|
||||
static const LocalizationsDelegate<AppL10n> delegate = _AppL10nDelegate();
|
||||
|
||||
/// A list of this localizations delegate along with the default localizations
|
||||
/// delegates.
|
||||
///
|
||||
/// Returns a list of localizations delegates containing this delegate along with
|
||||
/// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
|
||||
/// and GlobalWidgetsLocalizations.delegate.
|
||||
///
|
||||
/// Additional delegates can be added by appending to this list in
|
||||
/// MaterialApp. This list does not have to be used at all if a custom list
|
||||
/// of delegates is preferred or required.
|
||||
static const List<LocalizationsDelegate<dynamic>> localizationsDelegates =
|
||||
<LocalizationsDelegate<dynamic>>[
|
||||
delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
];
|
||||
|
||||
/// A list of this localizations delegate's supported locales.
|
||||
static const List<Locale> supportedLocales = <Locale>[
|
||||
Locale('en'),
|
||||
Locale('zh'),
|
||||
];
|
||||
|
||||
/// Application title shown in launchers and the app bar. The product name `Chanora` is fixed by DEC-018 and must not be translated.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Chanora'**
|
||||
String get appTitle;
|
||||
|
||||
/// Greeting shown on the empty home screen before any server is connected.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Welcome to Chanora'**
|
||||
String get homeGreeting;
|
||||
|
||||
/// Plain-language banner informing testers that this build is not for end-user use. Mirrors README.md's status line.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Chanora is currently in early development. This build is not production-ready.'**
|
||||
String get homeNotProductionReadyBanner;
|
||||
|
||||
/// Label for the primary action that opens the connect-to-server flow.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Connect to a server'**
|
||||
String get connectAction;
|
||||
}
|
||||
|
||||
class _AppL10nDelegate extends LocalizationsDelegate<AppL10n> {
|
||||
const _AppL10nDelegate();
|
||||
|
||||
@override
|
||||
Future<AppL10n> load(Locale locale) {
|
||||
return SynchronousFuture<AppL10n>(lookupAppL10n(locale));
|
||||
}
|
||||
|
||||
@override
|
||||
bool isSupported(Locale locale) =>
|
||||
<String>['en', 'zh'].contains(locale.languageCode);
|
||||
|
||||
@override
|
||||
bool shouldReload(_AppL10nDelegate old) => false;
|
||||
}
|
||||
|
||||
AppL10n lookupAppL10n(Locale locale) {
|
||||
// Lookup logic when only language code is specified.
|
||||
switch (locale.languageCode) {
|
||||
case 'en':
|
||||
return AppL10nEn();
|
||||
case 'zh':
|
||||
return AppL10nZh();
|
||||
}
|
||||
|
||||
throw FlutterError(
|
||||
'AppL10n.delegate failed to load unsupported locale "$locale". This is likely '
|
||||
'an issue with the localizations generation tool. Please file an issue '
|
||||
'on GitHub with a reproducible sample app and the gen-l10n configuration '
|
||||
'that was used.',
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// ignore: unused_import
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
import 'app_localizations.dart';
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
|
||||
/// The translations for English (`en`).
|
||||
class AppL10nEn extends AppL10n {
|
||||
AppL10nEn([String locale = 'en']) : super(locale);
|
||||
|
||||
@override
|
||||
String get appTitle => 'Chanora';
|
||||
|
||||
@override
|
||||
String get homeGreeting => 'Welcome to Chanora';
|
||||
|
||||
@override
|
||||
String get homeNotProductionReadyBanner =>
|
||||
'Chanora is currently in early development. This build is not production-ready.';
|
||||
|
||||
@override
|
||||
String get connectAction => 'Connect to a server';
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// ignore: unused_import
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
import 'app_localizations.dart';
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
|
||||
/// The translations for Chinese (`zh`).
|
||||
class AppL10nZh extends AppL10n {
|
||||
AppL10nZh([String locale = 'zh']) : super(locale);
|
||||
|
||||
@override
|
||||
String get appTitle => 'Chanora';
|
||||
|
||||
@override
|
||||
String get homeGreeting => '欢迎使用 Chanora';
|
||||
|
||||
@override
|
||||
String get homeNotProductionReadyBanner =>
|
||||
'Chanora 目前处于早期开发阶段,此版本尚未达到正式发布质量。';
|
||||
|
||||
@override
|
||||
String get connectAction => '连接到服务器';
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// Chanora Flutter application entry point.
|
||||
//
|
||||
// Scaffold only. This file is the minimum viable wiring to demonstrate
|
||||
// the i18n architecture (DEC-015: English + Chinese Simplified) and to
|
||||
// give `flutter analyze` and `flutter build linux --debug` something
|
||||
// to compile. Real product UI lands later when the Chanora Design
|
||||
// System is implemented per docs/ui-ux/.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'l10n/generated/app_localizations.dart';
|
||||
|
||||
void main() {
|
||||
runApp(const ChanoraApp());
|
||||
}
|
||||
|
||||
class ChanoraApp extends StatelessWidget {
|
||||
const ChanoraApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
onGenerateTitle: (ctx) => AppL10n.of(ctx).appTitle,
|
||||
theme: ThemeData(
|
||||
useMaterial3: true,
|
||||
colorSchemeSeed: const Color(0xFF3F51B5),
|
||||
),
|
||||
// DEC-015 (register v0.9.5): English + Chinese Simplified at MVP.
|
||||
// i18n-ready architecture; additional locales add an ARB file
|
||||
// under lib/l10n and a Locale entry here.
|
||||
localizationsDelegates: AppL10n.localizationsDelegates,
|
||||
supportedLocales: AppL10n.supportedLocales,
|
||||
home: const _HomeScreen(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _HomeScreen extends StatelessWidget {
|
||||
const _HomeScreen();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppL10n.of(context);
|
||||
final theme = Theme.of(context);
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(l10n.appTitle)),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(l10n.homeGreeting, style: theme.textTheme.headlineSmall),
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Text(l10n.homeNotProductionReadyBanner),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
FilledButton(
|
||||
onPressed: null, // wired in a later phase
|
||||
child: Text(l10n.connectAction),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user