commit 976c3d0679d9665d1c9ce8d6944029ffffc26e10 Author: Nathan Anderson Date: Sat Sep 7 14:42:32 2024 -0600 WIP hot reloading enabled, much progress diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a30b03 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# https://dart.dev/guides/libraries/private-files +# Created by `dart pub` +.dart_tool/ +.direnv/** + +*.toml +*.pdf +*.jpeg +*.png +*.JPEG diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..effe43c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +- Initial version. diff --git a/README.md b/README.md new file mode 100644 index 0000000..3816eca --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +A sample command-line application with an entrypoint in `bin/`, library code +in `lib/`, and example unit test in `test/`. diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..214c536 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,12 @@ +include: package:lint/strict.yaml + +# linter: +# rules: +# - camel_case_types +analyzer: + language: + strict-casts: true + strict-inference: true + strict-raw-types: true + exclude: + # - path/to/excluded/files/** diff --git a/bin/dartboard_resume.dart b/bin/dartboard_resume.dart new file mode 100644 index 0000000..c314058 --- /dev/null +++ b/bin/dartboard_resume.dart @@ -0,0 +1,47 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:dartboard_resume/render.dart'; +import 'package:hotreloader/hotreloader.dart'; +import 'package:logging/logging.dart' as logging; + +StreamSubscription? fileStreamSub; +StreamSubscription? stdinStreamSub; + +Future main(List arguments) async { + const String tomlFilePath = "resume.toml"; + logging.hierarchicalLoggingEnabled = true; + HotReloader.logLevel = logging.Level.INFO; + final HotReloader reloader = await HotReloader.create(); + + stdin.lineMode = false; + stdin.echoMode = false; + stdin.echoNewlineMode = false; + stdinStreamSub = stdin.transform(const Utf8Decoder()).transform(const LineSplitter()).listen( + (event) { + if (event == "r") { + stdout.writeln("Triggering pdf render..."); + renderPdf(tomlFilePath, force: true); + } + }, + ); + + ProcessSignal.sigint.watch().listen((_) { + stdout.writeln('SIGINT received. Exiting gracefully...'); + fileStreamSub?.cancel(); + stdinStreamSub?.cancel(); + // Perform cleanup or other necessary actions here + reloader.stop(); + exit(0); // Exit with code 0 to indicate a successful termination + }); + if (FileSystemEntity.isWatchSupported) { + final fileStream = File(tomlFilePath).watch(events: FileSystemEvent.modify); + fileStreamSub = fileStream.listen((e) { + renderPdf(tomlFilePath); + }); + stdout.writeln('Watching for file changes.'); + } else { + renderPdf(tomlFilePath); + } +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..1792a68 --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1719848872, + "narHash": "sha256-H3+EC5cYuq+gQW8y0lSrrDZfH71LB4DAf+TDFyvwCNA=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "00d80d13810dbfea8ab4ed1009b09100cca86ba8", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..18a1015 --- /dev/null +++ b/flake.nix @@ -0,0 +1,25 @@ +{ + description = "Simple dart flake"; + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + inputs.flake-utils.url = "github:numtide/flake-utils"; + + outputs = { + flake-utils, + nixpkgs, + ... + }: + flake-utils.lib.eachDefaultSystem (system: let + pkgs = import nixpkgs { + inherit system; + }; + in { + devShell = pkgs.mkShell { + buildInputs = with pkgs; [ + dart + ]; + # shellHook = '' + # export FLUTTER_ROOT + # ''; + }; + }); +} diff --git a/lib/dartboard_parser.dart b/lib/dartboard_parser.dart new file mode 100644 index 0000000..65783eb --- /dev/null +++ b/lib/dartboard_parser.dart @@ -0,0 +1,281 @@ +import 'dart:io'; + +import 'package:intl/intl.dart'; +import 'package:pdf/pdf.dart'; +import 'package:pdf/widgets.dart'; +import 'package:toml/toml.dart'; + +class DartboardData { + DartboardData({ + required this.fullName, + required this.phoneNumber, + required this.email, + // required this.address, + required this.imagePath, + required this.dartboardTheme, + required this.experiences, + required this.miscList, + }); + + factory DartboardData.fromToml(String tomlFilePath) { + final tomlData = TomlDocument.loadSync(tomlFilePath).toMap(); + + final List exps = tomlData.entries + .where((e) => e.value is List && (e.value as List).firstOrNull is Map) + .map((MapEntry expsEntry) { + final String subsection = tomlData['${expsEntry.key}_name'] as String? ?? ''; + final exps = (expsEntry.value as List).map((e) => e as Map).toList(); + return exps.map((exp) { + final TomlLocalDate? start = exp['start'] as TomlLocalDate?; + final TomlLocalDate? end = exp['end'] as TomlLocalDate?; + return DartboardExperience( + title: exp['title'] as String, + subsection: subsection, + range: DateRange( + start: start == null ? null : DateTime(start.date.year, start.date.month), + end: end == null ? null : DateTime(end.date.year, end.date.month), + ), + attributes: (exp['attributes'] as List).map((e) => DartboardText(content: e as String)).toList(), + location: exp['location'] as String?, + ); + }).toList(); + }) + .expand((i) => i) + .toList(); + return DartboardData( + fullName: tomlData['full_name'] as String, + phoneNumber: tomlData['phone_number'] as String, + email: tomlData['email'] as String, + imagePath: tomlData['image'] as String, + dartboardTheme: DartboardTheme.fromToml(tomlData), + experiences: exps, + miscList: [ + DartboardList(subsection: 'Skills & Interest', content: 'Proficient in flutter and other'), + ], + ); + } + + final String fullName; + final String phoneNumber; + final String email; + // final String address; + final String imagePath; + final DartboardTheme dartboardTheme; + final List experiences; + final List miscList; + + Font get font => dartboardTheme.font; + + PdfColor get primaryColor => dartboardTheme.primaryColor; + PdfColor get accentColor => dartboardTheme.accentColor; + PdfColor get backgroundColor => dartboardTheme.backgroundColor; + + TextStyle get headerTextStyle => + TextStyle(fontSize: 18, font: font, fontWeight: FontWeight.bold, color: const PdfColorGrey(0.18)); + TextStyle get subheaderTextStyle => TextStyle(fontSize: 14, font: font, color: const PdfColorGrey(0.3)); + TextStyle get defaultTextStyle => TextStyle(fontSize: 10, font: font, color: const PdfColorGrey(0.45)); + + Map> get groupedExperiences { + final Map> exps = {}; + for (final DartboardExperience exp in experiences) { + if (!exps.containsKey(exp.subsection)) { + exps[exp.subsection] = []; + } + exps[exp.subsection]!.add(exp); + } + return exps; + } + + @override + int get hashCode { + return Object.hashAll([fullName, phoneNumber, email, imagePath, ...experiences]); + } + + @override + bool operator ==(Object other) { + return super.hashCode == other.hashCode; + } +} + +class DartboardExperience { + DartboardExperience({ + required this.title, + required this.attributes, + required this.range, + required this.subsection, + this.location, + }); + final String subsection; + final String title; + String? location; + final List attributes; + final DateRange range; + + @override + int get hashCode { + return Object.hashAll([subsection, title, ...attributes, range]); + } + + @override + bool operator ==(Object other) { + return super.hashCode == other.hashCode; + } +} + +class DateRange { + DateRange({required this.start, required this.end}); + final DateTime? start; + final DateTime? end; + + @override + String toString() { + final DateFormat dateFormat = DateFormat("MMM yyyy"); + if (start == null && end == null) { + return ''; + } + if (start == null && end != null) { + return dateFormat.format(end!); + } + if (start != null && end == null) { + return "${dateFormat.format(start!)} - Current"; + } + return "${dateFormat.format(start!)} - ${dateFormat.format(end!)}"; + } + + @override + int get hashCode { + return Object.hashAll([start, end]); + } + + @override + bool operator ==(Object other) { + return super.hashCode == other.hashCode; + } +} + +class DartboardList { + DartboardList({required this.subsection, required this.content}); + final String subsection; + final String content; + @override + int get hashCode { + return Object.hashAll([subsection, content]); + } + + @override + bool operator ==(Object other) { + return super.hashCode == other.hashCode; + } +} + +class DartboardTheme { + DartboardTheme({ + required this.primaryHex, + required this.accentHex, + required this.backgroundHex, + required this.fontPath, + required this.bulletPoint, + }); + + factory DartboardTheme.fromToml(Map toml) => DartboardTheme( + primaryHex: (toml['theme'] as Map)['primary_hex'] as String, + accentHex: (toml['theme'] as Map)['accent_hex'] as String, + backgroundHex: (toml['theme'] as Map)['background_hex'] as String, + fontPath: (toml['theme'] as Map)['font'] as String, + bulletPoint: (toml['theme'] as Map)['bullet_point'] as String? ?? '-', + ); + + factory DartboardTheme.retro() => DartboardTheme( + primaryHex: "00AA00", + accentHex: "44EE66", + backgroundHex: "FFFFFF", + fontPath: "nerd.ttf", + bulletPoint: '-', + ); + + static const double inch = 72.0; + static const double cm = inch / 2.54; + static const double mm = inch / 25.4; + + static const width = 21.0 * cm; + static const height = 29.7 * cm; + static const margin = 2.0 * cm; + + final String primaryHex; + final String accentHex; + final String backgroundHex; + final String fontPath; + final String bulletPoint; + Font? _font; + + PdfColor get primaryColor => PdfColor.fromHex(primaryHex); + PdfColor get accentColor => PdfColor.fromHex(accentHex); + PdfColor get backgroundColor => PdfColor.fromHex(backgroundHex); + + Font get font { + _font ??= Font.ttf(File(fontPath).readAsBytesSync().buffer.asByteData()); + return _font!; + } + + @override + int get hashCode { + return Object.hashAll([primaryHex, accentHex, backgroundHex, fontPath, bulletPoint]); + } + + @override + bool operator ==(Object other) { + return super.hashCode == other.hashCode; + } +} + +enum DartboardTextType { normal, linkText } + +typedef DartboardTextLinkData = ({String text, String? url, DartboardTextType type}); + +/// Automatically detects and parses strings with hypertext in a markdown format +class DartboardText { + DartboardText({required this.content}); + + final String content; + static final _markdownLinkRegex = RegExp(r'\[(.*?)\]\((.*?)\)'); + + bool get hasLinkMarkup => _markdownLinkRegex.hasMatch(content); + + List toTextLinkList() { + final markdownLinkRegex = RegExp(r'\[(.*?)\]\((.*?)\)'); + if (markdownLinkRegex.hasMatch(content)) { + final matches = markdownLinkRegex.allMatches(content).toList(); + final List stringSections = []; + int prevStartIndex = 0; + while (matches.isNotEmpty) { + final match = matches.removeAt(0); + stringSections + .add((text: content.substring(prevStartIndex, match.start), url: null, type: DartboardTextType.normal)); + stringSections.add((text: match.group(1)!, url: match.group(2), type: DartboardTextType.linkText)); + prevStartIndex = match.end; + } + stringSections + .add((text: content.substring(prevStartIndex, content.length), url: null, type: DartboardTextType.normal)); + return stringSections; + } else { + return [(text: content, url: null, type: DartboardTextType.normal)]; + // return Text("${bulletString != null ? '$bulletString ' : ''}$content", style: style); + } + } + + @override + int get hashCode { + return Object.hashAll([content]); + } + + @override + bool operator ==(Object other) { + return super.hashCode == other.hashCode; + } +} + +String _getSubsectionFromKey(String key) { + switch (key) { + case 'exp': + } +} diff --git a/lib/dartboard_resume.dart b/lib/dartboard_resume.dart new file mode 100644 index 0000000..188c842 --- /dev/null +++ b/lib/dartboard_resume.dart @@ -0,0 +1,101 @@ +import 'dart:io'; + +import 'package:dartboard_resume/dartboard_parser.dart'; +import 'package:dartboard_resume/dartboard_widgets.dart'; +import 'package:pdf/pdf.dart'; +import 'package:pdf/widgets.dart'; + +Page generatePdfPage({required DartboardData dartboardData, required int renderNs}) { + final List groupedExperienceList = dartboardData.groupedExperiences.entries.map( + (entry) { + final String subsection = entry.key; + final List experiences = entry.value; + return Column( + children: [ + Row( + children: [ + Text( + subsection, + style: dartboardData.subheaderTextStyle + .merge(const TextStyle(fontSize: 18)) + .apply(color: const PdfColorGrey(0.2)), + ), + ], + ), + Container(height: 2, width: 200, color: const PdfColorGrey(0.7)), + ...experiences.map( + (DartboardExperience exp) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(exp.title, style: dartboardData.subheaderTextStyle.apply(color: const PdfColorGrey(0.3))), + Text( + exp.range.toString(), + style: dartboardData.subheaderTextStyle.apply(color: const PdfColorGrey(0.42)), + ), + ], + ), + ...exp.attributes.map( + (a) => Text( + "${dartboardData.dartboardTheme.bulletPoint} $a", + style: dartboardData.defaultTextStyle.apply( + color: const PdfColorGrey(0.55), + ), + ), + ), + SizedBox(height: 20), + ], + ), + ), + DartboardFooter(dartboardData: dartboardData, renderNs: renderNs), + ], + ); + }, + ).toList(); + return Page( + pageTheme: const PageTheme(pageFormat: PdfPageFormat.standard), + build: (Context context) { + return Column( + children: [ + SizedBox( + height: 120, + width: double.infinity, + child: Stack( + children: [ + Positioned( + left: 0, + child: Container( + height: 100, + width: 100, + decoration: BoxDecoration( + shape: BoxShape.circle, + image: DecorationImage( + fit: BoxFit.contain, + image: MemoryImage( + File(dartboardData.imagePath).readAsBytesSync(), + ), + ), + ), + ), + ), + Center( + child: Column( + children: [ + Text(dartboardData.fullName, style: dartboardData.headerTextStyle), + Text(dartboardData.phoneNumber, style: dartboardData.headerTextStyle), + Text(dartboardData.email, style: dartboardData.headerTextStyle), + ], + ), + ), + ], + ), + ), + Container(height: 20), + ...groupedExperienceList, + ], + ); + }, + ); +} diff --git a/lib/dartboard_widgets.dart b/lib/dartboard_widgets.dart new file mode 100644 index 0000000..da5df3e --- /dev/null +++ b/lib/dartboard_widgets.dart @@ -0,0 +1,123 @@ +import 'package:dartboard_resume/dartboard_parser.dart'; +import 'package:pdf/pdf.dart'; +import 'package:pdf/widgets.dart'; + +class DartboardFooter extends StatelessWidget { + DartboardFooter({required this.dartboardData, required this.renderNs}); + final int renderNs; + final DartboardData dartboardData; + + @override + Widget build(Context context) { + final double renderTimeMs = renderNs.toDouble() / 1000.0; + return Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Text( + 'This resume was generated with DartBoard Resume', + style: dartboardData.defaultTextStyle.copyWith(fontSize: 10), + ), + Text( + 'Rendered in ${renderTimeMs.toStringAsFixed(2)}ms', + style: dartboardData.defaultTextStyle.copyWith(fontSize: 10), + ), + ], + ); + } +} + +class DartboardExperienceEntry extends StatelessWidget { + DartboardExperienceEntry({required this.dartboardData, required this.exp}); + final DartboardData dartboardData; + final DartboardExperience exp; + + @override + Widget build(Context context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(exp.title, style: dartboardData.subheaderTextStyle.apply(color: const PdfColorGrey(0.3))), + Container(height: 1, width: 60, color: const PdfColorGrey(0.75)), + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + exp.range.toString(), + style: dartboardData.defaultTextStyle.apply(color: const PdfColorGrey(0.42)), + ), + if (exp.location != null) + Text( + exp.location!, + style: dartboardData.defaultTextStyle.apply(color: const PdfColorGrey(0.42)), + ), + ], + ), + ], + ), + SizedBox(height: 6), + ...exp.attributes.map( + (a) => Padding( + child: DartboardTextWithLink( + stringSections: a.toTextLinkList(), + bulletString: dartboardData.dartboardTheme.bulletPoint, + style: dartboardData.defaultTextStyle.apply( + color: const PdfColorGrey(0.55), + ), + ), + padding: const EdgeInsets.only(bottom: 4.0), + ), + ), + SizedBox(height: 20), + ], + ); + } +} + +// TODO this lays out long text lines on a newline after a uilink rather than soft wrapping it. +class DartboardTextWithLink extends StatelessWidget { + DartboardTextWithLink({this.bulletString, required this.stringSections, this.style}); + + final String? bulletString; + final List stringSections; + final TextStyle? style; + + @override + Widget build(Context context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(bulletString == null ? '' : '$bulletString ', style: style), + SizedBox( + width: DartboardTheme.width - DartboardTheme.margin * 2 - 20.0, + child: Wrap( + children: [ + ...stringSections.map((s) { + switch (s.type) { + case DartboardTextType.normal: + return Text(s.text, style: style); + case DartboardTextType.linkText: + return UrlLink( + destination: s.url!, + child: Text( + s.text, + style: style?.copyWith(color: PdfColors.blue), + ), + ); + } + }), + ], + ), + ), + ], + ); + } +} diff --git a/lib/render.dart b/lib/render.dart new file mode 100644 index 0000000..ec0f188 --- /dev/null +++ b/lib/render.dart @@ -0,0 +1,118 @@ +import 'dart:io'; + +import 'package:dartboard_resume/dartboard_parser.dart'; +import 'package:dartboard_resume/dartboard_widgets.dart'; +import 'package:pdf/pdf.dart'; +import 'package:pdf/widgets.dart'; +import 'package:toml/toml.dart'; + +int? lastDartboardHash; + +Future renderPdf(String tomlFilePath, {bool force = false}) async { + try { + final start = DateTime.now().microsecondsSinceEpoch; + final dartboardData = DartboardData.fromToml(tomlFilePath); + if (lastDartboardHash == dartboardData.hashCode && !force) { + return; + } + lastDartboardHash = dartboardData.hashCode; + stdout.writeln("Detected change:\nRendering with new dartboard data: $lastDartboardHash"); + final pdfFuture = Document()..addPage(_generatePdfPage(dartboardData: dartboardData, renderNs: 0)); + await pdfFuture.save(); + final renderNs = DateTime.now().microsecondsSinceEpoch - start; + final pdf = Document(); + pdf.addPage(_generatePdfPage(dartboardData: dartboardData, renderNs: renderNs)); + + final file = File("example.pdf"); + final bytes = await pdf.save(); + + stdout.writeln('New pdf file saved.'); + file.writeAsBytesSync(bytes); + stdout.writeln('Reloading llpp...'); + Process.runSync('pkill', ['-HUP', 'llpp']); + } catch (e) { + stderr.writeln('Encountered error: $e'); + try { + stderr.writeln('Current toml map:\n${TomlDocument.loadSync(tomlFilePath).toMap()}'); + } catch (_) { + stderr.writeln('Cannot display current toml map'); + } + } +} + +Page _generatePdfPage({required DartboardData dartboardData, required int renderNs}) { + final List groupedExperienceList = dartboardData.groupedExperiences.entries.map( + (entry) { + final String subsection = entry.key; + final List experiences = entry.value; + return Column( + children: [ + Row( + children: [ + Text( + subsection, + style: dartboardData.subheaderTextStyle + .merge(const TextStyle(fontSize: 18)) + .apply(color: const PdfColorGrey(0.2)), + ), + ], + ), + Container(height: 2, width: 200, color: const PdfColorGrey(0.7)), + ...experiences.map( + (DartboardExperience exp) => DartboardExperienceEntry(dartboardData: dartboardData, exp: exp), + ), + DartboardFooter(dartboardData: dartboardData, renderNs: renderNs), + ], + ); + }, + ).toList(); + return Page( + pageTheme: const PageTheme(pageFormat: PdfPageFormat.standard), + build: (Context context) { + return + // FullPage( + // ignoreMargins: true, + // child: + Column( + children: [ + SizedBox( + height: 120, + width: double.infinity, + child: Stack( + children: [ + Positioned( + left: 0, + child: Container( + height: 100, + width: 100, + decoration: BoxDecoration( + shape: BoxShape.circle, + image: DecorationImage( + fit: BoxFit.contain, + image: MemoryImage( + File(dartboardData.imagePath).readAsBytesSync(), + ), + ), + ), + ), + ), + Center( + child: Column( + children: [ + Text(dartboardData.fullName, style: dartboardData.headerTextStyle), + Text(dartboardData.phoneNumber, style: dartboardData.headerTextStyle), + Text(dartboardData.email, style: dartboardData.headerTextStyle), + ], + ), + ), + ], + ), + ), + Container(height: 20), + ...groupedExperienceList, + ], + // ), + ); + }, + ); +} diff --git a/lib/utils.dart b/lib/utils.dart new file mode 100644 index 0000000..e2ef47c --- /dev/null +++ b/lib/utils.dart @@ -0,0 +1,5 @@ +extension StringUtils on String { + String capitalize() { + return substring(0, 1).toUpperCase() + substring(1); + } +} diff --git a/nerd.ttf b/nerd.ttf new file mode 100644 index 0000000..2ff62ee Binary files /dev/null and b/nerd.ttf differ diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..c79574f --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,530 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: "5aaf60d96c4cd00fe7f21594b5ad6a1b699c80a27420f8a837f4d68473ef09e3" + url: "https://pub.dev" + source: hosted + version: "68.0.0" + _macros: + dependency: transitive + description: dart + source: sdk + version: "0.1.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "21f1d3720fd1c70316399d5e2bccaebb415c434592d778cce8acb967b8578808" + url: "https://pub.dev" + source: hosted + version: "6.5.0" + archive: + dependency: transitive + description: + name: archive + sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d + url: "https://pub.dev" + source: hosted + version: "3.6.1" + args: + dependency: transitive + description: + name: args + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + url: "https://pub.dev" + source: hosted + version: "2.5.0" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + barcode: + dependency: transitive + description: + name: barcode + sha256: ab180ce22c6555d77d45f0178a523669db67f95856e3378259ef2ffeb43e6003 + url: "https://pub.dev" + source: hosted + version: "2.2.8" + bidi: + dependency: transitive + description: + name: bidi + sha256: "9a712c7ddf708f7c41b1923aa83648a3ed44cfd75b04f72d598c45e5be287f9d" + url: "https://pub.dev" + source: hosted + version: "2.0.12" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + url: "https://pub.dev" + source: hosted + version: "1.19.0" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + coverage: + dependency: transitive + description: + name: coverage + sha256: c1fb2dce3c0085f39dc72668e85f8e0210ec7de05345821ff58530567df345a5 + url: "https://pub.dev" + source: hosted + version: "1.9.2" + crypto: + dependency: transitive + description: + name: crypto + sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27 + url: "https://pub.dev" + source: hosted + version: "3.0.5" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + hotreloader: + dependency: "direct dev" + description: + name: hotreloader + sha256: ed56fdc1f3a8ac924e717257621d09e9ec20e308ab6352a73a50a1d7a4d9158e + url: "https://pub.dev" + source: hosted + version: "4.2.0" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "40f592dd352890c3b60fec1b68e786cefb9603e05ff303dbc4dda49b304ecdf4" + url: "https://pub.dev" + source: hosted + version: "4.1.0" + image: + dependency: transitive + description: + name: image + sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8" + url: "https://pub.dev" + source: hosted + version: "4.2.0" + intl: + dependency: "direct main" + description: + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + url: "https://pub.dev" + source: hosted + version: "0.19.0" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + js: + dependency: transitive + description: + name: js + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf + url: "https://pub.dev" + source: hosted + version: "0.7.1" + lint: + dependency: "direct main" + description: + name: lint + sha256: d758a5211fce7fd3f5e316f804daefecdc34c7e53559716125e6da7388ae8565 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + lints: + dependency: "direct dev" + description: + name: lints + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + url: "https://pub.dev" + source: hosted + version: "3.0.0" + logging: + dependency: "direct main" + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + macros: + dependency: transitive + description: + name: macros + sha256: "12e8a9842b5a7390de7a781ec63d793527582398d16ea26c60fed58833c9ae79" + url: "https://pub.dev" + source: hosted + version: "0.1.0-main.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" + source: hosted + version: "0.12.16+1" + meta: + dependency: transitive + description: + name: meta + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + url: "https://pub.dev" + source: hosted + version: "1.15.0" + mime: + dependency: transitive + description: + name: mime + sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a" + url: "https://pub.dev" + source: hosted + version: "1.0.6" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + path: + dependency: transitive + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + path_parsing: + dependency: transitive + description: + name: path_parsing + sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf + url: "https://pub.dev" + source: hosted + version: "1.0.1" + pdf: + dependency: "direct main" + description: + name: pdf + sha256: "05df53f8791587402493ac97b9869d3824eccbc77d97855f4545cf72df3cae07" + url: "https://pub.dev" + source: hosted + version: "3.11.1" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + url: "https://pub.dev" + source: hosted + version: "6.0.2" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + qr: + dependency: transitive + description: + name: qr + sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf: + dependency: transitive + description: + name: shelf + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + url: "https://pub.dev" + source: hosted + version: "1.4.2" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e + url: "https://pub.dev" + source: hosted + version: "1.1.2" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + url: "https://pub.dev" + source: hosted + version: "2.1.2" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" + url: "https://pub.dev" + source: hosted + version: "0.10.12" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test: + dependency: "direct dev" + description: + name: test + sha256: "713a8789d62f3233c46b4a90b174737b2c04cb6ae4500f2aa8b1be8f03f5e67f" + url: "https://pub.dev" + source: hosted + version: "1.25.8" + test_api: + dependency: transitive + description: + name: test_api + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + url: "https://pub.dev" + source: hosted + version: "0.7.3" + test_core: + dependency: transitive + description: + name: test_core + sha256: "12391302411737c176b0b5d6491f466b0dd56d4763e347b6714efbaa74d7953d" + url: "https://pub.dev" + source: hosted + version: "0.6.5" + toml: + dependency: "direct main" + description: + name: toml + sha256: d968d149c8bd06dc14e09ea3a140f90a3f2ba71949e7a91df4a46f3107400e71 + url: "https://pub.dev" + source: hosted + version: "0.16.0" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + url: "https://pub.dev" + source: hosted + version: "14.2.5" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062 + url: "https://pub.dev" + source: hosted + version: "1.0.0" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" + url: "https://pub.dev" + source: hosted + version: "0.1.6" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + xml: + dependency: transitive + description: + name: xml + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + url: "https://pub.dev" + source: hosted + version: "6.5.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" +sdks: + dart: ">=3.4.4 <4.0.0" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..81adf0b --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,22 @@ +name: dartboard_resume +description: A sample command-line application. +version: 1.0.0 +# repository: https://github.com/my_org/my_repo + +environment: + sdk: ^3.4.4 + +# Add regular dependencies here. +dependencies: + intl: ^0.19.0 + lint: ^2.3.0 + logging: ^1.2.0 + pdf: ^3.11.1 + toml: ^0.16.0 + # path: ^1.8.0 + +dev_dependencies: + lints: ^3.0.0 + test: ^1.24.0 + hotreloader: ^4.2.0 + diff --git a/test/dartboard_resume_test.dart b/test/dartboard_resume_test.dart new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/test/dartboard_resume_test.dart @@ -0,0 +1 @@ +