Skip to content

Commit f3b3586

Browse files
committed
[green] Ship numbered adjustable editor
Signed-off-by: Viwat Vchirawongkwin <viwat.v@chula.ac.th>
1 parent 113123e commit f3b3586

23 files changed

Lines changed: 1269 additions & 149 deletions

‎app/DEPENDENCIES.md‎

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,16 @@
1010
1111
## Policy
1212

13-
- **Permissive only.** Every dependency — direct **and** transitive — must be
14-
**MIT / BSD-2-Clause / BSD-3-Clause / Apache-2.0** (or another MIT-compatible
15-
permissive licence). Copyleft (GPL/LGPL/MPL) is rejected. PyBLE ships MIT
16-
(CON-6); no dependency may encumber that.
17-
- **Recorded rationale + minimum version.** Each direct dependency below carries
18-
a rationale and a minimum (constraint) version.
13+
- **Permissive by default; exceptions are owner-ratified and package-specific.**
14+
Direct and transitive dependencies must normally be **MIT / BSD-2-Clause /
15+
BSD-3-Clause / Apache-2.0** (or another MIT-compatible permissive licence).
16+
GPL/LGPL and unknown licences are rejected. MPL is rejected unless an
17+
accepted decision explicitly names the exact package, shipped scope, and
18+
notice/source obligations. [ADR-0012](../docs/decisions/0012-flutter-code-editor-behind-editorsurface.md)
19+
is that owner ratification for unmodified `autotrie` 2.0.0 only; it does not
20+
relicense PyBLE's MIT source or admit other copyleft dependencies.
21+
- **Recorded rationale + version policy.** Each direct dependency below carries
22+
a rationale and either a minimum compatible version or an exact pin.
1923
- **Tracked lockfile.** `app/pubspec.lock` is committed; CI resolves against it
2024
with `flutter pub get --enforce-lockfile` and builds/tests from the pinned
2125
versions (BLD-2). A resolved-version change is a reviewable diff.
@@ -31,12 +35,14 @@ Toolchain pin: **Flutter 3.44.1 (stable)** / Dart SDK `^3.12.1` (see
3135
| Package | Kind | Constraint | Resolved | SPDX | Rationale |
3236
|---|---|---|---|---|---|
3337
| `flutter` | runtime (SDK) | Flutter SDK | 3.44.1 | BSD-3-Clause | The app framework. One codebase targets iPadOS + Android (BLD-1); BLE-only, so no web/desktop targets are wired. |
38+
| `flutter_code_editor` | runtime | `0.3.5` (exact) | 0.3.5 | Apache-2.0 AND MIT | The default rich Python editor behind PyBLE's app-owned `EditorSurface` seam (A-20, [ADR-0012](../docs/decisions/0012-flutter-code-editor-behind-editorsurface.md)). Exact pin because upstream is dormant and PyBLE owns the Tab/smart-punctuation safeguards plus the live plain-field fallback. The package `LICENSE` contains Apache-2.0 for the package and retains the MIT grant for its `code_field` ancestry. Optional network, error, find, and autocomplete UI stays disabled. Its unavoidable shipped `autotrie` dependency is covered by the owner-ratified `†` exception below. |
3439
| `flutter_localizations` | runtime (SDK) | Flutter SDK | 3.44.1 | BSD-3-Clause | The ARB/`gen-l10n` runtime + the `GlobalMaterial/Widgets/Cupertino` localization delegates (X-12). Activates `generate: true` (`app/l10n.yaml` → `AppLocalizations`) and is wired into `MaterialApp` at the shell-string migration (FR-I18N-1..5, BLD-5). Ships with the Flutter SDK (no separate version). |
3540
| `flutter_riverpod` | runtime | `^2.6.1` | 2.6.1 | MIT | The single declarative state-management approach fixed by [ADR-0007](../docs/decisions/0007-riverpod-state-management.md) (NFR-MAINT-2). The one `Connection` is injected at a single `ProviderScope` root and read-only-derived downstream (TDD D1/D3). A 3.x major bump requires a new ledger row + rationale. |
3641
| `flutter_blue_plus` | runtime | `^1.35.0` | 1.36.8 | BSD-3-Clause | The BLE transport (A-02; FR-BLE-1/2/3/5). The **sole** importer lives under `lib/ble/`; no other layer may import it (enforced by `tools/ci/import_boundary.sh`, CON-8). Min 1.35.0 is the floor carrying the GATT service-filtered scan + `requestMtu(247)` + write-without-response API the `lib/ble` adapter binds to. It is a **federated** plugin: it hard-depends on all platform backends (`_android`/`_darwin`/`_linux`/`_web`); only the Android + iOS (`_darwin`) backends compile into PyBLE's shipped targets — see the **copyleft carve-out** under the transitive table. |
3742
| `intl` | runtime | `^0.20.2` | 0.20.2 | BSD-3-Clause | The ARB message runtime consumed by the generated `AppLocalizations` (X-12). The constraint is reconciled to the exact `intl` the pinned `flutter_localizations` (Flutter 3.44.1) admits — 0.20.2. |
3843
| `package_info_plus` | runtime | `^10.2.1` | 10.2.1 | BSD-3-Clause | The sole `lib/app/app_build_info.dart` adapter reads the actual installed iOS/Android version and build number for the X-11 About page (FR-ABOUT-3). It is never imported by `lib/pble`; static ASCII `kAppVersion` remains the PBLE/1 HELLO identity. Min 10.2.1 supports PyBLE's pinned Flutter 3.44.1, Dart 3.12.1, iOS 13, Java 17, AGP 9.0.1, and Gradle 9.1 toolchain. |
3944
| `http` | runtime | `^1.6.0` | 1.6.0 | BSD-3-Clause | The bounded public GitHub REST client for the connected A-33 subset ([ADR-0040](../docs/decisions/0040-sha-pinned-connected-github-import.md)). The direct dependency is confined to `lib/github_import/`'s production composition point and `GithubRepositoryClient`; all feature consumers bind to the neutral `GithubApi` seam. The adapter accepts only canonical public repository input, sends no credential or user source, permits requests only to exact HTTPS host `api.github.com`, disables redirects, pins every browse to a full commit SHA, and bounds time and response bodies. Min 1.6.0 supplies the abortable request surface used by the cancellation contract on the pinned Dart/Flutter toolchain. |
45+
| `highlight` | runtime | `^0.7.0` | 0.7.0 | MIT | Python grammar data consumed directly by `lib/editor`'s rich adapter (A-20). It also appears in `flutter_code_editor`'s closure, but PyBLE's direct import makes the dependency and compatible minimum explicit under BLD-2. |
4046
| `webview_flutter` | runtime | `^4.14.1` | 4.14.1 | BSD-3-Clause | The offline platform-WebView host for the pinned Blockly runtime (A-31, ADR-0013). Min 4.14.1 matches PyBLE's Android 24 / iOS 13 platform floors and supplies `loadFlutterAsset`, JavaScript channels, and navigation delegates. Its sole importer is `lib/blocks/`; generated source and board actions stay in Dart behind the neutral `Connection` seam. |
4147
| `flutter_test` | dev (SDK) | Flutter SDK | 3.44.1 | BSD-3-Clause | Widget/unit/golden test harness; the substrate for the G1 suites (NFR-MAINT-4). |
4248
| `integration_test` | dev (SDK) | Flutter SDK | 3.44.1 | BSD-3-Clause | On-device Android/iPadOS parity coverage for the real Blockly platform WebView, local asset load, JavaScript bridge, generated Python, and file-backed actions (A-31). Ships with the Flutter SDK and is never part of a release build. |
@@ -46,19 +52,25 @@ Toolchain pin: **Flutter 3.44.1 (stable)** / Dart SDK `^3.12.1` (see
4652

4753
All are pulled in by the direct dependencies above; PyBLE declares none of them
4854
directly. Every one is permissive and MIT-compatible (verified against the
49-
`LICENSE` file shipped in each package), **except** the two `‡`-marked `bluez` /
50-
`dbus` packages — see the copyleft carve-out below the table.
55+
`LICENSE` file shipped in the exact locally resolved package), **except** the
56+
`†`-marked shipped `autotrie` package and the two `‡`-marked Linux-only
57+
`bluez` / `dbus` packages. Their distinct copyleft treatments are recorded
58+
below the table.
5159

5260
| Package | Resolved | SPDX |
5361
|---|---|---|
5462
| `args` | 2.7.0 | BSD-3-Clause |
5563
| `async` | 2.13.1 | BSD-3-Clause |
64+
| `autotrie` `†` | 2.0.0 | MPL-2.0 |
5665
| `bluez` `‡` | 0.8.3 | MPL-2.0 |
5766
| `boolean_selector` | 2.1.2 | BSD-3-Clause |
5867
| `characters` | 1.4.1 | BSD-3-Clause |
68+
| `charcode` | 1.4.0 | BSD-3-Clause |
5969
| `clock` | 1.1.2 | Apache-2.0 |
6070
| `collection` | 1.19.1 | BSD-3-Clause |
71+
| `crypto` | 3.0.7 | BSD-3-Clause |
6172
| `dbus` `‡` | 0.7.14 | MPL-2.0 |
73+
| `equatable` | 2.1.0 | MIT |
6274
| `fake_async` | 1.3.3 | Apache-2.0 |
6375
| `ffi` | 2.2.0 | BSD-3-Clause |
6476
| `ffi_leak_tracker` | 0.1.2 | BSD-3-Clause |
@@ -69,16 +81,20 @@ directly. Every one is permissive and MIT-compatible (verified against the
6981
| `flutter_blue_plus_platform_interface` | 7.0.0 | BSD-3-Clause |
7082
| `flutter_blue_plus_web` | 7.0.2 | BSD-3-Clause |
7183
| `flutter_driver` | (Flutter SDK) | BSD-3-Clause |
84+
| `flutter_highlight` | 0.7.0 | MIT |
7285
| `flutter_web_plugins` | (Flutter SDK) | BSD-3-Clause |
7386
| `fuchsia_remote_debug_protocol` | (Flutter SDK) | BSD-3-Clause |
87+
| `hive` | 2.2.3 | Apache-2.0 |
7488
| `http_parser` | 4.1.2 | BSD-3-Clause |
7589
| `leak_tracker` | 11.0.2 | BSD-3-Clause |
7690
| `leak_tracker_flutter_testing` | 3.0.10 | BSD-3-Clause |
7791
| `leak_tracker_testing` | 3.0.2 | BSD-3-Clause |
92+
| `linked_scroll_controller` | 0.2.0 | BSD-3-Clause |
7893
| `lints` | 6.1.0 | BSD-3-Clause |
7994
| `matcher` | 0.12.19 | BSD-3-Clause |
8095
| `material_color_utilities` | 0.13.0 | Apache-2.0 |
8196
| `meta` | 1.18.0 | BSD-3-Clause |
97+
| `mocktail` | 1.0.5 | MIT |
8298
| `package_info_plus_platform_interface` | 4.1.0 | BSD-3-Clause |
8399
| `path` | 1.9.1 | BSD-3-Clause |
84100
| `petitparser` | 7.0.2 | MIT |
@@ -87,6 +103,7 @@ directly. Every one is permissive and MIT-compatible (verified against the
87103
| `process` | 5.0.5 | BSD-3-Clause |
88104
| `riverpod` | 2.6.1 | MIT |
89105
| `rxdart` | 0.28.0 | Apache-2.0 |
106+
| `scrollable_positioned_list` | 0.3.8 | BSD-3-Clause |
90107
| `sky_engine` | (Flutter SDK) | BSD-3-Clause |
91108
| `source_span` | 1.10.2 | BSD-3-Clause |
92109
| `stack_trace` | 1.12.1 | BSD-3-Clause |
@@ -96,7 +113,16 @@ directly. Every one is permissive and MIT-compatible (verified against the
96113
| `sync_http` | 0.3.1 | BSD-3-Clause |
97114
| `term_glyph` | 1.2.2 | BSD-3-Clause |
98115
| `test_api` | 0.7.11 | BSD-3-Clause |
116+
| `tuple` | 2.0.2 | BSD-2-Clause |
99117
| `typed_data` | 1.4.0 | BSD-3-Clause |
118+
| `url_launcher` | 6.3.2 | BSD-3-Clause |
119+
| `url_launcher_android` | 6.3.32 | BSD-3-Clause |
120+
| `url_launcher_ios` | 6.4.1 | BSD-3-Clause |
121+
| `url_launcher_linux` | 3.2.2 | BSD-3-Clause |
122+
| `url_launcher_macos` | 3.2.5 | BSD-3-Clause |
123+
| `url_launcher_platform_interface` | 2.3.2 | BSD-3-Clause |
124+
| `url_launcher_web` | 2.4.3 | BSD-3-Clause |
125+
| `url_launcher_windows` | 3.1.5 | BSD-3-Clause |
100126
| `vector_math` | 2.2.0 | BSD-3-Clause |
101127
| `vm_service` | 15.2.0 | BSD-3-Clause |
102128
| `web` | 1.1.1 | BSD-3-Clause |
@@ -107,6 +133,36 @@ directly. Every one is permissive and MIT-compatible (verified against the
107133
| `win32` | 6.3.0 | BSD-3-Clause |
108134
| `xml` | 6.6.1 | MIT |
109135

136+
### `†` Owner-ratified shipped carve-out — `autotrie` 2.0.0 (MPL-2.0)
137+
138+
`flutter_code_editor` constructs its autocomplete index with `autotrie`, even
139+
when PyBLE leaves the package's autocomplete UI disabled. The dependency is
140+
therefore part of the Android and iPadOS app rather than a lockfile-only
141+
platform artifact. Accepted [ADR-0012](../docs/decisions/0012-flutter-code-editor-behind-editorsurface.md)
142+
is the project owner's explicit ratification of this bounded exception:
143+
144+
- **Exact, unmodified scope.** The exception covers only the resolved
145+
`autotrie` 2.0.0 package required by exactly pinned
146+
`flutter_code_editor` 0.3.5. PyBLE vendors or modifies none of its files. A
147+
package/version change or a PyBLE-authored modification requires a fresh
148+
licence review and decision.
149+
- **File-level boundary.** MPL-2.0 applies to the covered `autotrie` files; it
150+
does not relicense separate PyBLE-authored files or the app as a whole. The
151+
downstream `hive` package is Apache-2.0.
152+
- **Distribution obligations remain active.** Release notices must include the
153+
complete MPL-2.0 text, exact package/version identity, and a durable way to
154+
obtain the corresponding unmodified source (the locked
155+
[pub.dev package](https://pub.dev/packages/autotrie/versions/2.0.0) and its
156+
[recorded upstream repository](https://github.com/AKushWarrior/autotrie)).
157+
Flutter's generated Pub-package `NOTICES.Z`/`LicenseRegistry` payload (the
158+
app's `THIRD_PARTY_LICENSES` bundle) must retain this entry on both shipped
159+
targets. The in-app **Open-source licenses** page reads that payload locally.
160+
- **No general copyleft precedent.** This owner-approved exception does not
161+
admit another MPL package, GPL/LGPL code, or an unknown licence by analogy.
162+
163+
This is intentionally separate from the `‡` exception below: `autotrie` is
164+
shipped and its MPL obligations apply, while `bluez` / `dbus` are not shipped.
165+
110166
### `‡` Copyleft carve-out — `bluez` / `dbus` (MPL-2.0), Linux-only, never shipped
111167

112168
`flutter_blue_plus` is a **federated** plugin that hard-depends on every platform

‎app/lib/editor/editor.dart‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,21 @@
1010
/// ([EditorView]). Every board action crosses the abstract `Connection` seam
1111
/// (CON-8); nothing here imports `lib/ble/`.
1212
///
13-
/// Deferred (recorded, not dropped): Drift persistence (A-24); syntax
14-
/// highlighting, multi-file tabs, find/replace, external-keyboard shortcuts, and
15-
/// a line-number gutter (FR-EDIT-1..4, OI-1).
13+
/// ADR-0012 puts the rich and plain editing implementations behind the exported
14+
/// EditorSurface seam. The rich default supplies Python highlighting and line
15+
/// numbers; session display settings resize code and gutter together. Deferred
16+
/// (recorded, not dropped): Drift persistence (A-24), multi-file tabs,
17+
/// app-owned find/replace, and the remaining external-keyboard shortcuts.
1618
library;
1719

20+
export 'editor_display_settings.dart';
1821
export 'editor_document.dart';
22+
export 'editor_surface.dart';
23+
export 'editor_surface_factory.dart';
1924
export 'editor_view.dart';
25+
export 'plain_editor_surface.dart' show kEditorPlainSurfaceKey;
2026
export 'program_actions.dart';
27+
export 'rich_editor_surface.dart' show kEditorRichSurfaceKey;
2128
export 'run_controller.dart';
2229
export 'save_controller.dart';
2330
export 'smart_punctuation.dart';
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// SPDX-License-Identifier: MIT
2+
// Part of PyBLE (https://pyble.dev) — see /LICENSE.
3+
4+
/// Session-wide editor presentation settings.
5+
///
6+
/// A-24 will hydrate this state through the single Drift `SettingsStore`.
7+
/// Until that store exists the value deliberately survives navigation, but not
8+
/// process restart; introducing a second preferences backend would violate D4.
9+
library;
10+
11+
import 'package:flutter/foundation.dart';
12+
import 'package:flutter_riverpod/flutter_riverpod.dart';
13+
14+
/// Inclusive editor-font bounds and the Signal-code default (FR-EDIT-5).
15+
const double kEditorMinFontSize = 10;
16+
const double kEditorDefaultFontSize = 14;
17+
const double kEditorMaxFontSize = 24;
18+
const double kEditorFontSizeStep = 1;
19+
20+
@immutable
21+
class EditorDisplaySettings {
22+
const EditorDisplaySettings({this.fontSize = kEditorDefaultFontSize});
23+
24+
final double fontSize;
25+
26+
@override
27+
bool operator ==(Object other) =>
28+
identical(this, other) ||
29+
other is EditorDisplaySettings && other.fontSize == fontSize;
30+
31+
@override
32+
int get hashCode => fontSize.hashCode;
33+
}
34+
35+
class EditorDisplaySettingsController extends Notifier<EditorDisplaySettings> {
36+
@override
37+
EditorDisplaySettings build() => const EditorDisplaySettings();
38+
39+
void increaseFontSize() => setFontSize(state.fontSize + kEditorFontSizeStep);
40+
41+
void decreaseFontSize() => setFontSize(state.fontSize - kEditorFontSizeStep);
42+
43+
/// Applies a finite, whole-point value inside the frozen editor range.
44+
void setFontSize(double value) {
45+
final double normalized = value.isFinite
46+
? value.roundToDouble().clamp(kEditorMinFontSize, kEditorMaxFontSize)
47+
: kEditorDefaultFontSize;
48+
if (normalized == state.fontSize) return;
49+
state = EditorDisplaySettings(fontSize: normalized);
50+
}
51+
}
52+
53+
/// Kept alive at the application ProviderScope so zoom survives navigation.
54+
final NotifierProvider<EditorDisplaySettingsController, EditorDisplaySettings>
55+
editorDisplaySettingsProvider =
56+
NotifierProvider<EditorDisplaySettingsController, EditorDisplaySettings>(
57+
EditorDisplaySettingsController.new,
58+
);

‎app/lib/editor/editor_surface.dart‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// SPDX-License-Identifier: MIT
2+
// Part of PyBLE (https://pyble.dev) — see /LICENSE.
3+
4+
/// The package-independent editor presentation seam fixed by ADR-0012.
5+
library;
6+
7+
import 'package:flutter/material.dart';
8+
9+
@immutable
10+
class EditorSurfaceConfiguration {
11+
const EditorSurfaceConfiguration({
12+
required this.text,
13+
required this.onChanged,
14+
required this.textStyle,
15+
required this.backgroundColor,
16+
required this.cursorColor,
17+
required this.gutterColor,
18+
required this.hintText,
19+
required this.contentPadding,
20+
});
21+
22+
/// Push-in document snapshot. The surface never owns canonical source.
23+
final String text;
24+
25+
/// Stream-out edits. The document provider remains the source of truth.
26+
final ValueChanged<String> onChanged;
27+
final TextStyle textStyle;
28+
final Color backgroundColor;
29+
final Color cursorColor;
30+
final Color gutterColor;
31+
final String hintText;
32+
final EdgeInsets contentPadding;
33+
}
34+
35+
/// Base type implemented by both executable ADR-0012 surfaces.
36+
abstract class EditorSurface extends StatefulWidget {
37+
const EditorSurface({super.key});
38+
}
39+
40+
typedef EditorSurfaceBuilder =
41+
EditorSurface Function(EditorSurfaceConfiguration configuration);
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// SPDX-License-Identifier: MIT
2+
// Part of PyBLE (https://pyble.dev) — see /LICENSE.
3+
4+
/// ADR-0012 editor-surface composition and its test/fallback injection seam.
5+
library;
6+
7+
import 'package:flutter_riverpod/flutter_riverpod.dart';
8+
9+
import 'editor_surface.dart';
10+
import 'plain_editor_surface.dart';
11+
import 'rich_editor_surface.dart';
12+
13+
EditorSurface richEditorSurfaceBuilder(
14+
EditorSurfaceConfiguration configuration,
15+
) => RichEditorSurface(configuration: configuration);
16+
17+
EditorSurface plainEditorSurfaceBuilder(
18+
EditorSurfaceConfiguration configuration,
19+
) => PlainEditorSurface(configuration: configuration);
20+
21+
final Provider<EditorSurfaceBuilder> editorSurfaceBuilderProvider =
22+
Provider<EditorSurfaceBuilder>((_) => richEditorSurfaceBuilder);

0 commit comments

Comments
 (0)