Просмотр исходного кода

Compact resource and summary views

Tim Jones 2 дней назад
Родитель
Сommit
e36470f797

+ 3 - 3
RackPeek.Domain/RackPeek.Domain.csproj

@@ -16,9 +16,9 @@
     <ItemGroup>
         <PackageReference Include="DocMigrator.Yaml" Version="10.0.3" />
         <PackageReference Include="LibGit2Sharp" Version="0.32.0" />
-        <PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.12" />
-        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.12" />
-        <PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.12" />
+        <PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.13" />
+        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.13" />
+        <PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.13" />
         <PackageReference Include="YamlDotNet" Version="18.1.0" />
     </ItemGroup>
 

+ 1 - 1
RackPeek.Mcp/RackPeek.Mcp.csproj

@@ -13,7 +13,7 @@
          only exists on the server. -->
 
     <ItemGroup>
-        <PackageReference Include="ModelContextProtocol" Version="2.2.0"/>
+        <PackageReference Include="ModelContextProtocol" Version="2.3.0"/>
     </ItemGroup>
 
     <ItemGroup>

+ 3 - 3
RackPeek.Web.Viewer/RackPeek.Web.Viewer.csproj

@@ -8,12 +8,12 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.12" />
-        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.12" PrivateAssets="all" />
+        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.13" />
+        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.13" PrivateAssets="all" />
         <!-- Microsoft.DotNet.HotReload.WebAssembly.Browser is provided by the
              .NET 10 BlazorWebAssembly SDK — adding an explicit PackageReference
              causes NU1504 "duplicate PackageReference" during restore. -->
-        <PackageReference Include="Spectre.Console.Cli" Version="0.55.0"/>
+        <PackageReference Include="Spectre.Console.Cli" Version="0.57.2"/>
         <PackageReference Include="Spectre.Console.Testing" Version="0.57.2" />
     </ItemGroup>
 

+ 1 - 1
RackPeek.Web/RackPeek.Web.csproj

@@ -24,7 +24,7 @@
     </ItemGroup>
 
     <ItemGroup>
-      <PackageReference Include="ModelContextProtocol.AspNetCore" Version="2.2.0" />
+      <PackageReference Include="ModelContextProtocol.AspNetCore" Version="2.3.0" />
     </ItemGroup>
 
 </Project>

+ 8 - 8
RackPeek/RackPeek.csproj

@@ -9,15 +9,15 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.12"/>
-        <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="10.0.12"/>
-        <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="10.0.12"/>
-        <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.12"/>
-        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.12"/>
-        <PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.12"/>
-        <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.12"/>
+        <PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.13"/>
+        <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="10.0.13"/>
+        <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="10.0.13"/>
+        <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.13"/>
+        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.13"/>
+        <PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.13"/>
+        <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.13"/>
         <PackageReference Include="Spectre.Console" Version="0.57.2"/>
-        <PackageReference Include="Spectre.Console.Cli" Version="0.55.0"/>
+        <PackageReference Include="Spectre.Console.Cli" Version="0.57.2"/>
         <PackageReference Include="Spectre.Console.Testing" Version="0.57.2"/>
         <PackageReference Include="YamlDotNet" Version="18.1.0"/>
     </ItemGroup>

+ 3 - 3
Shared.Rcl/Shared.Rcl.csproj

@@ -14,9 +14,9 @@
     </ItemGroup>
 
     <ItemGroup>
-        <PackageReference Include="Markdig" Version="1.4.0"/>
-        <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="10.0.12" />
-        <PackageReference Include="Spectre.Console.Cli" Version="0.55.0"/>
+        <PackageReference Include="Markdig" Version="1.4.2"/>
+        <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="10.0.13" />
+        <PackageReference Include="Spectre.Console.Cli" Version="0.57.2"/>
         <PackageReference Include="Spectre.Console.Testing" Version="0.57.2" />
     </ItemGroup>
 

+ 5 - 5
Tests.Discovery/Tests.Discovery.csproj

@@ -14,13 +14,13 @@
          is driven from the captured fixtures below. -->
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.10.1"/>
-        <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.12"/>
-        <PackageReference Include="JsonSchema.Net" Version="9.4.0"/>
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.11.0"/>
+        <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.13"/>
+        <PackageReference Include="JsonSchema.Net" Version="9.4.1"/>
         <PackageReference Include="YamlDotNet" Version="18.1.0"/>
         <PackageReference Include="xunit" Version="2.9.3"/>
-        <PackageReference Include="xunit.runner.visualstudio" Version="4.0.0"/>
-        <PackageReference Include="coverlet.collector" Version="10.0.1">
+        <PackageReference Include="xunit.runner.visualstudio" Version="4.0.1"/>
+        <PackageReference Include="coverlet.collector" Version="10.1.0">
             <PrivateAssets>all</PrivateAssets>
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
         </PackageReference>

+ 8 - 8
Tests.E2e/Tests.E2e.csproj

@@ -8,18 +8,18 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="coverlet.collector" Version="10.0.1">
+        <PackageReference Include="coverlet.collector" Version="10.1.0">
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
             <PrivateAssets>all</PrivateAssets>
         </PackageReference>
-        <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.12" />
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.10.1" />
-        <PackageReference Include="Microsoft.Playwright" Version="1.62.0" />
-        <PackageReference Include="Microsoft.Playwright.Xunit" Version="1.62.0" />
-        <PackageReference Include="ModelContextProtocol" Version="2.2.0" />
-        <PackageReference Include="Testcontainers" Version="4.15.0" />
+        <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.13" />
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.11.0" />
+        <PackageReference Include="Microsoft.Playwright" Version="1.64.1" />
+        <PackageReference Include="Microsoft.Playwright.Xunit" Version="1.64.1" />
+        <PackageReference Include="ModelContextProtocol" Version="2.3.0" />
+        <PackageReference Include="Testcontainers" Version="4.16.0" />
         <PackageReference Include="xunit" Version="2.9.3" />
-        <PackageReference Include="xunit.runner.visualstudio" Version="4.0.0">
+        <PackageReference Include="xunit.runner.visualstudio" Version="4.0.1">
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
             <PrivateAssets>all</PrivateAssets>
         </PackageReference>

+ 6 - 6
Tests.Mcp/Tests.Mcp.csproj

@@ -14,14 +14,14 @@
          client cannot observe it either. -->
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.10.1"/>
-        <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.12"/>
-        <PackageReference Include="ModelContextProtocol" Version="2.2.0"/>
-        <PackageReference Include="JsonSchema.Net" Version="9.4.0"/>
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.11.0"/>
+        <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.13"/>
+        <PackageReference Include="ModelContextProtocol" Version="2.3.0"/>
+        <PackageReference Include="JsonSchema.Net" Version="9.4.1"/>
         <PackageReference Include="YamlDotNet" Version="18.1.0"/>
         <PackageReference Include="xunit" Version="2.9.3"/>
-        <PackageReference Include="xunit.runner.visualstudio" Version="4.0.0"/>
-        <PackageReference Include="coverlet.collector" Version="10.0.1">
+        <PackageReference Include="xunit.runner.visualstudio" Version="4.0.1"/>
+        <PackageReference Include="coverlet.collector" Version="10.1.0">
             <PrivateAssets>all</PrivateAssets>
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
         </PackageReference>

+ 7 - 7
Tests/Tests.csproj

@@ -8,19 +8,19 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="coverlet.collector" Version="10.0.1">
+        <PackageReference Include="coverlet.collector" Version="10.1.0">
           <PrivateAssets>all</PrivateAssets>
           <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
         </PackageReference>
-        <PackageReference Include="JsonSchema.Net" Version="9.4.0"/>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.10.1" />
+        <PackageReference Include="JsonSchema.Net" Version="9.4.1"/>
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.11.0" />
         <PackageReference Include="NSubstitute" Version="6.2.0"/>
         <PackageReference Include="xunit" Version="2.9.3"/>
-        <PackageReference Include="xunit.runner.visualstudio" Version="4.0.0"/>
+        <PackageReference Include="xunit.runner.visualstudio" Version="4.0.1"/>
         <PackageReference Include="Spectre.Console.Testing" Version="0.57.2" />
-        <PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.12" />
-        <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.12"/>
-        <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.12"/>
+        <PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.13" />
+        <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.13"/>
+        <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.13"/>
     </ItemGroup>
 
     <ItemGroup>

+ 13 - 10
mobile/lib/design.dart

@@ -202,9 +202,9 @@ class FilterPill extends StatelessWidget {
         onTap: onTap,
         borderRadius: BorderRadius.circular(ScribeSpacing.minTarget / 2),
         child: Container(
-          height: ScribeSpacing.minTarget - 8,
+          height: ScribeSpacing.minTarget - 4,
           alignment: Alignment.center,
-          padding: const EdgeInsets.symmetric(horizontal: 16),
+          padding: const EdgeInsets.symmetric(horizontal: 12),
           decoration: BoxDecoration(
             color: selected ? ScribeColors.accent : ScribeColors.surface,
             borderRadius:
@@ -388,23 +388,26 @@ class ScribeSheet extends StatelessWidget {
     super.key,
     required this.children,
     this.scrollable = true,
+    this.horizontalPadding = ScribeSpacing.screenMargin,
+    this.sectionGap = ScribeSpacing.majorSectionGap,
   });
 
   final List<Widget> children;
   final bool scrollable;
+  final double horizontalPadding;
+  final double sectionGap;
 
   @override
   Widget build(BuildContext context) {
     final Column content = Column(
       crossAxisAlignment: CrossAxisAlignment.stretch,
-      children: _spaced(children),
+      children: _spaced(children, sectionGap),
     );
 
     if (!scrollable) {
       return SafeArea(
         child: Padding(
-          padding: const EdgeInsets.symmetric(
-              horizontal: ScribeSpacing.screenMargin),
+          padding: EdgeInsets.symmetric(horizontal: horizontalPadding),
           child: content,
         ),
       );
@@ -412,22 +415,22 @@ class ScribeSheet extends StatelessWidget {
 
     return SafeArea(
       child: SingleChildScrollView(
-        padding: const EdgeInsets.symmetric(
-            horizontal: ScribeSpacing.screenMargin, vertical: 12),
+        padding: EdgeInsets.symmetric(
+            horizontal: horizontalPadding, vertical: 8),
         child: Column(
           crossAxisAlignment: CrossAxisAlignment.stretch,
-          children: _spaced(children),
+          children: _spaced(children, sectionGap),
         ),
       ),
     );
   }
 
-  static List<Widget> _spaced(List<Widget> children) {
+  static List<Widget> _spaced(List<Widget> children, double sectionGap) {
     final List<Widget> result = [];
     for (var i = 0; i < children.length; i++) {
       result.add(children[i]);
       if (i < children.length - 1) {
-        result.add(const SizedBox(height: ScribeSpacing.majorSectionGap));
+        result.add(SizedBox(height: sectionGap));
       }
     }
     return result;

+ 32 - 54
mobile/lib/screens/resources_tab.dart

@@ -12,7 +12,6 @@ class ResourceFilter {
     this.hardwareOnly = false,
     this.systemType,
     this.systemOs,
-    this.hasIpAddress = false,
     this.tag,
     this.labelKey,
   });
@@ -21,7 +20,6 @@ class ResourceFilter {
   final bool hardwareOnly;
   final String? systemType;
   final String? systemOs;
-  final bool hasIpAddress;
   final String? tag;
   final String? labelKey;
 }
@@ -62,7 +60,6 @@ class _ResourcesTabState extends State<ResourcesTab> {
   bool _hardwareOnly = false;
   String? _systemType;
   String? _systemOs;
-  bool _hasIpAddress = false;
   String? _tag;
   String? _labelKey;
   late final TextEditingController _searchController;
@@ -95,7 +92,6 @@ class _ResourcesTabState extends State<ResourcesTab> {
     _hardwareOnly = filter.hardwareOnly;
     _systemType = filter.systemType;
     _systemOs = filter.systemOs;
-    _hasIpAddress = filter.hasIpAddress;
     _tag = filter.tag;
     _labelKey = filter.labelKey;
     _query = '';
@@ -107,7 +103,6 @@ class _ResourcesTabState extends State<ResourcesTab> {
       _hardwareOnly ||
       _systemType != null ||
       _systemOs != null ||
-      _hasIpAddress ||
       _tag != null ||
       _labelKey != null ||
       _query.isNotEmpty;
@@ -120,7 +115,6 @@ class _ResourcesTabState extends State<ResourcesTab> {
       if (_hardwareOnly && !r.isHardware) return false;
       if (_systemType != null && r.type != _systemType) return false;
       if (_systemOs != null && r.os != _systemOs) return false;
-      if (_hasIpAddress && !_hasResolvedIp(r, all)) return false;
       if (_tag != null && !r.tags.contains(_tag)) return false;
       if (_labelKey != null && !r.labels.containsKey(_labelKey)) return false;
       if (query.isEmpty) return true;
@@ -139,22 +133,6 @@ class _ResourcesTabState extends State<ResourcesTab> {
     });
   }
 
-  bool _hasResolvedIp(Resource resource, List<Resource> all) {
-    final directIp = resource.ip ?? resource.network?.ip;
-    if (directIp != null && directIp.trim().isNotEmpty) return true;
-    if (resource.kind != 'Service' || resource.runsOn.length != 1) return false;
-
-    Resource? parent;
-    for (final item in all) {
-      if (item.name == resource.runsOn.single) {
-        parent = item;
-        break;
-      }
-    }
-    if (parent == null || parent.kind != 'System') return false;
-    return _hasResolvedIp(parent, all);
-  }
-
   List<String> get _systemOperatingSystems {
     final values =
         (widget.store.document?.resources ?? [])
@@ -169,14 +147,15 @@ class _ResourcesTabState extends State<ResourcesTab> {
   }
 
   List<String> get _systemTypes {
-    final values = (widget.store.document?.resources ?? [])
-        .where((resource) => resource.kind == 'System')
-        .map((resource) => resource.type)
-        .whereType<String>()
-        .where((type) => type.trim().isNotEmpty)
-        .toSet()
-        .toList()
-      ..sort();
+    final values =
+        (widget.store.document?.resources ?? [])
+            .where((resource) => resource.kind == 'System')
+            .map((resource) => resource.type)
+            .whereType<String>()
+            .where((type) => type.trim().isNotEmpty)
+            .toSet()
+            .toList()
+          ..sort();
     return values;
   }
 
@@ -210,12 +189,16 @@ class _ResourcesTabState extends State<ResourcesTab> {
       body: RefreshIndicator(
         onRefresh: widget.store.refresh,
         child: ScribeSheet(
+          horizontalPadding: 12,
+          sectionGap: 10,
           children: [
             TextField(
               controller: _searchController,
               decoration: const InputDecoration(
                 hintText: 'Search names, IPs, tags, labels',
                 prefixIcon: Icon(Icons.search),
+                isDense: true,
+                contentPadding: EdgeInsets.symmetric(vertical: 10),
               ),
               onChanged: (value) => setState(() => _query = value),
             ),
@@ -289,30 +272,25 @@ class _ResourcesTabState extends State<ResourcesTab> {
               ),
             ],
             const SizedBox(height: ScribeSpacing.relatedGap),
-            Wrap(
-              spacing: 8,
-              children: [
-                FilterPill(
-                  label: 'Has IP',
-                  selected: _hasIpAddress,
-                  onTap: () => setState(() {
-                    _hasIpAddress = !_hasIpAddress;
-                  }),
-                ),
-                if (_tag != null)
-                  FilterPill(
-                    label: 'Tag: $_tag',
-                    selected: true,
-                    onTap: () => setState(() => _tag = null),
-                  ),
-                if (_labelKey != null)
-                  FilterPill(
-                    label: 'Label: $_labelKey',
-                    selected: true,
-                    onTap: () => setState(() => _labelKey = null),
-                  ),
-              ],
-            ),
+            if (_tag != null || _labelKey != null)
+              Wrap(
+                spacing: 8,
+                runSpacing: 6,
+                children: [
+                  if (_tag != null)
+                    FilterPill(
+                      label: 'Tag: $_tag',
+                      selected: true,
+                      onTap: () => setState(() => _tag = null),
+                    ),
+                  if (_labelKey != null)
+                    FilterPill(
+                      label: 'Label: $_labelKey',
+                      selected: true,
+                      onTap: () => setState(() => _labelKey = null),
+                    ),
+                ],
+              ),
             SurfacePanel(padding: EdgeInsets.zero, child: _list(context)),
           ],
         ),

+ 10 - 10
mobile/lib/screens/summary_tab.dart

@@ -38,6 +38,8 @@ class SummaryTab extends StatelessWidget {
     return RefreshIndicator(
       onRefresh: store.refresh,
       child: ScribeSheet(
+        horizontalPadding: 14,
+        sectionGap: 12,
         children: [
           const Text('Infrastructure', style: ScribeType.hero),
           _countPanel(
@@ -93,6 +95,7 @@ class SummaryTab extends StatelessWidget {
     );
 
     return SurfacePanel(
+      padding: const EdgeInsets.all(12),
       child: Column(
         crossAxisAlignment: CrossAxisAlignment.start,
         children: [
@@ -146,6 +149,7 @@ class SummaryTab extends StatelessWidget {
   ) {
     if (counts.isEmpty) return const SizedBox.shrink();
     return SurfacePanel(
+      padding: const EdgeInsets.all(12),
       child: Column(
         crossAxisAlignment: CrossAxisAlignment.start,
         children: [
@@ -172,6 +176,7 @@ class SummaryTab extends StatelessWidget {
     final total = services['totalServices'] as int? ?? 0;
     final totalIpAddresses = services['totalIpAddresses'] as int? ?? 0;
     return SurfacePanel(
+      padding: const EdgeInsets.all(12),
       child: Row(
         children: [
           Expanded(
@@ -181,22 +186,16 @@ class SummaryTab extends StatelessWidget {
               const ResourceFilter(kind: 'Service'),
             ),
           ),
-          Expanded(
-            child: _serviceCount(
-              totalIpAddresses,
-              'IP addresses',
-              const ResourceFilter(kind: 'Service', hasIpAddress: true),
-            ),
-          ),
+          Expanded(child: _serviceCount(totalIpAddresses, 'IP addresses')),
         ],
       ),
     );
   }
 
-  Widget _serviceCount(int count, String label, ResourceFilter filter) {
+  Widget _serviceCount(int count, String label, [ResourceFilter? filter]) {
     return InkWell(
       borderRadius: BorderRadius.circular(ScribeSpacing.controlRadius),
-      onTap: () => onOpenResources(filter),
+      onTap: filter == null ? null : () => onOpenResources(filter),
       child: Padding(
         padding: const EdgeInsets.symmetric(vertical: 4),
         child: Row(
@@ -209,7 +208,7 @@ class SummaryTab extends StatelessWidget {
               ],
             ),
             const SizedBox(width: 4),
-            const Icon(Icons.chevron_right, size: 18),
+            if (filter != null) const Icon(Icons.chevron_right, size: 18),
           ],
         ),
       ),
@@ -223,6 +222,7 @@ class SummaryTab extends StatelessWidget {
   ) {
     if (counts.isEmpty) return const SizedBox.shrink();
     return SurfacePanel(
+      padding: const EdgeInsets.all(12),
       child: Column(
         crossAxisAlignment: CrossAxisAlignment.start,
         children: [

+ 2 - 2
mobile/pubspec.lock

@@ -457,10 +457,10 @@ packages:
     dependency: transitive
     description:
       name: vm_service
-      sha256: "5f37239c4851efcef929cea7824e76df7f2f0970aef85d66bbc430afa40e72f0"
+      sha256: "89b11d62e1040ee681af3b4f1c97a803ecbf010c6e8b9c5896ee5b08c3bf7b78"
       url: "https://pub.dev"
     source: hosted
-    version: "15.3.0"
+    version: "15.4.0"
   web:
     dependency: transitive
     description:

+ 1 - 1
mobile/pubspec.yaml

@@ -33,7 +33,7 @@ dependencies:
 
   # The following adds the Cupertino Icons font to your application.
   # Use with the CupertinoIcons class for iOS style icons.
-  cupertino_icons: ^1.0.8
+  cupertino_icons: ^1.0.9
   http: ^1.6.0
   flutter_secure_storage: ^11.2.0
 

+ 10 - 27
vhs/package-lock.json

@@ -9,51 +9,34 @@
       "version": "1.0.0",
       "license": "ISC",
       "dependencies": {
-        "playwright": "^1.58.2"
-      }
-    },
-    "node_modules/fsevents": {
-      "version": "2.3.2",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
-      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
-      "hasInstallScript": true,
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+        "playwright": "^1.64.1"
       }
     },
     "node_modules/playwright": {
-      "version": "1.58.2",
-      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz",
-      "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==",
+      "version": "1.64.1",
+      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.64.1.tgz",
+      "integrity": "sha512-HCrVhHr5QQuwYoswEUGTb7gGp522yflbv/ST8nF4sFWGuLjQCt17Q/EubpGGSNKYn1tsTCPZxqHhHCfVURu7UA==",
       "license": "Apache-2.0",
       "dependencies": {
-        "playwright-core": "1.58.2"
+        "playwright-core": "1.64.1"
       },
       "bin": {
         "playwright": "cli.js"
       },
       "engines": {
-        "node": ">=18"
-      },
-      "optionalDependencies": {
-        "fsevents": "2.3.2"
+        "node": ">=20"
       }
     },
     "node_modules/playwright-core": {
-      "version": "1.58.2",
-      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz",
-      "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==",
+      "version": "1.64.1",
+      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.64.1.tgz",
+      "integrity": "sha512-BC4s8y6SEVA6Q1rSxZTto8hI1KUI3xXlHGZgvGqP4IqPdI/a6FX9H29IDNz7vKo9l21b6LfYosScPL6M3xxyDQ==",
       "license": "Apache-2.0",
       "bin": {
         "playwright-core": "cli.js"
       },
       "engines": {
-        "node": ">=18"
+        "node": ">=20"
       }
     }
   }

+ 1 - 1
vhs/package.json

@@ -10,6 +10,6 @@
   "author": "",
   "license": "ISC",
   "dependencies": {
-    "playwright": "^1.58.2"
+    "playwright": "^1.64.1"
   }
 }