Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .fvmrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"flutter": "stable"
}
"flutter": "3.41.9"
}
2 changes: 0 additions & 2 deletions .github/workflows/firebase-hosting-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ jobs:
run: |
curl -fsSL https://fvm.app/install.sh | bash
echo "/home/runner/fvm/bin" >> $GITHUB_PATH
export PATH="/home/runner/fvm/bin:$PATH"
fvm use --force

- uses: kuhnroyal/flutter-fvm-config-action@v2
id: fvm-config-action
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/firebase-hosting-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ jobs:
run: |
curl -fsSL https://fvm.app/install.sh | bash
echo "/home/runner/fvm/bin" >> $GITHUB_PATH
export PATH="/home/runner/fvm/bin:$PATH"
fvm use --force

- uses: kuhnroyal/flutter-fvm-config-action@v2
id: fvm-config-action
Expand Down
19 changes: 0 additions & 19 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,5 @@
"**/.fvm/versions": true,
"**/.fvm/flutter_sdk": true
},
"workbench.colorCustomizations": {
"activityBar.activeBackground": "#fbed80",
"activityBar.background": "#fbed80",
"activityBar.foreground": "#15202b",
"activityBar.inactiveForeground": "#15202b99",
"activityBarBadge.background": "#06b9a5",
"activityBarBadge.foreground": "#15202b",
"commandCenter.border": "#15202b99",
"sash.hoverBorder": "#fbed80",
"statusBar.background": "#f9e64f",
"statusBar.foreground": "#15202b",
"statusBarItem.hoverBackground": "#f7df1e",
"statusBarItem.remoteBackground": "#f9e64f",
"statusBarItem.remoteForeground": "#15202b",
"titleBar.activeBackground": "#f9e64f",
"titleBar.activeForeground": "#15202b",
"titleBar.inactiveBackground": "#f9e64f99",
"titleBar.inactiveForeground": "#15202b99"
},
"peacock.color": "#f9e64f"
}
6 changes: 3 additions & 3 deletions demo/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ environment:
dependencies:
flutter:
sdk: flutter
google_fonts: ^6.3.2
google_fonts: ^8.1.0
mesh: ^0.4.3
mix: ^2.0.0-rc.0
mix: ^2.0.3
superdeck_core: ^1.0.0
superdeck: ^1.0.0
signals_flutter: ^6.0.4
naked_ui: ^0.2.0-beta.7
remix: ^0.1.0-beta.1
remix: ^0.2.0
dev_dependencies:
flutter_test:
sdk: flutter
Expand Down
2 changes: 1 addition & 1 deletion melos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ command:
flutter: ">=3.38.1"
dependencies:
collection: ^1.18.0
mix: ^2.0.0-rc.0
mix: ^2.0.3
ack: 1.0.0-beta.9
# publish:
# hooks:
Expand Down
79 changes: 34 additions & 45 deletions packages/builder/lib/src/parsers/markdown_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ class MarkdownParser {

static final _yamlKeyPattern = RegExp(r'^[A-Za-z_][\w-]*\s*:');

/// Leading characters that mark a line as markdown body (heading, directive,
/// blockquote, image/link) and therefore rule out YAML frontmatter.
static const _markdownLeadChars = {'#', '@', '>', '!'};

/// Splits the entire markdown into slides.
///
/// A slide is bounded by `---` separator lines. A slide may begin with an
Expand All @@ -54,35 +58,35 @@ class MarkdownParser {
final slides = <String>[];
final buffer = StringBuffer();

void flush() {
final pending = buffer.toString().trim();
if (pending.isNotEmpty) slides.add(pending);
buffer.clear();
}

var i = 0;
while (i < lines.length) {
if (separators.contains(i)) {
final pending = buffer.toString().trim();
if (pending.isNotEmpty) {
slides.add(pending);
buffer.clear();
}

final closeIdx = _findFrontmatterClose(lines, i, separators);
if (closeIdx != null) {
for (var j = i; j <= closeIdx; j++) {
buffer.writeln(lines[j]);
}
i = closeIdx + 1;
continue;
}
if (!separators.contains(i)) {
buffer.writeln(lines[i]);
i++;
continue;
}

flush();
final closeIdx = _findFrontmatterClose(lines, i, separators);
if (closeIdx == null) {
i++;
continue;
}

buffer.writeln(lines[i]);
i++;
// Consume the frontmatter block (open `---`, YAML body, close `---`).
for (var j = i; j <= closeIdx; j++) {
buffer.writeln(lines[j]);
}
i = closeIdx + 1;
}

final tail = buffer.toString().trim();
if (tail.isNotEmpty) slides.add(tail);

flush();
return slides;
}

Expand Down Expand Up @@ -112,46 +116,31 @@ class MarkdownParser {
}

/// If [openIdx] opens a YAML frontmatter block, returns the index of the
/// closing `---`. Returns null when the next separator is too far away or
/// the lines between look like markdown content rather than YAML.
/// closing `---`. Returns null when no closing `---` is found, or when the
/// lines between look like markdown content rather than YAML.
static int? _findFrontmatterClose(
List<String> lines,
int openIdx,
Set<int> separators,
) {
int? closeIdx;
var hasContent = false;
var hasYamlMarker = false;
for (var j = openIdx + 1; j < lines.length; j++) {
if (separators.contains(j)) {
closeIdx = j;
break;
}
final trimmed = lines[j].trimLeft();
if (trimmed.isEmpty) continue;
// Distinctive markdown body indicators rule out frontmatter.
final firstChar = trimmed[0];
if (firstChar == '#' ||
firstChar == '@' ||
firstChar == '>' ||
firstChar == '!') {
return null;
// An empty pair (`---\n---`) is a valid (empty) frontmatter block.
// Otherwise require at least one YAML-shaped line.
return (!hasContent || hasYamlMarker) ? j : null;
}
}

if (closeIdx == null) return null;

var hasContent = false;
var hasYamlMarker = false;
for (var j = openIdx + 1; j < closeIdx; j++) {
final trimmed = lines[j].trim();
if (trimmed.isEmpty) continue;
// Distinctive markdown body indicators rule out frontmatter.
if (_markdownLeadChars.contains(trimmed[0])) return null;
hasContent = true;
if (_yamlKeyPattern.hasMatch(trimmed) || trimmed.startsWith('- ')) {
hasYamlMarker = true;
break;
}
}

if (!hasContent || hasYamlMarker) return closeIdx;
// Reached EOF without a closing `---`.
return null;
}

Expand Down
3 changes: 3 additions & 0 deletions packages/builder/lib/superdeck_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ export 'package:superdeck_core/superdeck_core.dart' show DeckFormatException;

export 'src/build/build_event.dart';
export 'src/build/deck_builder.dart';
export 'src/parsers/comment_parser.dart';
export 'src/parsers/markdown_parser.dart';
export 'src/parsers/section_parser.dart';
45 changes: 45 additions & 0 deletions packages/playground/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/

# IntelliJ related
*.iml
*.ipr
*.iws
.idea/

# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/

# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
/coverage/

# Symbolication related
app.*.symbols

# Obfuscation related
app.*.map.json

# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
33 changes: 33 additions & 0 deletions packages/playground/.metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.

version:
revision: "00b0c91f06209d9e4a41f71b7a512d6eb3b9c694"
channel: "stable"

project_type: app

# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
base_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
- platform: macos
create_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
base_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
- platform: web
create_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
base_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694

# User provided section

# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
48 changes: 48 additions & 0 deletions packages/playground/CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Playground Editor

The live Markdown editor in the SuperDeck playground app: a three-panel screen
where edits to Markdown re-render slide previews in real time.

## Language

**Deck**:
The ordered set of slides parsed from the editor's Markdown buffer.
_Avoid_: presentation, document

**Slide**:
One unit of deck content, delimited in Markdown by `---` separators.
_Avoid_: page, card

**Active Slide**:
The slide the editor caret currently sits in; its index is owned solely by `EditorState.activeSlideIndex`.
_Avoid_: current slide, selected slide

**Thumbnail**:
A cached PNG render of a slide, shown in the Preview Sidebar for every slide except the Active Slide.
_Avoid_: preview image, snapshot

**Preview Sidebar**:
The left panel that lists every slide — the Active Slide rendered live, the rest as Thumbnails.
_Avoid_: slide list, filmstrip

**Thumbnail Refresher**:
The module that regenerates Thumbnails when the deck first loads and whenever the Active Slide changes.
_Avoid_: thumbnail service (that is superdeck's capture pipeline), debouncer

## Relationships

- A **Deck** contains one or more **Slides**
- Exactly one **Slide** is the **Active Slide** at any time
- Every **Slide** except the **Active Slide** appears in the **Preview Sidebar** as a **Thumbnail**
- The **Thumbnail Refresher** regenerates **Thumbnails** when the deck first loads and when the **Active Slide** changes

## Example dialogue

> **Dev:** "When the caret moves from slide 2 to slide 3, what regenerates?"
> **Domain expert:** "Slide 3 becomes the Active Slide and renders live. Slide 2 is no longer active, so the Thumbnail Refresher captures a fresh Thumbnail for it — its content has settled."
> **Dev:** "And while I'm typing inside slide 3?"
> **Domain expert:** "Nothing regenerates. Slide 3 is the Active Slide; the Preview Sidebar already shows it live, so its Thumbnail can wait until you leave it."

## Flagged ambiguities

- "current slide" was used for both the **Active Slide** (editor caret) and `DeckController.presentation.currentIndex` (presentation-mode routing) — resolved: these are distinct concepts; **Active Slide** refers only to the editor.
37 changes: 37 additions & 0 deletions packages/playground/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# SuperDeck Studio (playground)

> This package is currently named `playground` but is evolving toward **SuperDeck Studio** — a rename is planned in a future PR.

SuperDeck Studio is a standalone editor and presentation environment built on the SuperDeck framework. It lets users author Markdown slides in a rich text editor, see a live preview as they type, and present directly from the app — all without the CLI pipeline or external file setup.

## How it differs from `demo/`

The root-level `demo/` app exists to test the CLI and custom extension points (styles, templates, widgets). Studio is the product: a self-contained SuperDeck environment for authoring and presenting slides using the base framework implementation.

## Current Features

| Feature | Description |
|---------|-------------|
| **Rich text editor** | Markdown editing with syntax highlighting for headers, `---` separators, and `@block` directives. |
| **Live preview** | Slide thumbnails update in real time as you type. |
| **Presentation mode** | Full-screen takeover route with keyboard navigation (arrows, space, escape). |
| **Theme support** | Follows the system light/dark theme automatically. |

## Planned Features

- Visual controls in the customization sidebar for adjusting slide style, layout, and content options
- Text scaling for previews

## Running

From the repository root:

```bash
cd packages/playground
fvm flutter run -d macos # macOS desktop
fvm flutter run -d chrome # Web
```

## Note

This package is not published (`publish_to: none`). It depends on local `superdeck`, `superdeck_core`, and `superdeck_builder` packages from the monorepo.
2 changes: 2 additions & 0 deletions packages/playground/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
include: package:flutter_lints/flutter.yaml
extends: ../../shared_analysis_options.yaml
3 changes: 3 additions & 0 deletions packages/playground/devtools_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:
Loading
Loading