flet-code-editor-dsl 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,619 @@
1
+ Metadata-Version: 2.4
2
+ Name: flet-code-editor-dsl
3
+ Version: 0.1.0
4
+ Summary: A real Flutter code editor exposed as a Flet control
5
+ Author: obgwew
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/obgwew/flet-code-editor-dsl
8
+ Project-URL: Issues, https://github.com/obgwew/flet-code-editor-dsl/issues
9
+ Keywords: flet,flutter,code-editor,syntax-highlighting
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: Apache Software License
12
+ Classifier: Operating System :: OS Independent
13
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ Requires-Dist: flet>=0.80
16
+
17
+ # flet-code-editor-dsl
18
+
19
+ > A real Flutter code editor exposed as a [Flet](https://flet.dev) control, with a small Python DSL for defining syntax-highlighting rules and bracket/quote pair matching.
20
+
21
+ ![Python](https://img.shields.io/badge/python-%3E%3D3.10-blue)
22
+ ![Flet](https://img.shields.io/badge/flet-%3E%3D0.80-informational)
23
+ ![Version](https://img.shields.io/badge/version-0.1.0-green)
24
+ ![License](https://img.shields.io/badge/license-Apache%202.0-blue)
25
+
26
+ ---
27
+
28
+ ## Table of Contents
29
+
30
+ 1. [Overview](#overview)
31
+ 2. [Features](#features)
32
+ 3. [Requirements](#requirements)
33
+ 4. [Installation](#installation)
34
+ - [Install directly from GitHub with pip](#option-1-install-directly-from-github-with-pip)
35
+ - [Use in a Flet app (`pyproject.toml`)](#option-2-declare-it-as-a-dependency-in-your-flet-app)
36
+ - [Pin to a branch, tag or commit](#pinning-a-version)
37
+ - [Local / editable install](#option-3-local-editable-install)
38
+ 5. [Quick Start](#quick-start)
39
+ 6. [API Reference](#api-reference)
40
+ - [`CodeEditor`](#codeeditor)
41
+ - [`rule()`](#rule)
42
+ - [`group()`](#group)
43
+ - [`pair()`](#pair)
44
+ - [`rules_json()` and `pairs_json()`](#rules_json-and-pairs_json)
45
+ 7. [How the Highlighting Engine Works](#how-the-highlighting-engine-works)
46
+ 8. [Controlling the Editor at Runtime](#controlling-the-editor-at-runtime)
47
+ 9. [Complete Example](#complete-example)
48
+ 10. [Building Your App](#building-your-app)
49
+ 11. [Project Structure](#project-structure)
50
+ 12. [Known Limitations](#known-limitations)
51
+ 13. [Troubleshooting](#troubleshooting)
52
+ 14. [Contributing](#contributing)
53
+ 15. [License](#license)
54
+
55
+ ---
56
+
57
+ ## Overview
58
+
59
+ `flet-code-editor-dsl` is a Flet extension that embeds a multi-line code editor built on Flutter's `TextField`. Instead of shipping a fixed set of language grammars, it provides a compact **DSL** (domain-specific language) in Python that lets you describe exactly how text should be colored:
60
+
61
+ - **Rules** — regular expressions mapped to colors, with optional context conditions (`before`, `after`, `ignore_if`).
62
+ - **Pairs** — matching delimiters (brackets, quotes, tags) with distinct colors for matched and unmatched occurrences.
63
+ - **Strict mode** — any text not covered by a rule can be flagged as an error.
64
+
65
+ This makes it well suited for custom languages, configuration formats, templating syntaxes, and educational tools.
66
+
67
+ ## Features
68
+
69
+ - Native Flutter rendering through the Flet extension mechanism.
70
+ - Declarative highlighting rules defined entirely from Python.
71
+ - Context-aware matching using look-behind (`before`), look-ahead (`after`) and exclusion (`ignore_if`) patterns.
72
+ - Grouped rules: several patterns sharing a single color.
73
+ - Bracket and quote pair matching, including nesting and unmatched-delimiter detection.
74
+ - Strict mode that paints unrecognized text with an error color.
75
+ - Configurable font family, font size and background color.
76
+ - `on_change` event for reading the current text.
77
+ - Inherits standard layout properties from `ft.LayoutControl` (`width`, `height`, `expand`, `margin`, etc.).
78
+
79
+ ## Requirements
80
+
81
+ | Component | Version |
82
+ |-----------|---------|
83
+ | Python | `>= 3.10` |
84
+ | Flet | `>= 0.80` (the Flutter side depends on `flet: ^0.80.0`) |
85
+ | Dart SDK | `>= 3.0.0 < 4.0.0` |
86
+ | Flutter | `>= 3.10.0` |
87
+ | Git | Required for installing from GitHub |
88
+
89
+ > **Note:** Because this package contains a Flutter extension, it only takes effect when your app is packaged with `flet build` (or run with a Flet client that includes the extension). See [Building Your App](#building-your-app).
90
+
91
+ ---
92
+
93
+ ## Installation
94
+
95
+ ### Option 1: Install directly from GitHub with pip
96
+
97
+ ```bash
98
+ pip install "git+https://github.com/obgwew/flet-code-editor-dsl.git"
99
+ ```
100
+
101
+ Or, using the explicit direct-reference form:
102
+
103
+ ```bash
104
+ pip install "flet-code-editor-dsl @ git+https://github.com/obgwew/flet-code-editor-dsl.git"
105
+ ```
106
+
107
+ ### Option 2: Declare it as a dependency in your Flet app
108
+
109
+ Add the package to the `dependencies` of your app's `pyproject.toml`:
110
+
111
+ ```toml
112
+ [project]
113
+ name = "my-flet-app"
114
+ version = "0.1.0"
115
+ requires-python = ">=3.10"
116
+ dependencies = [
117
+ "flet",
118
+ "flet-code-editor-dsl @ git+https://github.com/obgwew/flet-code-editor-dsl.git",
119
+ ]
120
+ ```
121
+
122
+ Then install and run as usual:
123
+
124
+ ```bash
125
+ pip install -e .
126
+ flet run
127
+ ```
128
+
129
+ If you use a `requirements.txt` instead:
130
+
131
+ ```text
132
+ flet
133
+ git+https://github.com/obgwew/flet-code-editor-dsl.git
134
+ ```
135
+
136
+ ### Pinning a version
137
+
138
+ Append `@<ref>` to the URL, where `<ref>` is a branch, tag, or commit hash:
139
+
140
+ ```bash
141
+ # Branch
142
+ pip install "git+https://github.com/obgwew/flet-code-editor-dsl.git@main"
143
+
144
+ # Tag
145
+ pip install "git+https://github.com/obgwew/flet-code-editor-dsl.git@v0.1.0"
146
+
147
+ # Commit
148
+ pip install "git+https://github.com/obgwew/flet-code-editor-dsl.git@<commit-sha>"
149
+ ```
150
+
151
+ Pinning to a tag or commit is strongly recommended for reproducible builds.
152
+
153
+ ### Option 3: Local editable install
154
+
155
+ ```bash
156
+ git clone https://github.com/obgwew/flet-code-editor-dsl.git
157
+ cd flet-code-editor-dsl
158
+ pip install -e .
159
+ ```
160
+
161
+ ### Verifying the installation
162
+
163
+ ```bash
164
+ python -c "import flet_code_editor_dsl as m; print(m.__all__)"
165
+ ```
166
+
167
+ Expected output:
168
+
169
+ ```text
170
+ ['CodeEditor', 'rule', 'group', 'pair', 'rules_json', 'pairs_json']
171
+ ```
172
+
173
+ ---
174
+
175
+ ## Quick Start
176
+
177
+ ```python
178
+ import flet as ft
179
+ import flet_code_editor_dsl as ce
180
+
181
+
182
+ def main(page: ft.Page):
183
+ page.title = "Code Editor"
184
+
185
+ editor = ce.CodeEditor(
186
+ value="let x = (1 + 2)",
187
+ rules=ce.rules_json([
188
+ ce.group(["let", "if", "else"], color="#C586C0"), # keywords
189
+ ce.rule(r"\d+", color="#B5CEA8"), # numbers
190
+ ]),
191
+ pairs=ce.pairs_json([
192
+ ce.pair("(", ")", color="#FFD700"),
193
+ ]),
194
+ default_color="#D4D4D4",
195
+ background_color="#1E1E1E",
196
+ expand=True,
197
+ )
198
+
199
+ page.add(editor)
200
+
201
+
202
+ ft.run(main)
203
+ ```
204
+
205
+ > On Flet versions that still use the older entry point, replace `ft.run(main)` with `ft.app(main)`.
206
+
207
+ ---
208
+
209
+ ## API Reference
210
+
211
+ All public names are importable from the top-level package:
212
+
213
+ ```python
214
+ from flet_code_editor_dsl import CodeEditor, rule, group, pair, rules_json, pairs_json
215
+ ```
216
+
217
+ ### `CodeEditor`
218
+
219
+ ```python
220
+ @ft.control("flet_code_editor_dsl")
221
+ class CodeEditor(ft.LayoutControl): ...
222
+ ```
223
+
224
+ | Property | Type | Default | Description |
225
+ |----------|------|---------|-------------|
226
+ | `value` | `str` | `""` | The text shown in the editor. Setting it from Python replaces the editor content. |
227
+ | `rules` | `str` (JSON) | `"[]"` | Highlighting rules. Build it with `rules_json([...])`. |
228
+ | `pairs` | `str` (JSON) | `"[]"` | Delimiter pairs. Build it with `pairs_json([...])`. |
229
+ | `default_color` | `str` | `"#000000"` | Color for text not matched by any rule (when `strict` is `False`). |
230
+ | `strict` | `bool` | `False` | If `True`, unmatched text is painted with `error_color` instead of `default_color`. |
231
+ | `error_color` | `str` | `"#FF0000"` | Color used for unrecognized text in strict mode. |
232
+ | `background_color` | `ft.ColorValue \| None` | `None` | Editor background. When `None`, the Flutter side falls back to `#202124`. |
233
+ | `font_family` | `str` | `"monospace"` | Font family used by the editor. |
234
+ | `font_size` | `float` | `14.0` | Font size in logical pixels. |
235
+ | `on_change` | `ft.ControlEventHandler \| None` | `None` | Called whenever the text changes. The new text is available as `e.data`. |
236
+
237
+ In addition, all `ft.LayoutControl` properties (such as `width`, `height`, `expand`, `margin`, `visible`, `opacity`) are supported.
238
+
239
+ > **Tip:** The default `default_color` is black (`#000000`) while the default background is dark. Always set `default_color` explicitly (for example `"#D4D4D4"`) when using a dark theme.
240
+
241
+ #### Color format
242
+
243
+ Colors are strings in one of these forms:
244
+
245
+ | Format | Example | Meaning |
246
+ |--------|---------|---------|
247
+ | `#RRGGBB` | `#FF8800` | Opaque color (alpha `FF` is added automatically). |
248
+ | `#AARRGGBB` | `#80FF8800` | Color with explicit alpha channel. |
249
+
250
+ Invalid values fall back to a default color instead of raising an error.
251
+
252
+ ---
253
+
254
+ ### `rule()`
255
+
256
+ Creates a single highlighting rule.
257
+
258
+ ```python
259
+ rule(
260
+ form: str,
261
+ color: str,
262
+ before: Optional[str] = None,
263
+ after: Optional[str] = None,
264
+ ignore_if: Optional[str] = None,
265
+ ) -> dict
266
+ ```
267
+
268
+ | Parameter | Description |
269
+ |-----------|-------------|
270
+ | `form` | Regular expression that selects the text to color. |
271
+ | `color` | Color applied to every match. |
272
+ | `before` | Regex that must match **immediately before** the match (it is anchored to the end of the preceding text). If it does not match, the occurrence is skipped. |
273
+ | `after` | Regex that must match **immediately after** the match (anchored to the start of the following text). |
274
+ | `ignore_if` | Regex tested at the **start of the match**. If it matches, the occurrence is skipped. |
275
+
276
+ ```python
277
+ # Highlight identifiers that directly follow "def "
278
+ ce.rule(r"[A-Za-z_]\w*", color="#DCDCAA", before=r"def\s+")
279
+
280
+ # Highlight names followed by "(" as function calls
281
+ ce.rule(r"[A-Za-z_]\w*", color="#DCDCAA", after=r"\(")
282
+
283
+ # Highlight words, but skip the keyword "let"
284
+ ce.rule(r"[A-Za-z_]\w*", color="#9CDCFE", ignore_if=r"let\b")
285
+ ```
286
+
287
+ ### `group()`
288
+
289
+ Creates a rule that applies **one color to several patterns**. The patterns are combined into a single alternation.
290
+
291
+ ```python
292
+ group(
293
+ forms: list[str],
294
+ color: str,
295
+ before: Optional[str] = None,
296
+ after: Optional[str] = None,
297
+ ignore_if: Optional[str] = None,
298
+ ) -> dict
299
+ ```
300
+
301
+ ```python
302
+ ce.group([r"\bif\b", r"\belse\b", r"\bwhile\b", r"\breturn\b"], color="#C586C0")
303
+ ```
304
+
305
+ ### `pair()`
306
+
307
+ Defines a pair of delimiters that are matched against each other.
308
+
309
+ ```python
310
+ pair(
311
+ open: str,
312
+ close: str,
313
+ color: Optional[str] = None,
314
+ unmatched_color: str = "#FF0000",
315
+ ) -> dict
316
+ ```
317
+
318
+ | Parameter | Description |
319
+ |-----------|-------------|
320
+ | `open` | Opening delimiter (a **literal string**, not a regex). |
321
+ | `close` | Closing delimiter (a literal string). |
322
+ | `color` | Color for correctly matched delimiters. If `None`, matched delimiters keep the color assigned by rules. |
323
+ | `unmatched_color` | Color for delimiters that have no partner. Defaults to red. |
324
+
325
+ Behavior:
326
+
327
+ - When `open != close` (e.g. `(` and `)`), delimiters are matched using a stack, so nesting is supported. An unmatched closer, or an opener left on the stack, is painted with `unmatched_color`.
328
+ - When `open == close` (e.g. `"` and `"`), occurrences are counted in order. If the total count is odd, the **last** one is treated as unmatched.
329
+
330
+ ```python
331
+ ce.pair("(", ")", color="#FFD700")
332
+ ce.pair("[", "]", color="#DA70D6")
333
+ ce.pair("{", "}", color="#179FFF")
334
+ ce.pair('"', '"', color="#CE9178", unmatched_color="#FF5555")
335
+ ```
336
+
337
+ ### `rules_json()` and `pairs_json()`
338
+
339
+ Serialize a list of rules or pairs into the compact JSON string expected by `CodeEditor.rules` and `CodeEditor.pairs`.
340
+
341
+ ```python
342
+ rules_json(rules: Iterable[dict]) -> str
343
+ pairs_json(pairs: Iterable[dict]) -> str
344
+ ```
345
+
346
+ ```python
347
+ editor.rules = ce.rules_json([ce.rule(r"\d+", "#B5CEA8")])
348
+ editor.pairs = ce.pairs_json([ce.pair("(", ")", "#FFD700")])
349
+ ```
350
+
351
+ ---
352
+
353
+ ## How the Highlighting Engine Works
354
+
355
+ Understanding the evaluation order helps you write predictable rules.
356
+
357
+ 1. **Rule pass** — Rules are applied in the order they appear in the list. Each match paints its characters with the rule's color. **Later rules override earlier ones** where matches overlap.
358
+ 2. **Context checks** — For every match, `before`, `after` and `ignore_if` are evaluated. If a check fails, that match is ignored.
359
+ 3. **Fallback pass** — Characters not covered by any rule receive `default_color`, or `error_color` if `strict=True`.
360
+ 4. **Pair pass** — Pair matching runs last and **overrides** the colors of delimiter characters. This ensures unmatched delimiters are always visible.
361
+
362
+ **Practical consequences**
363
+
364
+ - Put general rules first (identifiers, words) and specific rules last (keywords, literals).
365
+ - Regular expressions use the **Dart `RegExp`** engine (ECMAScript-style), not Python's `re`. Most common syntax is identical, but avoid Python-only constructs such as `(?P<name>...)` — use `(?<name>...)` instead.
366
+ - Patterns that fail to compile are silently skipped, so test new patterns incrementally.
367
+ - Use raw strings (`r"..."`) in Python to avoid double escaping.
368
+
369
+ ---
370
+
371
+ ## Controlling the Editor at Runtime
372
+
373
+ ### Reading the text
374
+
375
+ ```python
376
+ def on_change(e: ft.ControlEvent):
377
+ print("Current text:", e.data)
378
+
379
+ editor = ce.CodeEditor(on_change=on_change)
380
+ ```
381
+
382
+ ### Setting the text
383
+
384
+ ```python
385
+ editor.value = "new content"
386
+ editor.update()
387
+ ```
388
+
389
+ ### Updating rules or pairs dynamically
390
+
391
+ ```python
392
+ editor.rules = ce.rules_json([
393
+ ce.rule(r"\bTODO\b", "#FFCC00"),
394
+ ])
395
+ editor.update()
396
+ ```
397
+
398
+ ### Switching strict mode
399
+
400
+ ```python
401
+ editor.strict = True
402
+ editor.error_color = "#FF5555"
403
+ editor.update()
404
+ ```
405
+
406
+ ### Changing appearance
407
+
408
+ ```python
409
+ editor.font_size = 16
410
+ editor.font_family = "Courier New"
411
+ editor.background_color = "#0D1117"
412
+ editor.update()
413
+ ```
414
+
415
+ ### Layout
416
+
417
+ ```python
418
+ ce.CodeEditor(width=600, height=400) # fixed size
419
+ ce.CodeEditor(expand=True) # fill available space
420
+ ```
421
+
422
+ ---
423
+
424
+ ## Complete Example
425
+
426
+ A live-validating editor for a tiny expression language. Keywords, numbers, strings and comments are colored, brackets are matched, and anything unrecognized is flagged as an error.
427
+
428
+ ```python
429
+ import flet as ft
430
+ import flet_code_editor_dsl as ce
431
+
432
+ SAMPLE = '''\
433
+ # Example program
434
+ let total = (price * 3) + 12
435
+ if (total > 100) {
436
+ print("expensive")
437
+ } else {
438
+ print("cheap")
439
+ }
440
+ '''
441
+
442
+ RULES = ce.rules_json([
443
+ # Identifiers (general rule first)
444
+ ce.rule(r"[A-Za-z_]\w*", "#9CDCFE"),
445
+
446
+ # Function calls: a name followed by "("
447
+ ce.rule(r"[A-Za-z_]\w*", "#DCDCAA", after=r"\("),
448
+
449
+ # Keywords
450
+ ce.group([r"\blet\b", r"\bif\b", r"\belse\b"], "#C586C0"),
451
+
452
+ # Numbers
453
+ ce.rule(r"\b\d+(?:\.\d+)?\b", "#B5CEA8"),
454
+
455
+ # Operators and whitespace (so strict mode does not flag them)
456
+ ce.group([r"[+\-*/=<>!]+", r"\s+", r"[(){}\[\],;]"], "#D4D4D4"),
457
+
458
+ # Strings
459
+ ce.rule(r'"[^"\n]*"', "#CE9178"),
460
+
461
+ # Comments (last, so they override everything on the line)
462
+ ce.rule(r"#.*", "#6A9955"),
463
+ ])
464
+
465
+ PAIRS = ce.pairs_json([
466
+ ce.pair("(", ")", color="#FFD700"),
467
+ ce.pair("{", "}", color="#179FFF"),
468
+ ce.pair("[", "]", color="#DA70D6"),
469
+ ])
470
+
471
+
472
+ def main(page: ft.Page):
473
+ page.title = "flet-code-editor-dsl demo"
474
+ page.theme_mode = ft.ThemeMode.DARK
475
+
476
+ status = ft.Text("Characters: %d" % len(SAMPLE))
477
+
478
+ def on_change(e: ft.ControlEvent):
479
+ status.value = "Characters: %d" % len(e.data or "")
480
+ status.update()
481
+
482
+ def toggle_strict(e: ft.ControlEvent):
483
+ editor.strict = e.control.value
484
+ editor.update()
485
+
486
+ editor = ce.CodeEditor(
487
+ value=SAMPLE,
488
+ rules=RULES,
489
+ pairs=PAIRS,
490
+ default_color="#D4D4D4",
491
+ strict=False,
492
+ error_color="#F44747",
493
+ background_color="#1E1E1E",
494
+ font_family="monospace",
495
+ font_size=15,
496
+ expand=True,
497
+ on_change=on_change,
498
+ )
499
+
500
+ page.add(
501
+ ft.Row([ft.Switch(label="Strict mode", on_change=toggle_strict), status]),
502
+ editor,
503
+ )
504
+
505
+
506
+ ft.run(main)
507
+ ```
508
+
509
+ ---
510
+
511
+ ## Building Your App
512
+
513
+ Flutter extensions are compiled into the Flet client, so you must build a custom client for your target platform. Make sure the package is listed in your app's `pyproject.toml` dependencies (see [Installation](#option-2-declare-it-as-a-dependency-in-your-flet-app)), then run:
514
+
515
+ ```bash
516
+ # Desktop
517
+ flet build windows
518
+ flet build macos
519
+ flet build linux
520
+
521
+ # Mobile
522
+ flet build apk
523
+ flet build ipa
524
+
525
+ # Web
526
+ flet build web
527
+ ```
528
+
529
+ `flet build` detects the Flutter package bundled inside `flutter/flet_code_editor_dsl/` and adds it to the generated Flutter project automatically. A working Flutter SDK is required; Flet will download one if it is missing.
530
+
531
+ ---
532
+
533
+ ## Project Structure
534
+
535
+ ```text
536
+ flet-code-editor-dsl/
537
+ ├── pyproject.toml
538
+ └── src/
539
+ ├── flet_code_editor_dsl/
540
+ │ └── __init__.py # Python API: CodeEditor + DSL helpers
541
+ └── flutter/
542
+ ├── __init__.py
543
+ └── flet_code_editor_dsl/
544
+ ├── __init__.py
545
+ ├── pubspec.yaml # Flutter package definition
546
+ └── lib/
547
+ ├── flet_code_editor_dsl.dart
548
+ └── src/
549
+ ├── extension.dart # Registers the control with Flet
550
+ └── code_editor.dart # Editor widget + highlighting engine
551
+ ```
552
+
553
+ | File | Responsibility |
554
+ |------|----------------|
555
+ | `src/flet_code_editor_dsl/__init__.py` | Declares `CodeEditor` and the `rule` / `group` / `pair` helpers. |
556
+ | `.../lib/src/extension.dart` | Maps the control type `flet_code_editor_dsl` to the Flutter widget. |
557
+ | `.../lib/src/code_editor.dart` | Custom `TextEditingController` that builds colored `TextSpan`s from rules and pairs. |
558
+ | `pyproject.toml` | Packaging metadata; includes `pubspec.yaml` and `lib/**/*` as package data. |
559
+
560
+ ---
561
+
562
+ ## Known Limitations
563
+
564
+ - **No language awareness in pairs.** Delimiters inside strings or comments are still matched. Use `ignore_if` or rule ordering to reduce false positives, or avoid pairing characters that appear inside literals.
565
+ - **Highlighting is recomputed on each rebuild.** Very large documents with many rules may reduce typing performance.
566
+ - **Plain text editing only.** Line numbers, code folding, auto-indent and autocompletion are not included.
567
+ - **Dart regex semantics.** Patterns must be valid for Dart's `RegExp`; invalid patterns are ignored without an error message.
568
+ - **Client rebuild required.** As with all Flet extensions, the control is not available in the stock Flet client.
569
+ - **Minimum height.** The editor shows at least 12 lines.
570
+
571
+ ## Troubleshooting
572
+
573
+ | Symptom | Likely cause and fix |
574
+ |---------|----------------------|
575
+ | The editor does not appear, or an "unknown control" error is shown | The app is running with a stock Flet client. Build with `flet build`, or ensure the extension is registered in your custom client. |
576
+ | Text is hard to see | `default_color` defaults to black while the background is dark. Set `default_color` (for example `#D4D4D4`) or change `background_color`. |
577
+ | A rule has no effect | Its regex may be invalid for Dart, or a later rule overrides it. Simplify the pattern and check rule order. |
578
+ | Everything is red | `strict=True` is set and some text is not covered by any rule. Add rules for whitespace, operators and punctuation, or disable strict mode. |
579
+ | `pip install` from GitHub fails | Confirm Git is installed, the URL is correct, and (for private repositories) that you are authenticated via SSH or a personal access token. |
580
+ | Changes made from Python are not visible | Call `editor.update()` after modifying a property. |
581
+
582
+ For private repositories, authenticate with a token:
583
+
584
+ ```bash
585
+ pip install "git+https://<TOKEN>@github.com/obgwew/flet-code-editor-dsl.git"
586
+ ```
587
+
588
+ ## Contributing
589
+
590
+ Contributions are welcome.
591
+
592
+ 1. Fork the repository.
593
+ 2. Create a feature branch: `git checkout -b feature/my-feature`.
594
+ 3. Commit your changes with clear messages.
595
+ 4. Push the branch and open a Pull Request describing the change and its motivation.
596
+
597
+ Please open an issue first for larger changes or new features so the design can be discussed.
598
+
599
+ ## License
600
+
601
+ This project is licensed under the **Apache License, Version 2.0**. See the [LICENSE](LICENSE) file for the full text.
602
+
603
+ ```text
604
+ Copyright 2026 obgwew
605
+
606
+ Licensed under the Apache License, Version 2.0 (the "License");
607
+ you may not use this file except in compliance with the License.
608
+ You may obtain a copy of the License at
609
+
610
+ http://www.apache.org/licenses/LICENSE-2.0
611
+
612
+ Unless required by applicable law or agreed to in writing, software
613
+ distributed under the License is distributed on an "AS IS" BASIS,
614
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
615
+ See the License for the specific language governing permissions and
616
+ limitations under the License.
617
+ ```
618
+
619
+ By contributing to this repository, you agree that your contributions will be licensed under the same Apache 2.0 license.