diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..30db7c8
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,59 @@
+---
+name: 'bug report'
+about: Create a bug report to help improve FilePod.
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+## Describe the Bug
+
+[A clear and concise description of the bug.]
+
+## To Reproduce
+
+Steps to reproduce the behaviour:
+
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+```bash
+$ commands
+...
+```
+
+## Expected Behaviour
+
+[A clear and concise description of what you expected to happen.]
+
+## Screenshots
+
+[If applicable, add screenshots to help explain your problem.]
+
+## Context
+
+Where has the issue been observed:
+
+- [ ] Android
+- [ ] Chrome
+- [ ] iOS
+- [ ] Linux
+- [ ] macOS
+- [ ] Web
+- [ ] Windows
+
+[Add any other context about the problem here.]
+
+App Version:
+Flutter Version:
+
+## Closing Criteria
+
+Checklist for closing the issue:
+
+- [ ] No errors from `make prep`
+- [ ] All tests pass `make qtest`
+- [ ] [Issue specific requirements.]
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..541ef1b
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,32 @@
+---
+name: 'feature request'
+about: Suggest an idea or feature improvement for FilePod.
+title: ''
+labels: ''
+assignees: ''
+---
+
+## Description
+
+[A clear and concise description of the problem or new functionality.]
+
+## Why
+
+So that as a user I can [clear and concise benefit].
+
+## Closing Criteria
+
+Checklist for closing the issue:
+
+- [ ] No errors from `make prep`
+- [ ] All tests pass `make qtest`
+- [ ] [Issue specific requirements.]
+
+## Alternatives
+
+[A clear and concise description of any alternative solutions or
+features you've considered.]
+
+## Additional Context
+
+[Add any other context or screenshots about the feature request here.]
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000..4f42ea9
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,54 @@
+# Pull Request Details
+
+## What issue does this PR address
+
+- [Description]
+
+## Associated Issue
+
+- This PR relates to issue #
+
+## Type of Change
+
+- [ ] Bug fix (non-breaking change which fixes an issue)
+- [ ] New feature (non-breaking change which adds functionality)
+- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
+- [ ] This change requires a documentation update
+
+## How Has This Been Tested?
+
+Please describe the tests that you ran to verify your changes.
+
+## Checklist
+
+Complete the check-list below to ensure your branch is ready for PR.
+
+- [ ] Screenshots included in linked issue #
+- [ ] Changes adhere to the [style and coding guidelines](https://survivor.togaware.com/gnulinux/flutter-style.html)
+- [ ] I have performed a self-review of my code
+- [ ] I have commented my code, particularly in hard-to-understand areas
+- [ ] I have made corresponding changes to the documentation
+- [ ] Any dependent changes have been merged and published in downstream modules
+- [ ] The update contains no confidential information
+- [ ] The update has no duplicated content
+- [ ] No lint check errors are related to these changes (`make prep` or `flutter analyze lib`)
+- [ ] Integration test `dart test` output or screenshot included in issue #
+- [ ] I tested the PR on these devices:
+ - [ ] Android
+ - [ ] iOS
+ - [ ] Linux
+ - [ ] MacOS
+ - [ ] Windows
+ - [ ] Web
+- [ ] I have identified reviewers
+- [ ] The PR has been approved by reviewers
+
+## Finalising
+
+Once PR discussion is complete and reviewers have approved:
+
+- [ ] Merge dev into the this branch
+- [ ] Resolve any conflicts
+- [ ] Add a one line summary into the CHANGELOG.md
+- [ ] Push to the git repository and review
+- [ ] Merge the PR into dev
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
new file mode 100644
index 0000000..c258407
--- /dev/null
+++ b/.github/workflows/ci.yaml
@@ -0,0 +1,133 @@
+name: Lint Checks
+
+on:
+ push:
+ pull_request:
+ types: [opened, reopened, synchronize]
+
+env:
+ FLUTTER_VERSION: '3.38.9'
+
+jobs:
+
+ analyze:
+ runs-on: ubuntu-latest
+ if: github.event.repository.private == false
+ steps:
+ - uses: actions/checkout@v4
+ - uses: subosito/flutter-action@v2
+ with:
+ channel: 'stable'
+ flutter-version: ${{env.FLUTTER_VERSION}}
+ - run: flutter pub get
+ - run: flutter analyze --fatal-infos
+
+ format:
+ runs-on: ubuntu-latest
+ if: github.event.repository.private == false
+ steps:
+ - uses: actions/checkout@v4
+ - uses: subosito/flutter-action@v2
+ with:
+ channel: 'stable'
+ flutter-version: ${{env.FLUTTER_VERSION}}
+ - run: flutter pub get
+ - run: dart format --set-exit-if-changed .
+
+ unused_code:
+ runs-on: ubuntu-latest
+ if: github.event.repository.private == false
+ steps:
+ - uses: actions/checkout@v4
+ - uses: subosito/flutter-action@v2
+ with:
+ channel: 'stable'
+ flutter-version: ${{env.FLUTTER_VERSION}}
+ - run: flutter pub get
+ - run: dart pub global activate dart_code_metrics
+ - run: metrics check-unused-code --disable-sunset-warning lib
+
+ unused_files:
+ runs-on: ubuntu-latest
+ if: github.event.repository.private == false
+ steps:
+ - uses: actions/checkout@v4
+ - uses: subosito/flutter-action@v2
+ with:
+ channel: 'stable'
+ flutter-version: ${{env.FLUTTER_VERSION}}
+ - run: flutter pub get
+ - run: dart pub global activate dart_code_metrics
+ - run: metrics check-unused-files --disable-sunset-warning lib
+
+ import_order:
+ runs-on: ubuntu-latest
+ if: github.event.repository.private == false
+ steps:
+ - uses: actions/checkout@v4
+ - uses: subosito/flutter-action@v2
+ with:
+ channel: 'stable'
+ flutter-version: ${{env.FLUTTER_VERSION}}
+ - run: flutter pub get
+ - run: dart pub global activate import_order_lint
+ - run: import_order --check
+
+ markdown:
+ runs-on: ubuntu-latest
+ if: github.event.repository.private == false
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 18
+ - run: npm install -g markdownlint-cli
+ - run: markdownlint *.md lib assets
+
+ link_checker:
+ runs-on: ubuntu-latest
+ if: github.event.repository.private == false
+ permissions:
+ issues: write
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Link Checker
+ id: lychee
+ uses: lycheeverse/lychee-action@v2
+ with:
+ args:
+ --no-progress
+ '*.md'
+ './**/*.dart'
+ fail: true
+
+ locmax:
+ runs-on: ubuntu-latest
+ if: github.event.repository.private == false
+ steps:
+ - uses: actions/checkout@v4
+ - run: make locmax
+
+ copyright:
+ runs-on: ubuntu-latest
+ if: github.event.repository.private == false
+ steps:
+ - uses: actions/checkout@v4
+ - name: Check copyright headers
+ run: |
+ missing_copyright=$(find lib -type f -name '*.dart' \
+ ! -name '*.g.dart' \
+ ! -name '*.gr.dart' \
+ ! -name '*.freezed.dart' \
+ ! -name '*.chopper.dart' \
+ ! -name '*.part.dart' \
+ ! -name '*.config.dart' \
+ -exec grep -L "Copyright" {} \;)
+ if [ -n "$missing_copyright" ]; then
+ echo "Files missing copyright headers:"
+ echo "$missing_copyright"
+ exit 1
+ else
+ echo "All non-generated Dart files have copyright headers"
+ fi
diff --git a/.gitignore b/.gitignore
index 3150b40..101fa97 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,29 +1,258 @@
-# See https://www.dartlang.org/guides/libraries/private-files
+########################################################################
+# Local ignores
+########################################################################
-# Files and directories created by pub
+!lib
+
+*~
+*.bak
+
+ARCHIVE
+ignore
+pubspec.yaml.*
+
+qtest_*.txt
+
+installers/*.apk
+installers/*.exe
+installers/*.snap
+installers/*.tar.gz
+installers/*-linux.zip
+installers/*-macos.zip
+installers/*-windows.zip
+
+.Rhistory
+
+# Puppeteer browser cache used in testing solidui apps
+
+.local-chrome/
+
+########################################################################
+# From https://github.com/flutter/flutter/blob/master/.gitignore
+# Combined with the github flutter .gitignore
+# Combined with the flutter create .gitignore
+########################################################################
+
+#-----------------------------------------------------------------------
+# Miscellaneous
+#-----------------------------------------------------------------------
+
+*.class
+*.lock
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.build/
+.buildlog/
+.history
+.svn/
+.swiftpm/
+migrate_working_dir/
+
+#-----------------------------------------------------------------------
+# IntelliJ related
+#-----------------------------------------------------------------------
+
+*.iml
+*.ipr
+*.iws
+.idea/
+
+#-----------------------------------------------------------------------
+# Visual Studio Code related
+#-----------------------------------------------------------------------
+
+.vscode/
+
+# 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
+
+#-----------------------------------------------------------------------
+# Flutter repo-specific
+#-----------------------------------------------------------------------
+
+/bin/cache/
+/bin/internal/bootstrap.bat
+/bin/internal/bootstrap.sh
+/bin/mingit/
+/dev/benchmarks/mega_gallery/
+/dev/bots/.recipe_deps
+/dev/bots/android_tools/
+/dev/devicelab/ABresults*.json
+/dev/docs/doc/
+/dev/docs/flutter.docs.zip
+/dev/docs/lib/
+/dev/docs/pubspec.yaml
+/dev/integration_tests/**/xcuserdata
+/dev/integration_tests/**/Pods
+/packages/flutter/coverage/
+version
+analysis_benchmark.json
+
+# packages file containing multi-root paths
+
+.packages.generated
+
+#-----------------------------------------------------------------------
+# Flutter/Dart/Pub related
+#-----------------------------------------------------------------------
+
+**/doc/api/
.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+**/generated_plugin_registrant.dart
.packages
+.pub-cache/
+.pub-preload-cache/
+.pub/
build/
-# If you're building an application, you may want to check-in your pubspec.lock
-pubspec.lock
+flutter_*.png
+linked_*.ds
+unlinked.ds
+unlinked_spec.ds
+
+# If you're building an application, you may want to check-in the
+# pubspec.lock. Libraries should not include pubspec.lock, per
+# https://dart.dev/guides/libraries/private-files#pubspeclock.
-# Directory created by dartdoc
-# If you don't generate documentation locally you can remove this line.
-doc/api/
+/pubspec.lock
-# dotenv environment variables file
-.env*
+#-----------------------------------------------------------------------
+# Avoid committing generated Javascript files.
+#-----------------------------------------------------------------------
-# Avoid committing generated Javascript files:
*.dart.js
-# Produced by the --dump-info flag.
*.info.json
-# When generated by dart2js. Don't specify *.js if your
-# project includes source files written in JavaScript.
*.js
*.js_
*.js.deps
*.js.map
-.flutter-plugins
-.flutter-plugins-dependencies
+#-----------------------------------------------------------------------
+# Android related
+#-----------------------------------------------------------------------
+
+**/android/**/gradle-wrapper.jar
+.gradle/
+**/android/captures/
+**/android/gradlew
+**/android/gradlew.bat
+**/android/local.properties
+**/android/**/GeneratedPluginRegistrant.java
+**/android/key.properties
+*.jks
+
+android/app/.cxx/
+
+#-----------------------------------------------------------------------
+# iOS/XCode related
+#-----------------------------------------------------------------------
+
+**/ios/**/*.mode1v3
+**/ios/**/*.mode2v3
+**/ios/**/*.moved-aside
+**/ios/**/*.pbxuser
+**/ios/**/*.perspectivev3
+**/ios/**/*sync/
+**/ios/**/.sconsign.dblite
+**/ios/**/.tags*
+**/ios/**/.vagrant/
+**/ios/**/DerivedData/
+**/ios/**/Icon?
+**/ios/**/Pods/
+**/ios/**/.symlinks/
+**/ios/**/profile
+**/ios/**/xcuserdata
+**/ios/.generated/
+**/ios/Flutter/.last_build_id
+**/ios/Flutter/App.framework
+**/ios/Flutter/Flutter.framework
+**/ios/Flutter/Flutter.podspec
+**/ios/Flutter/Generated.xcconfig
+**/ios/Flutter/ephemeral
+**/ios/Flutter/app.flx
+**/ios/Flutter/app.zip
+**/ios/Flutter/flutter_assets/
+**/ios/Flutter/flutter_export_environment.sh
+**/ios/ServiceDefinitions.json
+**/ios/Runner/GeneratedPluginRegistrant.*
+
+# ios/macos generated project files
+ios/Flutter/
+ios/Runner.xcodeproj/
+ios/Runner.xcworkspace/
+ios/Runner/
+ios/RunnerTests/
+
+# Backups of ios/macos project config yml and Podfile
+ios/Podfile-*
+ios/project-*.yml
+
+#-----------------------------------------------------------------------
+# macOS
+#-----------------------------------------------------------------------
+
+**/Flutter/ephemeral/
+**/Pods/
+**/macos/Flutter/GeneratedPluginRegistrant.swift
+**/macos/Flutter/ephemeral
+**/xcuserdata/
+
+# ios/macos generated project files
+macos/Runner.xcodeproj/
+macos/Runner.xcworkspace/
+
+# Backups of ios/macos project config yml and Podfile
+macos/Podfile-*
+macos/project-*.yml
+
+#-----------------------------------------------------------------------
+# Windows
+#-----------------------------------------------------------------------
+
+**/windows/flutter/generated_plugin_registrant.cc
+**/windows/flutter/generated_plugin_registrant.h
+**/windows/flutter/generated_plugins.cmake
+
+#-----------------------------------------------------------------------
+# Linux
+#-----------------------------------------------------------------------
+
+**/linux/flutter/generated_plugin_registrant.cc
+**/linux/flutter/generated_plugin_registrant.h
+**/linux/flutter/generated_plugins.cmake
+**/linux/runner
+
+#-----------------------------------------------------------------------
+# Coverage
+#-----------------------------------------------------------------------
+
+coverage/
+
+#-----------------------------------------------------------------------
+# Symbols
+#-----------------------------------------------------------------------
+
+app.*.symbols
+
+#-----------------------------------------------------------------------
+# Exceptions to above rules.
+#-----------------------------------------------------------------------
+
+!**/ios/**/default.mode1v3
+!**/ios/**/default.mode2v3
+!**/ios/**/default.pbxuser
+!**/ios/**/default.perspectivev3
+!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
+!/dev/ci/**/Gemfile.lock
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..7462cad
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,13 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+## 0.0.1
+
+- Initial release of FilePod.
+- Solid POD file browser with upload and download functionality.
+- Authentication via SolidLogin.
+- Responsive navigation with rail and drawer modes.
+- Theme switching (light/dark/system).
+- Security key management integration.
+- All POD Files view for browsing from the root.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..c0bfacf
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,91 @@
+########################################################################
+#
+# Makefile for FilePod
+#
+# Copyright (c) Graham.Williams@togaware.com
+#
+# License: Creative Commons Attribution-ShareAlike 4.0 International.
+#
+########################################################################
+
+# App is often the current directory name.
+
+APP=$(shell pwd | xargs basename)
+VER = $(shell egrep '^version:' pubspec.yaml | cut -d' ' -f2 | cut -d'+' -f1)
+DATE=$(shell date +%Y-%m-%d)
+
+DEST=/var/www/html/$(APP)
+
+REPO=solidcommunity.au
+RLOC=/var/www/html/installers/
+DWLD=https://$(REPO)/installers/
+
+########################################################################
+# Supported Makefile modules.
+
+INC_BASE=support
+
+INC_DOCKER=skip
+INC_MLHUB=skip
+INC_WEBCAM=skip
+
+INC_MODULE=$(INC_BASE)/modules.mk
+
+ifneq ("$(wildcard $(INC_MODULE))","")
+ include $(INC_MODULE)
+endif
+
+########################################################################
+# HELP
+
+define HELP
+$(APP):
+
+ ginstall After a github build download bundles and upload to $(REPO)
+
+ local Install to $(HOME)/.local/share/$(APP)
+ tgz Upload the installer to $(REPO)
+ apk Upload the installer to $(REPO)
+
+endef
+export HELP
+
+help::
+ @echo "$$HELP"
+
+########################################################################
+# LOCAL TARGETS
+
+clean::
+ rm -f README.html
+
+local: tgz
+ tar zxvf installers/$(APP).tar.gz -C $(HOME)/.local/share/
+
+tgz::
+ chmod a+r installers/$(APP)*.tar.gz
+ rsync -avzh installers/$(APP)*.tar.gz $(REPO):/var/www/html/installers/
+ ssh $(REPO) chmod -R go+rX /var/www/html/installers/
+ ssh $(REPO) chmod go=x /var/www/html/installers/
+
+apk::
+ rsync -avzh installers/$(APP).apk $(REPO):$(RLOC)
+ ssh $(REPO) chmod a+r $(RLOC)$(APP).apk
+ mv -f installers/$(APP)-*.apk installers/ARCHIVE/
+ rm -f installers/$(APP).apk
+ @echo ''
+
+appbundle::
+ rsync -avzh installers/$(APP).aab $(REPO):$(RLOC)
+ ssh $(REPO) chmod a+r $(RLOC)$(APP).aab
+ mv -f installers/$(APP)-*.aab installers/ARCHIVE/
+ rm -f installers/$(APP).aab
+ @echo ''
+
+.PHONY: upload
+upload:
+ (cd installers; make ginstall)
+ @echo ''
+
+.PHONY: ginstall
+ginstall: upload apk appbundle
diff --git a/README.md b/README.md
index ef2d165..0f198d0 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,255 @@
-# filepod
\ No newline at end of file
+
+
+# FilePod
+
+[](https://flutter.dev)
+[](https://dart.dev)
+
+[](https://raw.githubusercontent.com/anusii/filepod/dev/LICENSE)
+[](https://github.com/anusii/filepod/blob/main/CHANGELOG.md)
+[](https://github.com/anusii/filepod/commits/dev/)
+[](https://github.com/anusii/filepod/commits/dev/)
+[](https://github.com/anusii/filepod/issues)
+
+A [Solid](https://solidproject.org) file browser application built
+with [Flutter](https://flutter.dev) and
+[SolidUI](https://github.com/anusii/solidui). FilePod allows users to
+browse, upload, and download files stored on their personal online
+data store ([POD](https://solidproject.org/about)), providing a
+familiar file-management experience for the decentralised web.
+
+See the [AU Solid Community](https://solidcommunity.au) page for apps
+utilising the Solid ecosystem.
+
+## Table of Contents
+
+- [Features](#features)
+- [Screenshots](#screenshots)
+- [Requirements](#requirements)
+- [Installation](#installation)
+- [Getting Started](#getting-started)
+- [Application Structure](#application-structure)
+- [Contributing](#contributing)
+- [Licence](#licence)
+
+## Features
+
+- **Solid POD File Browsing** — Navigate folders and files stored on
+ your Solid POD with an intuitive, responsive interface.
+
+- **File Upload** — Upload files from your local device directly to
+ your POD.
+
+- **File Download** — Download files from your POD to your local
+ device.
+
+- **All POD Files View** — Browse every folder and file on your POD
+ from the root, giving a complete overview of your stored data.
+
+- **Solid Authentication** — Secure login against a Solid server
+ using the `SolidLogin` widget.
+
+- **Security Key Management** — Manage encryption keys for private
+ data stored on your POD.
+
+- **Responsive Navigation** — Automatically switches between a
+ vertical navigation rail (wide screens) and a collapsible navigation
+ drawer (narrow screens).
+
+- **Theme Switching** — Toggle between light, dark, and system theme
+ modes.
+
+- **Cross-Platform** — Runs on macOS, Linux, Windows, Android, iOS,
+ and the Web.
+
+## Screenshots
+
+
+

+
+

+
+
+## Requirements
+
+- Flutter SDK: `>=3.10.0`
+- Dart SDK: `>=3.0.0 <4.0.0`
+
+### Dependencies
+
+FilePod relies on the following key packages:
+
+- [`solidui`](https://github.com/anusii/solidui) — UI components for
+ Solid applications
+- [`solidpod`](https://github.com/anusii/solidpod) — Solid POD
+ integration
+- `shared_preferences` — Local storage for settings
+- `markdown_tooltip` — Markdown-enabled tooltips
+- `window_manager` — Desktop window management
+
+## Installation
+
+### Clone the Repository
+
+```bash
+git clone https://github.com/anusii/filepod.git
+cd filepod
+```
+
+### Install Dependencies
+
+```bash
+flutter pub get
+```
+
+## Getting Started
+
+### Running the Application
+
+```bash
+# macOS
+flutter run -d macos
+
+# Linux
+flutter run -d linux
+
+# Windows
+flutter run -d windows
+
+# Web
+flutter run -d chrome
+```
+
+### Using the Makefile
+
+A `Makefile` is provided for common development tasks:
+
+```bash
+# Run on macOS
+make macos
+
+# Run on Linux
+make linux
+
+# Run code analysis
+make analyze
+
+# Format code
+make format
+
+# Full preparation for a pull request
+make prep
+```
+
+## Application Structure
+
+```text
+lib/
+├── main.dart # Main entry point
+├── app.dart # Root App widget with SolidThemeApp and SolidLogin
+├── app_scaffold.dart # SolidScaffold configuration (menu, appBar, statusBar)
+├── home.dart # Home page widget
+├── constants/
+│ └── app.dart # Application-wide constants
+├── screens/
+│ └── all_pod_files_page.dart # Browse all POD files from root
+└── utils/
+ └── is_desktop.dart # Desktop platform detection utility
+```
+
+### Key Components
+
+#### `main.dart`
+
+Application entry point. Initialises Flutter bindings, configures
+the window manager for desktop platforms, and launches the `App()`
+widget.
+
+#### `app.dart`
+
+Root widget implementing `SolidThemeApp` with theme configuration
+and `SolidLogin` for Solid server authentication. After login, the
+`AppScaffold` is displayed.
+
+#### `app_scaffold.dart`
+
+Configures the `SolidScaffold` with:
+
+- **Menu items** — Home, Files, and All POD Files navigation
+- **App bar** — Title, version information, and file browser action
+- **Status bar** — Server info, login status, and security key status
+- **About dialogue** — Application information and links
+- **Theme toggle** — Light/dark/system mode switching
+- **Logout** — Secure session termination
+
+#### `home.dart`
+
+Welcome page displaying a feature overview and usage guidance.
+
+#### `screens/all_pod_files_page.dart`
+
+A dedicated page that browses all folders and files on the POD
+from the root, providing a complete view of the user's stored data.
+
+## Contributing
+
+We welcome contributions! Please follow these guidelines:
+
+1. **Fork** the repository and create a feature branch from `dev`.
+2. **Follow** the [coding style
+ guidelines](https://survivor.togaware.com/gnulinux/flutter-style.html).
+3. **Run** `make prep` before submitting a pull request.
+4. **Submit** a pull request using the provided
+ [PR template](.github/pull_request_template.md).
+
+### Reporting Issues
+
+- **Bug reports**: Use the [bug report
+ template](.github/ISSUE_TEMPLATE/bug_report.md).
+- **Feature requests**: Use the [feature request
+ template](.github/ISSUE_TEMPLATE/feature_request.md).
+
+### Development Setup
+
+```bash
+git clone https://github.com/anusii/filepod.git
+cd filepod
+flutter pub get
+flutter run -d macos
+```
+
+## Licence
+
+Copyright (C) 2026, Software Innovation Institute, ANU.
+
+Licensed under the GNU General Public License, Version 3.
+See [LICENSE](LICENSE) for details.
+
+## Authors
+
+- Graham Williams
+- Tony Chen
+
+For more information about Solid and PODs, visit
+[solidproject.org](https://solidproject.org).
+
+## Additional Information
+
+The source code can be accessed via the [GitHub
+repository](https://github.com/anusii/filepod). You can also file
+issues at [GitHub Issues](https://github.com/anusii/filepod/issues).
+The authors of the package will respond to issues as best we can.
+
+
+*Time-stamp: *
+
+
+
+[comment]: # (Local Variables:)
+[comment]: # (time-stamp-line-limit: -8)
+[comment]: # (End:)
+
diff --git a/analysis_options.yaml b/analysis_options.yaml
new file mode 100644
index 0000000..9880a29
--- /dev/null
+++ b/analysis_options.yaml
@@ -0,0 +1,20 @@
+# Configure the analyser for static analysis of Dart code.
+#
+# Invoke the analyser from the command line:
+#
+# flutter analyze
+
+include: package:flutter_lints/flutter.yaml
+
+linter:
+ rules:
+ avoid_print: true
+ prefer_single_quotes: true
+ require_trailing_commas: true
+ directives_ordering: false
+ prefer_const_constructors: true
+
+analyzer:
+ exclude:
+ - ignore/**
+ - ignore/
diff --git a/assets/images/app_icon.png b/assets/images/app_icon.png
new file mode 100644
index 0000000..bb0378a
Binary files /dev/null and b/assets/images/app_icon.png differ
diff --git a/assets/images/app_image.jpg b/assets/images/app_image.jpg
new file mode 100644
index 0000000..ef47d69
Binary files /dev/null and b/assets/images/app_image.jpg differ
diff --git a/lib/app.dart b/lib/app.dart
new file mode 100644
index 0000000..fedabec
--- /dev/null
+++ b/lib/app.dart
@@ -0,0 +1,54 @@
+/// The primary App widget.
+///
+/// Copyright (C) 2026, Software Innovation Institute, ANU.
+///
+/// Licensed under the GNU General Public License, Version 3 (the "License");
+///
+/// License: https://www.gnu.org/licenses/gpl-3.0.html
+//
+// This program is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free Software
+// Foundation, either version 3 of the License, or (at your option) any later
+// version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+// details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program. If not, see .
+///
+/// Authors: Tony Chen
+
+library;
+
+import 'package:flutter/material.dart';
+
+import 'package:solidui/solidui.dart';
+
+import 'app_scaffold.dart';
+import 'constants/app.dart';
+
+class App extends StatelessWidget {
+ const App({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return SolidThemeApp(
+ debugShowCheckedModeBanner: false,
+ title: appTitle,
+ theme: ThemeData(
+ colorScheme: ColorScheme.fromSeed(
+ seedColor: const Color(0xFF007AFF),
+ ),
+ useMaterial3: true,
+ ),
+ home: const SolidLogin(
+ image: AssetImage('assets/images/app_image.jpg'),
+ logo: AssetImage('assets/images/app_icon.png'),
+ child: appScaffold,
+ ),
+ );
+ }
+}
diff --git a/lib/app_scaffold.dart b/lib/app_scaffold.dart
new file mode 100644
index 0000000..7ea58ae
--- /dev/null
+++ b/lib/app_scaffold.dart
@@ -0,0 +1,151 @@
+/// The application scaffold configuration.
+///
+/// Copyright (C) 2026, Software Innovation Institute, ANU.
+///
+/// Licensed under the GNU General Public License, Version 3 (the "License");
+///
+/// License: https://www.gnu.org/licenses/gpl-3.0.html
+//
+// This program is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free Software
+// Foundation, either version 3 of the License, or (at your option) any later
+// version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+// details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program. If not, see .
+///
+/// Authors: Tony Chen
+
+library;
+
+import 'package:flutter/material.dart';
+
+import 'package:solidui/solidui.dart';
+
+import 'constants/app.dart';
+import 'home.dart';
+import 'screens/all_pod_files_page.dart';
+
+final _scaffoldController = SolidScaffoldController();
+
+const appScaffold = AppScaffold();
+
+class AppScaffold extends StatelessWidget {
+ const AppScaffold({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+
+ return SolidScaffold(
+ controller: _scaffoldController,
+ menu: const [
+ SolidMenuItem(
+ icon: Icons.home,
+ title: 'Home',
+ tooltip: '''
+
+ **Home:** Tap here to return to the main page for the app.
+
+ ''',
+ child: Home(title: appTitle),
+ ),
+ SolidMenuItem(
+ icon: Icons.folder,
+ title: 'Files',
+ tooltip: '''
+
+ **Files:** Tap here to browse the files on your POD.
+
+ ''',
+ child: SolidFile(),
+ ),
+ SolidMenuItem(
+ icon: Icons.storage,
+ title: 'All POD Files',
+ tooltip: '''
+
+ **All POD Files:** Tap here to browse all folders on your POD
+ from the root.
+
+ ''',
+ child: AllPodFilesPage(),
+ ),
+ ],
+ appBar: SolidAppBarConfig(
+ title: appTitle.split(' - ')[0],
+ versionConfig: SolidVersionConfig(
+ changelogUrl: 'https://github.com/anusii/filepod/blob/main/'
+ 'CHANGELOG.md',
+ showDate: true,
+ userTextStyle: TextStyle(
+ color: theme.colorScheme.onSurface,
+ ),
+ ),
+ actions: [
+ SolidAppBarAction(
+ icon: Icons.folder,
+ onPressed: () => _scaffoldController.navigateToSubpage(
+ const SolidFile(),
+ ),
+ tooltip: 'Files',
+ ),
+ ],
+ ),
+ statusBar: const SolidStatusBarConfig(
+ serverInfo: SolidServerInfo(serverUri: SolidConfig.defaultServerUrl),
+ loginStatus: SolidLoginStatus(),
+ securityKeyStatus: SolidSecurityKeyStatus(),
+ ),
+ aboutConfig: SolidAboutConfig(
+ applicationName: appTitle.split(' - ')[0],
+ applicationIcon: Image.asset(
+ 'assets/images/app_icon.png',
+ width: 64,
+ height: 64,
+ ),
+ applicationLegalese: '''
+
+ © 2026 Software Innovation Institute, the Australian National University
+
+ ''',
+ text: '''
+
+ FilePod is a Solid file browser application that allows you
+ to manage files on your personal online data store (POD).
+
+ Key features:
+
+ 📂 Browse and manage files on your Solid POD;
+
+ 📤 Upload files to your POD;
+
+ 📥 Download files from your POD;
+
+ 🔐 Security key management for encrypted data;
+
+ 🎨 Theme switching (light/dark/system);
+
+ 🧭 Responsive navigation (rail ↔ drawer).
+
+ For more information, visit the
+ [FilePod](https://github.com/anusii/filepod) GitHub repository and our
+ [Australian Solid Community](https://solidcommunity.au) web site.
+
+ ''',
+ ),
+ themeToggle: const SolidThemeToggleConfig(
+ enabled: true,
+ showInAppBarActions: true,
+ ),
+ hideNavRail: false,
+ onLogout: (context) => SolidAuthHandler.instance.handleLogout(context),
+ child: const Home(title: appTitle),
+ );
+ }
+}
diff --git a/lib/constants/app.dart b/lib/constants/app.dart
new file mode 100644
index 0000000..a616c31
--- /dev/null
+++ b/lib/constants/app.dart
@@ -0,0 +1,26 @@
+/// App-wide constants.
+///
+/// Copyright (C) 2026, Software Innovation Institute, ANU.
+///
+/// Licensed under the GNU General Public License, Version 3 (the "License");
+///
+/// License: https://www.gnu.org/licenses/gpl-3.0.html
+//
+// This program is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free Software
+// Foundation, either version 3 of the License, or (at your option) any later
+// version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+// details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program. If not, see .
+///
+/// Authors: Tony Chen
+
+library;
+
+const String appTitle = 'FilePod - Solid File Browser';
diff --git a/lib/home.dart b/lib/home.dart
new file mode 100644
index 0000000..ed9a1e7
--- /dev/null
+++ b/lib/home.dart
@@ -0,0 +1,83 @@
+/// The application's home page.
+///
+/// Copyright (C) 2026, Software Innovation Institute, ANU.
+///
+/// Licensed under the GNU General Public License, Version 3 (the "License");
+///
+/// License: https://www.gnu.org/licenses/gpl-3.0.html
+//
+// This program is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free Software
+// Foundation, either version 3 of the License, or (at your option) any later
+// version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+// details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program. If not, see .
+///
+/// Authors: Tony Chen
+
+library;
+
+import 'package:flutter/material.dart';
+
+class Home extends StatefulWidget {
+ const Home({super.key, required this.title});
+
+ final String title;
+
+ @override
+ State createState() => _HomeState();
+}
+
+class _HomeState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return SingleChildScrollView(
+ padding: const EdgeInsets.all(24.0),
+ child: Center(
+ child: Card(
+ child: Padding(
+ padding: const EdgeInsets.all(32.0),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Icon(
+ Icons.folder_open,
+ size: 64,
+ color: Theme.of(context).colorScheme.primary,
+ ),
+ const SizedBox(height: 16),
+ Text(
+ widget.title,
+ style: Theme.of(context).textTheme.headlineMedium,
+ ),
+ const SizedBox(height: 24),
+ Text(
+ 'Welcome to FilePod!\n\n'
+ 'FilePod is a Solid file browser that lets you manage '
+ 'files on your personal online data store (POD).\n\n'
+ 'Key features:\n\n'
+ '• Browse files and folders on your Solid POD\n'
+ '• Upload files to your POD\n'
+ '• Download files from your POD\n'
+ '• View all POD files from the root\n'
+ '• Security key management for encrypted data\n'
+ '• Theme switching (light/dark/system)\n'
+ '• Responsive navigation (rail ↔ drawer)\n\n'
+ 'Use the navigation menu to explore your POD files!',
+ style: Theme.of(context).textTheme.bodyLarge,
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/main.dart b/lib/main.dart
new file mode 100644
index 0000000..d1026cd
--- /dev/null
+++ b/lib/main.dart
@@ -0,0 +1,55 @@
+/// FilePod - Solid File Browser Application
+///
+/// Copyright (C) 2026, Software Innovation Institute, ANU.
+///
+/// Licensed under the GNU General Public License, Version 3 (the "License");
+///
+/// License: https://www.gnu.org/licenses/gpl-3.0.html
+//
+// This program is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free Software
+// Foundation, either version 3 of the License, or (at your option) any later
+// version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+// details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program. If not, see .
+///
+/// Authors: Tony Chen
+
+library;
+
+import 'package:flutter/material.dart';
+
+import 'package:window_manager/window_manager.dart';
+
+import 'app.dart';
+import 'constants/app.dart';
+import 'utils/is_desktop.dart';
+
+void main() async {
+ WidgetsFlutterBinding.ensureInitialized();
+
+ if (isDesktop) {
+ await windowManager.ensureInitialized();
+
+ const windowOptions = WindowOptions(
+ title: appTitle,
+ minimumSize: Size(500, 800),
+ backgroundColor: Colors.transparent,
+ skipTaskbar: false,
+ titleBarStyle: TitleBarStyle.normal,
+ );
+
+ await windowManager.waitUntilReadyToShow(windowOptions, () async {
+ await windowManager.show();
+ await windowManager.focus();
+ });
+ }
+
+ runApp(const App());
+}
diff --git a/lib/screens/all_pod_files_page.dart b/lib/screens/all_pod_files_page.dart
new file mode 100644
index 0000000..f31ebd5
--- /dev/null
+++ b/lib/screens/all_pod_files_page.dart
@@ -0,0 +1,40 @@
+/// All POD Files page - Displays all folders on the POD from the root.
+///
+/// Copyright (C) 2026, Software Innovation Institute, ANU.
+///
+/// Licensed under the GNU General Public License, Version 3 (the "License");
+///
+/// License: https://www.gnu.org/licenses/gpl-3.0.html
+//
+// This program is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free Software
+// Foundation, either version 3 of the License, or (at your option) any later
+// version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+// details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program. If not, see .
+///
+/// Authors: Tony Chen
+
+library;
+
+import 'package:flutter/material.dart';
+
+import 'package:solidui/solidui.dart';
+
+class AllPodFilesPage extends StatelessWidget {
+ const AllPodFilesPage({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return const SolidFile(
+ currentPath: SolidFile.podRoot,
+ friendlyFolderName: 'All POD Files',
+ );
+ }
+}
diff --git a/lib/utils/is_desktop.dart b/lib/utils/is_desktop.dart
new file mode 100644
index 0000000..3b3040f
--- /dev/null
+++ b/lib/utils/is_desktop.dart
@@ -0,0 +1,34 @@
+/// Check if we are running a desktop (and not a browser).
+///
+/// Copyright (C) 2026, Software Innovation Institute, ANU.
+///
+/// Licensed under the GNU General Public License, Version 3 (the "License");
+///
+/// License: https://www.gnu.org/licenses/gpl-3.0.html
+//
+// This program is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free Software
+// Foundation, either version 3 of the License, or (at your option) any later
+// version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+// details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program. If not, see .
+///
+/// Authors: Tony Chen
+
+library;
+
+import 'dart:io' show Platform;
+
+import 'package:flutter/foundation.dart' show kIsWeb;
+
+bool get isDesktop {
+ if (kIsWeb) return false;
+
+ return Platform.isLinux || Platform.isMacOS || Platform.isWindows;
+}
diff --git a/license.dart b/license.dart
new file mode 100644
index 0000000..49506f7
--- /dev/null
+++ b/license.dart
@@ -0,0 +1,27 @@
+/// FilePod - A Solid File Browser Application.
+///
+/// Copyright (C) 2026, Software Innovation Institute, ANU.
+///
+/// Licensed under the GNU General Public License, Version 3 (the "License");
+///
+/// License: https://www.gnu.org/licenses/gpl-3.0.html
+//
+// This program is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free Software
+// Foundation, either version 3 of the License, or (at your option) any later
+// version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+// details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program. If not, see .
+///
+/// Authors: AUTHORS
+
+// Add the library directive as we have doc entries above. We publish the above
+// meta doc lines in the docs.
+
+library;
diff --git a/pubspec.yaml b/pubspec.yaml
new file mode 100644
index 0000000..815d169
--- /dev/null
+++ b/pubspec.yaml
@@ -0,0 +1,37 @@
+name: filepod
+description: >-
+ FilePod - A Solid File Browser Application for managing files
+ on your personal online data store (POD).
+publish_to: 'none'
+version: 0.0.1+1
+
+environment:
+ sdk: '>=3.0.0 <4.0.0'
+ flutter: ">=3.10.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+ markdown_tooltip: 0.0.10
+ shared_preferences: ^2.5.4
+ solidpod: ^0.10.1
+ solidui:
+ git:
+ url: https://github.com/anusii/solidui.git
+ ref: dev
+ window_manager: ^0.5.1
+
+dependency_overrides:
+ solidpod:
+ git:
+ url: https://github.com/anusii/solidpod.git
+ ref: dev
+
+dev_dependencies:
+ flutter_lints: ^6.0.0
+
+flutter:
+ uses-material-design: true
+
+ assets:
+ - assets/images/
diff --git a/support/flutter.mk b/support/flutter.mk
new file mode 100644
index 0000000..ac54e78
--- /dev/null
+++ b/support/flutter.mk
@@ -0,0 +1,551 @@
+########################################################################
+#
+# Makefile template for Flutter
+#
+# Copyright 2021-2025 (c) Graham.Williams@togaware.com
+#
+# License: Creative Commons Attribution-ShareAlike 4.0 International.
+#
+########################################################################
+
+# App version numbers
+# Major release
+# Minor update
+# Trivial update or bug fix
+
+ifeq ($(VER),)
+ VER = $(if $(wildcard pubspec.yaml),$(shell egrep '^version:' pubspec.yaml | cut -d' ' -f2),)
+endif
+
+define FLUTTER_HELP
+flutter:
+
+ android Run with an attached Android device;
+ chrome Run with the chrome device;
+ emu Run with the android emulator;
+ linux Run with the linux device;
+ qlinux Run with the linux device and debugPrint() turned off;
+ macos Run with the macos device;
+
+ prep Prep for PR by running tests, checks, docs.
+ push Do a git push and bump the build number if there is one.
+
+ docs Run `dart doc` to create documentation.
+
+ import_order Run import order checking.
+ import_order_fix Run import order fixing.
+
+ pubspec Choose actual/local pubspec using meld.
+ pubspec.local Overwrite with local pubspec.
+ pubspec.actual Overwrite with actual pubspec.
+
+ fix Run `dart fix --apply`.
+ format Run `dart format`.
+ analyze Run `flutter analyze`.
+ depend Run `dart run dependency_validator`.
+ ignore Look for usage of ignore directives.
+ license Look for missing top license in source code.
+
+ test Run flutter testing.
+ itest Run flutter interation testing.
+ qtest Run above test with PAUSE=0.
+ qtest.all Run qtest with output redirected - good running all tests.
+ coverage Run with `--coverage`.
+ coview View the generated html coverage in browser.
+
+ riverpod Setup `pubspec.yaml` to support riverpod.
+ runner Build the auto generated code as *.g.dart files.
+
+ desktops Set up for all desktop platforms (linux, windows, macos)
+
+ distributions
+ apk Builds installers/$(APP).apk.
+ tgz Builds installers/$(APP).tar.gz.
+ dmg-unsigned Builds unsigned macos app.
+ dmg-dev Builds macos app with development certificate.
+ dmg-staging Builds macos app with distribution certificate.
+ (TODO convert to dmg).
+
+ publish Publish a package to pub.dev
+
+Also supported:
+
+ *.itest
+ *.qtest
+
+endef
+export FLUTTER_HELP
+
+help::
+ @echo "$$FLUTTER_HELP"
+
+TICK=\033[0;32m✔\033[0m
+CROSS=\033[31m❌\033[0m
+
+DART_CODE=lib \
+ $(if $(wildcard test/),test) \
+ $(if $(wildcard integration_test/),integration_test)
+
+# Cater for the case where the support folder is one directory up.
+
+LOC := $(shell if [ -f support/loc.sh ]; then echo support/loc.sh; \
+ elif [ -f ../support/loc.sh ]; then echo ../support/loc.sh; fi)
+
+.PHONY: chrome
+chrome:
+ flutter run -d chrome --release
+
+# 20220503 gjw The following fails if the target files already exist -
+# just needs to be run once.
+#
+# dart run build_runner build --delete-conflicting-outputs
+#
+# List the files that are automatically generated. Then they will get
+# built as required.
+
+# BUILD_RUNNER = \
+# lib/models/synchronise_time.g.dart
+
+# $(BUILD_RUNNER):
+# dart run build_runner build --delete-conflicting-outputs
+
+pubspec.lock:
+ flutter pub get
+
+.PHONY: upgrade
+upgrade:
+ flutter pub upgrade
+
+.PHONY: linux
+linux: pubspec.lock $(BUILD_RUNNER) upgrade
+ flutter run --device-id linux
+
+# Turn off debugPrint() output.
+
+.PHONY: qlinux
+qlinux: pubspec.lock $(BUILD_RUNNER) upgrade
+ flutter run --dart-define DEBUG_PRINT="FALSE" --device-id linux
+
+.PHONY: macos
+macos: $(BUILD_RUNNER) upgrade
+ flutter run --device-id macos
+
+.PHONY: android
+android: $(BUILD_RUNNER) upgrade
+ flutter run --device-id $(shell flutter devices | grep android | tr '•' '|' | tr -s '|' | tr -s ' ' | cut -d'|' -f2 | tr -d ' ')
+
+.PHONY: emu
+emu:
+ @if [ -n "$(shell flutter devices | grep emulator | cut -d" " -f 6)" ]; then \
+ flutter run --device-id $(shell flutter devices | grep emulator | cut -d" " -f 6); \
+ else \
+ flutter emulators --launch Pixel_3a_API_30; \
+ echo "Emulator has been started. Rerun `make emu` to build the app."; \
+ fi
+
+.PHONY: linux_config
+linux_config:
+ flutter config --enable-linux-desktop
+
+.PHONY: prep
+prep: analyze fix import_order_fix format dcm ignore license todo locgo markdown lychee depend bakfind
+ @echo "ADVISORY: make test tests docs"
+ @echo $(SEPARATOR)
+
+.PHONY: docs
+docs::
+ dart doc
+ chmod -R go+rX doc
+
+SEPARATOR="------------------------------------------------------------------------"
+
+.PHONY: pubspec
+pubspec:
+ meld pubspec.yaml.actual pubspec.yaml pubspec.yaml.local
+
+.PHONY: pubspec.local
+pubspec.local:
+ cp --backup pubspec.yaml pubspec.yaml.actual
+ cp --backup pubspec.yaml.local pubspec.yaml
+
+.PHONY: pubspec.actual
+pubspec.actual:
+ cp --backup pubspec.yaml.actual pubspec.yaml
+
+.PHONY: fix
+fix:
+ @echo "Dart: FIX"
+ dart fix --apply
+ @echo $(SEPARATOR)
+
+.PHONY: format
+format:
+ @echo "Dart: FORMAT"
+ dart format lib/ $(if $(shell test -d example && echo yes),example/)
+ @echo $(SEPARATOR)
+
+# My emacs IDE is starting to add imports of backups automagically!
+
+.PHONY: bakfind
+bakfind:
+ @echo "Find imports of backups.\n"
+ @-! find lib -type f -name '*.dart' -exec grep '\.dart\.~\([0-9]\)~' {} +
+ @echo $(SEPARATOR)
+
+.PHONY: bakfix
+bakfix:
+ @echo "Find and fix imports of backups."
+ find lib -type f -name '*.dart*' -exec sed -i 's/\.dart\.~\([0-9]\)~/\.dart/g' {} +
+ @echo $(SEPARATOR)
+
+.PHONY: tests
+tests:: test qtest
+
+.PHONY: analyze
+analyze:
+ @echo "Futter ANALYZE"
+ -flutter analyze
+# dart run custom_lint
+ @echo $(SEPARATOR)
+
+# dart pub global activate dependency_validator
+
+.PHONY: depend
+depend:
+ @echo "Dart: REVIEW DEPENDENCIES."
+ -dependency_validator
+ @echo $(SEPARATOR)
+
+# Check and fail if any files exceed limit.
+#
+# 20260115 gjw We utilise two targets both running locbase. The target
+# `locgo` ignores failure of the max loc check and is used in the
+# `prep` target above to ensure all tests are undertaken. It is
+# wrapped in the common echos for the `prep` workflow. The main target
+# `locmax` is used in the CI to fail on too many lines of code, and
+# thus fails the lint checking.
+
+LINES ?= 301
+
+.PHONY: locmax
+locmax:
+ @loc=$$(bash $(LOC) -t $(shell find lib -name '*.dart')); \
+ totl=$$(cat $(shell find lib -name '*.dart') | wc -l); \
+ numf=$$(find lib -name "*.dart" -type f | wc -l); \
+ output=$$(bash $(LOC) -n $(LINES) $(shell find lib -name '*.dart') | sort -nr); \
+ locm=$$(echo $$output | wc -w | awk '{print $$1/2}'); \
+ if [ -n "$$output" ]; then \
+ echo "$$output"; \
+ echo "\nTotal $$loc lines of code across $$numf files with total $$totl lines."; \
+ echo "\n$(CROSS) Error: Found $$locm files with more than $(LINES) lines of code."; \
+ exit 1; \
+ else \
+ echo "Total $$loc lines of code across $$numf files with total $$totl lines."; \
+ echo "\n$(TICK) All files are under $(LINES) lines."; \
+ fi
+
+.PHONY: locgo
+locgo:
+ @echo "Files with EXCESS LINES OF CODE:\n"
+ @-make --no-print-directory locmax
+ @echo $(SEPARATOR)
+
+# dart pub global activate dependency_validator
+
+.PHONY: markdown
+markdown:
+ @echo "Markdown: MARKDOWN FORMAT CHECK."
+ -markdownlint *.md lib assets installers
+ @echo
+ @echo $(SEPARATOR)
+
+.PHONY: ignore
+ignore:
+ @echo "Files that override lint checks with IGNORE:\n"
+ @-if grep -r -n 'ignore: ' lib; then exit 1; else exit 0; fi
+ @echo $(SEPARATOR)
+
+.PHONY: todo
+todo:
+ @echo "Files that include TODO items to be resolved:\n"
+ @-if grep -r -n ' TODO ' lib; then echo; exit 1; else exit 0; fi
+ @echo $(SEPARATOR)
+
+.PHONY: license
+license:
+ @echo "Files without a LICENSE:\n"
+ @-output=$$(find lib -type f -not -name '*~' -not -name 'README*' -not -name '*.g.dart' \
+ ! -exec grep -qE '^(///? Copyright|///? Licensed)' {} \; -print | xargs printf "\t%s\n"); \
+ if [ $$(echo "$$output" | wc -w) -ne 0 ]; then \
+ echo "$$output"; \
+ echo "\n$(CROSS) Error: Files with no license found."; \
+ exit 1; \
+ else \
+ echo "$(TICK) All source files contain a license."; \
+ fi
+ @echo $(SEPARATOR)
+
+.PHONY: riverpod
+riverpod:
+ flutter pub add flutter_riverpod
+ flutter pub add riverpod_annotation
+ flutter pub add dev:riverpod_generator
+ flutter pub add dev:build_runner
+ flutter pub add dev:custom_lint
+ flutter pub add dev:riverpod_lint
+
+.PHONY: runner
+runner:
+ dart run build_runner build
+
+# Support desktop platforms: Linux, MacOS and Windows. Using the
+# project name as in the already existant pubspec.yaml ensures the
+# project name is a valid name. Otherwise it is obtained from the
+# folder name and may not necessarily be a valid flutter project name.
+
+.PHONY: desktops
+desktops:
+ flutter create --platforms=windows,macos,linux --project-name $(shell grep 'name: ' pubspec.yaml | awk '{print $$2}') .
+
+########################################################################
+# INTEGRATION TESTING
+#
+# Run the integration tests for the desktop device (linux, windows,
+# macos). Without this explictly specified, if I have my android
+# device connected to the computer then the testing defaults to trying
+# to install on android. 20230713 gjw
+
+.PHONY: test
+test:
+ @echo "Unit TEST:"
+ @-if [ -d test ]; then flutter test; else echo "\nNo test folder found."; fi
+ @echo $(SEPARATOR)
+
+# For a specific interactive test we think of it as providing a
+# demonstration of the app functionality that we may actually use to
+# create a narrated video. A INTERACT of 5 or more is then useful.
+
+%.itest:
+ @case "$$(uname -s)" in \
+ Linux*) device_id="linux" ;; \
+ Darwin*) device_id="macos" ;; \
+ MINGW*|MSYS*|CYGWIN*) device_id="windows" ;; \
+ *) echo "Unsupported platform: $$(uname -s)"; exit 1 ;; \
+ esac; \
+ flutter test --dart-define=INTERACT=5 --device-id $$device_id integration_test/$*.dart
+
+# For a run over all tests interactively we INTERACT a little but not as
+# much as when running the individual tests.
+
+.PHONY: itest
+itest:
+ @case "$$(uname -s)" in \
+ Linux*) device_id="linux" ;; \
+ Darwin*) device_id="macos" ;; \
+ MINGW*|MSYS*|CYGWIN*) device_id="windows" ;; \
+ *) echo "Unsupported platform: $$(uname -s)"; exit 1 ;; \
+ esac; \
+ for t in integration_test/*.dart; do flutter test --dart-define=INTERACT=2 --device-id $$device_id $$t; done
+ @echo $(SEPARATOR)
+
+# For the quick tests we do not INTERACT at all. The aim is to quickly
+# test all functionality.
+
+.PHONY: qtest
+qtest:
+ @case "$$(uname -s)" in \
+ Linux*) device_id="linux" ;; \
+ Darwin*) device_id="macos" ;; \
+ MINGW*|MSYS*|CYGWIN*) device_id="windows" ;; \
+ *) echo "Unsupported platform: $$(uname -s)"; exit 1 ;; \
+ esac; \
+ if [ ! -d integration_test ]; then echo "No integration tests available."; exit 0; fi; \
+ for t in $$(find integration_test -name "*_test.dart" | sort); do \
+ echo "========================================"; \
+ echo $$t; /bin/echo -n $$t >&2; \
+ echo "========================================"; \
+ flutter test --dart-define=INTERACT=0 --device-id $$device_id --reporter failures-only $$t 2>/dev/null; \
+ if [ "$$?" -eq 0 ]; then /bin/echo ' YES' >&2; else /bin/echo -n ' ...' >&2; \
+ echo '****************************************> TRY AGAIN'; \
+ flutter test --dart-define=INTERACT=0 --device-id $$device_id --reporter failures-only $$t 2>/dev/null; \
+ if [ "$$?" -eq 0 ]; then /bin/echo ' YES' >&2; else /bin/echo ' NO *****' >&2; fi; fi; \
+ done
+ @echo $(SEPARATOR)
+
+%.qtest:
+ @case "$$(uname -s)" in \
+ Linux*) device_id="linux" ;; \
+ Darwin*) device_id="macos" ;; \
+ MINGW*|MSYS*|CYGWIN*) device_id="windows" ;; \
+ *) echo "Unsupported platform: $$(uname -s)"; exit 1 ;; \
+ esac; \
+ flutter test --dart-define=INTERACT=0 --device-id $$device_id --reporter failures-only integration_test/$*.dart 2>/dev/null
+
+.PHONY: qtest.all
+qtest.all:
+ @echo $(APP) `egrep '^version: ' pubspec.yaml`
+ @echo "flutter version:" `flutter --version | head -1 | cut -d ' ' -f 2`
+ make qtest > qtest_$(shell date +%Y%m%d%H%M%S).txt
+
+clean::
+ rm -f qtest_*.txt
+
+.PHONY: atest
+atest:
+ @echo "Full integration TEST:"
+ flutter test --dart-define=INTERACT=0 --verbose --device-id \
+ $(shell flutter devices | grep desktop | perl -pe 's|^[^•]*• ([^ ]*) .*|\1|') \
+ integration_test
+ @echo $(SEPARATOR)
+
+.PHONY: coverage
+coverage:
+ @echo "COVERAGE"
+ @flutter test --coverage
+ @echo
+ @-/bin/bash support/coverage.sh
+ @echo $(SEPARATOR)
+
+.PHONY: coview
+coview:
+ @genhtml coverage/lcov.info -o coverage/html
+ @open coverage/html/index.html
+
+realclean::
+ rm -rf coverage
+
+# Crate an installer for Linux as a tar.gz archive.
+
+tgz:: $(APP)-$(VER)-linux-x86_64.tar.gz
+
+$(APP)-$(VER)-linux-x86_64.tar.gz: clean
+ mkdir -p installers
+ rm -rf build/linux/x64/release
+ flutter build linux --release
+ tar --transform 's|^build/linux/x64/release/bundle|$(APP)|' -czvf $@ build/linux/x64/release/bundle
+ cp $@ installers/
+ mv $@ installers/$(APP).tar.gz
+
+apk::
+ @echo '******************** BUILD ANDROID APK'
+ flutter build apk --release
+ cp build/app/outputs/flutter-apk/app-release.apk installers/$(APP).apk
+ cp build/app/outputs/flutter-apk/app-release.apk installers/$(APP)-$(VER).apk
+
+appbundle::
+ @echo '******************** BUILD ANDROID AAB'
+ flutter clean
+ flutter build appbundle --release
+ cp build/app/outputs/bundle/release/app-release.aab installers/$(APP).aab
+ cp build/app/outputs/bundle/release/app-release.aab installers/$(APP)-$(VER).aab
+
+realclean::
+ flutter clean
+ flutter pub get
+
+# Create a macos app
+# [20251029 jesscmoore] TODO: add converting to dmg
+# Build unsigned macos app
+dmg-unsigned::
+ flutter clean
+ flutter build macos --release --flavor unsigned
+
+# Build macos app signed with development certificate for testing
+# by App Developer Program togaware registered devices
+dmg-dev::
+ flutter clean
+ flutter build macos --release --flavor dev
+
+# Build macos app signed with app store distribution for testing
+# on Testflight or publishing
+dmg-staging:
+ flutter clean
+ flutter build macos --release --flavor staging
+
+# For the `dev` branch only, update the version sequence number prior
+# to a push (relies on the git.mk being loaded after this
+# flutter.mk). This is only undertaken through `make push` rather than
+# a `git push` in any other way. If
+# the pubspec.yaml is not using a build number then do not push to bump
+# the build number.
+
+VERSEQ=$(shell grep '^version: ' pubspec.yaml | cut -d'+' -f2 | awk '{print $$1+1}')
+
+BRANCH := $(shell git branch --show-current)
+
+ifeq ($(BRANCH),dev)
+push::
+ @echo $(SEPARATOR)
+ perl -pi -e 's|(^version: .*)\+.*|$$1+$(VERSEQ)|' pubspec.yaml
+ -egrep '^version: .*\+.*' pubspec.yaml && \
+ git commit -m "Bump sequence $(VERSEQ)" pubspec.yaml
+endif
+
+.PHONY: publish
+publish:
+ dart pub publish
+
+# dart pub global activate import_order_lint
+
+.PHONY: import_order
+import_order:
+ @echo "Dart: CHECK IMPORT ORDER"
+ @which import_order > /dev/null 2>&1 \
+ || { echo "Error: Install with 'dart pub global activate import_order_lint'."; exit 1; }
+ import_order --check $(DART_CODE)
+ @echo $(SEPARATOR)
+
+.PHONY: import_order_fix
+import_order_fix:
+ @echo "Dart: FIX IMPORT ORDER"
+ @import_order --check $(DART_CODE) \
+ || import_order lib $(DART_CODE)
+ @echo $(SEPARATOR)
+
+# dart pub global activate dart_code_metrics
+
+.PHONY: dcm
+dcm: unused_code unused_files
+
+.PHONY: unused_code
+unused_code:
+ @echo "Dart Code Metrics: UNUSED CODE"
+ -metrics check-unused-code --disable-sunset-warning lib
+ @echo $(SEPARATOR)
+
+.PHONY: unused_files
+unused_files:
+ @echo "Dart Code Metrics: UNUSED FILES"
+ -metrics check-unused-files --disable-sunset-warning lib
+ @echo $(SEPARATOR)
+
+.PHONY: lychee
+lychee:
+ @echo "Lychee: CHECK LINKS."
+ -lychee --no-progress --format compact *.md ./**/*.dart $(if $(wildcard ./**/*.md),./**/*.md) $(if $(wildcard ./**/*.html),./**/*.html)
+ @echo $(SEPARATOR)
+
+### TODO THESE SHOULD BE CHECKED AND CLEANED UP
+
+.PHONY: docs
+docs::
+ rsync -avzh doc/api/ root@solidcommunity.au:/var/www/html/docs/$(APP)/
+
+.PHONY: versions
+versions:
+ if [ -d snap ]; then perl -pi -e 's|^version:.*|version: $(VER)|' snap/snapcraft.yaml; fi
+
+.PHONY: loc
+loc: lib/*.dart
+ @bash $(LOC) $(shell find lib -name '*.dart') | sort -nr
+
+#
+# Manage the production install on the remote server.
+#
+
+.PHONY: solidcommunity
+solidcommunity:
+ rsync -avzh ./ solidcommunity.au:projects/$(APP)/ \
+ --exclude .dart_tool --exclude build --exclude ios --exclude macos \
+ --exclude linux --exclude windows --exclude android
+ ssh solidcommunity.au '(cd projects/$(APP); flutter upgrade; make prod)'
diff --git a/support/git.mk b/support/git.mk
new file mode 100644
index 0000000..6814efb
--- /dev/null
+++ b/support/git.mk
@@ -0,0 +1,132 @@
+########################################################################
+#
+# Makefile template for Version Control - git
+#
+# Time-stamp:
+#
+# Copyright 2018-2024 (c) Graham.Williams@togaware.com
+#
+# License: Creative Commons Attribution-ShareAlike 4.0 International.
+#
+########################################################################
+
+define GIT_HELP
+git:
+
+ info Identify the git repository;
+ status Status listing untracked files;
+ qstatus A quieter status ignoring untracked files;
+
+ enter Do a git status, fetch, and rebase
+ exit Do a git status
+
+ push
+ pull
+
+ fetch Update local repo from remote.
+ stash Stash changes to allow a rebase.
+ merge Update local repo with remote updates.
+ rebase Rebase local repo to include remote in history.
+ pop Pop the stash.
+
+ main Checkout the main branch;
+ dev Checkout the dev branch;
+ log
+ flog Show the full log;
+ gdiff
+ vdiff Show a visual diff using meld.
+
+ upstream Merge from upstrem to local repo.
+
+endef
+export GIT_HELP
+
+help::
+ @echo "$$GIT_HELP"
+
+info:
+ @echo "-------------------------------------------------------"
+ git config --get remote.origin.url
+ @echo "-------------------------------------------------------"
+
+status:
+ @echo "-------------------------------------------------------"
+ git status
+ @echo "-------------------------------------------------------"
+
+qstatus:
+ @echo "-------------------------------------------------------"
+ git status --untracked-files=no
+ @echo "-------------------------------------------------------"
+
+diff:
+ @echo "-------------------------------------------------------"
+ git diff
+ @echo "-------------------------------------------------------"
+
+enter:: status fetch rebase
+exit:: status push
+
+# Use :: to allow push to be augmented in other makefiles.
+
+push::
+ @echo "-------------------------------------------------------"
+ git push
+ @echo "-------------------------------------------------------"
+
+pull:
+ @echo "-------------------------------------------------------"
+ git pull --stat
+ @echo "-------------------------------------------------------"
+
+fetch:
+ @echo "-------------------------------------------------------"
+ git fetch
+ @echo "-------------------------------------------------------"
+
+stash:
+ @echo "-------------------------------------------------------"
+ git stash
+ @echo "-------------------------------------------------------"
+
+rebase:
+ @echo "-------------------------------------------------------"
+ git rebase
+ @echo "-------------------------------------------------------"
+
+pop:
+ @echo "-------------------------------------------------------"
+ git stash pop
+ @echo "-------------------------------------------------------"
+
+main:
+ @echo "-------------------------------------------------------"
+ git checkout main
+ @echo "-------------------------------------------------------"
+
+dev:
+ @echo "-------------------------------------------------------"
+ git checkout $(USER)/dev
+ @echo "-------------------------------------------------------"
+
+log:
+ @echo "-------------------------------------------------------"
+ git --no-pager log --stat --max-count=10
+ @echo "-------------------------------------------------------"
+
+flog:
+ @echo "-------------------------------------------------------"
+ git --no-pager log
+ @echo "-------------------------------------------------------"
+
+gdiff:
+ @echo "-------------------------------------------------------"
+ git --no-pager diff --color
+ @echo "-------------------------------------------------------"
+
+vdiff:
+ git difftool --tool=meld
+
+upstream:
+ git fetch upstream
+ git merge upstream/main
diff --git a/support/loc.sh b/support/loc.sh
new file mode 100644
index 0000000..c526c42
--- /dev/null
+++ b/support/loc.sh
@@ -0,0 +1,132 @@
+#!/bin/bash
+
+IGNORE=false # Return an error if any files have more than N loc.
+MAX=300 # Value of N loc.
+CONCATE=false # Whether to simply count all lines across all files.
+THRESHOLD=0
+CLEAN=false
+
+# Function to display help
+
+show_help() {
+ echo "Usage: $0 [options] "
+ echo ""
+ echo "Options:"
+ echo " -c, --clean Just filter the file to remove lines not counted."
+ echo " -i, --ignore Ignore errors related to the line count threshold."
+ echo " -t, --total Concatenate all supplied files and count total lines after cleansing."
+ echo " -n, --max-lines Set the maximum number of allowed lines (default: ${MAX})."
+ echo " -h, --help Show this help message."
+ exit 0
+}
+
+# Function to cleanse lines.
+
+cleanse_lines() {
+ sed '1d' "$1" |
+ grep -v '^$' | # Remove empty lines
+ grep -v '^[[:space:]]*//' | # Remove comment only lines
+ grep -v '^\(import\|library\)' | # Remove library and import statements
+ grep -v '^\s*@' | # Remove directives
+ grep -v '^\s*[\}\)]' | # Remove linest that start with a bracket
+ grep -v '^\s*\]' | # Needed this as special case
+ grep -v '^\s*(\?|:) \[' | # Remove lines that consist of '? [' or ': ['
+ grep -v '\s*\w*: \[' | # Remove parameter list lines like ` names: [`
+ grep -v "^\s*['][^']*[']" | # Remove lines that are only a string.
+ cat
+}
+
+wc_cleanse_lines() {
+ cleanse_lines "$1" | wc -l
+}
+
+
+
+# Check for no arguments.
+
+if [ "$#" -eq 0 ]; then
+ show_help
+fi
+
+# Parse input arguments.
+
+while [[ "$1" != "" ]]; do
+ case $1 in
+ -c | --clean)
+ CLEAN=true
+ shift
+ ;;
+ -i | --ignore)
+ IGNORE=true
+ shift
+ ;;
+ -t | --total)
+ CONCATE=true
+ shift
+ ;;
+ -n | --max-lines)
+ shift
+ if [[ "$1" =~ ^[0-9]+$ ]]; then
+ THRESHOLD=$1
+ shift
+ else
+ THRESHOLD=${MAX}
+ fi
+ ;;
+ -h | --help)
+ show_help
+ ;;
+ *)
+ FILES+=("$1")
+ shift
+ ;;
+ esac
+done
+
+if [ "$CLEAN" = true ]; then
+ for file in "${FILES[@]}"; do
+ cleanse_lines "$file"
+ done
+ exit 0
+fi
+
+if [ "$CONCATE" = true ]; then
+ # Concatenate all files and get total line count after cleansing then exit
+ TOTAL_LINES=0
+ for file in "${FILES[@]}"; do
+ LINES=$(wc_cleanse_lines "$file")
+ TOTAL_LINES=$((TOTAL_LINES + LINES))
+ done
+ echo "$TOTAL_LINES"
+ exit 0
+fi
+
+# Check individual files for line count.
+
+ERROR=false
+
+if [ "$THRESHOLD" -gt 0 ]; then
+ for file in "${FILES[@]}"; do
+ LINES=$(wc_cleanse_lines "$file")
+ if [ "$LINES" -gt "$THRESHOLD" ]; then
+ printf "%4d %s\n" "$LINES" "$file"
+ ERROR=true
+ fi
+ done
+else
+ for file in "${FILES[@]}"; do
+ LINES=$(wc_cleanse_lines "$file")
+ printf "%4d %s\n" "$LINES" "$file"
+ done
+fi
+
+# Set exit status based on error occurrence
+if [ "$ERROR" = true ]; then
+ if [ "$IGNORE" = true ]; then
+ exit 0
+ else
+ exit 1
+ fi
+fi
+
+exit 0
diff --git a/support/modules.mk b/support/modules.mk
new file mode 100644
index 0000000..a11b5a5
--- /dev/null
+++ b/support/modules.mk
@@ -0,0 +1,68 @@
+########################################################################
+# Supported Makefile modules.
+#
+# Often the support Makefiles will be in the local support folder, or
+# else installed in the local user's shares.
+
+INC_CLEAN ?= $(INC_BASE)/clean.mk
+INC_BOOKDOWN ?= $(INC_BASE)/bookdown.mk
+INC_R ?= $(INC_BASE)/r.mk
+INC_KNITR ?= $(INC_BASE)/knitr.mk
+INC_PANDOC ?= $(INC_BASE)/pandoc.mk
+INC_GIT ?= $(INC_BASE)/git.mk
+INC_AZURE ?= $(INC_BASE)/azure.mk
+INC_LATEX ?= $(INC_BASE)/latex.mk
+INC_PDF ?= $(INC_BASE)/pdf.mk
+INC_DOCKER ?= $(INC_BASE)/docker.mk
+INC_FLUTTER ?= $(INC_BASE)/flutter.mk
+INC_JEKYLL ?= $(INC_BASE)/jekyll.mk
+INC_MLHUB ?= $(INC_BASE)/mlhub.mk
+INC_WEBCAM ?= $(INC_BASE)/webcam.mk
+INC_INSTALL ?= $(INC_BASE)/install.mk
+
+ifneq ("$(wildcard $(INC_CLEAN))","")
+ include $(INC_CLEAN)
+endif
+ifneq ("$(wildcard $(INC_BOOKDOWN))","")
+ include $(INC_BOOKDOWN)
+endif
+ifneq ("$(wildcard $(INC_R))","")
+ include $(INC_R)
+endif
+ifneq ("$(wildcard $(INC_KNITR))","")
+ include $(INC_KNITR)
+endif
+ifneq ("$(wildcard $(INC_PANDOC))","")
+ include $(INC_PANDOC)
+endif
+ifneq ("$(wildcard $(INC_FLUTTER))","")
+ include $(INC_FLUTTER)
+endif
+ifneq ("$(wildcard $(INC_GIT))","")
+ include $(INC_GIT)
+endif
+ifneq ("$(wildcard $(INC_AZURE))","")
+ include $(INC_AZURE)
+endif
+ifneq ("$(wildcard $(INC_LATEX))","")
+ include $(INC_LATEX)
+endif
+ifneq ("$(wildcard $(INC_PDF))","")
+ include $(INC_PDF)
+endif
+ifneq ("$(wildcard $(INC_DOCKER))","")
+ include $(INC_DOCKER)
+endif
+ifneq ("$(wildcard $(INC_JEKYLL))","")
+ include $(INC_JEKYLL)
+endif
+ifneq ("$(wildcard $(INC_MLHUB))","")
+ include $(INC_MLHUB)
+endif
+ifneq ("$(wildcard $(INC_WEBCAM))","")
+ include $(INC_WEBCAM)
+endif
+ifneq ("$(wildcard $(INC_INSTALL))","")
+ include $(INC_INSTALL)
+endif
+