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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
17 changes: 17 additions & 0 deletions .claude/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"version": "0.0.1",
"configurations": [
{
"name": "renderer-preview",
"runtimeExecutable": "python3",
"runtimeArgs": [
"-m",
"http.server",
"4173",
"--directory",
"vscode-frysharp"
],
"port": 4173
}
]
}
134 changes: 134 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
name: CI

# The headless engine and the VS Code extension: what a change to either must not break. (The desktop app's own suite and
# the release gate are in release.yml.)

on:
push:
branches: [main, Server]
pull_request:

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
engine:
name: Engine (Core, Sidecar)
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4

- name: Setup .NET 10
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'

# The sidecar's tests run real Python and Node, as the engine's users do.
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '22'

# The Java tests run the real JDK (javac, java, JShell).
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'

# The Go tests run the real toolchain: every cell is built with `go build`.
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 'stable'

# The Rust tests run the real toolchain: every cell is built with Cargo (a runner has no crates cached, so the first cell is slow).
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable

# The SQL tests run the real sqlite3: macOS and Linux runners have it (a missing one fails there); a Windows runner
# does not, and its SQL tests are skipped with the reason.
# The F# tests run the real `dotnet fsi` (the SDK that setup-dotnet installs) and restore a small NuGet package.
- name: Core tests
run: dotnet test Tests.Core/FrySharp.Core.Tests.csproj
env:
FRY_REQUIRE_SQL: ${{ matrix.os == 'windows-latest' && '' || '1' }}
FRY_REQUIRE_FSHARP: '1'
FRY_REQUIRE_JAVA: '1'
FRY_REQUIRE_RUST: '1'
FRY_REQUIRE_GO: '1'
# a Windows runner's MSVC needs its own environment: the C++ tests are skipped there, with the reason
FRY_REQUIRE_CPP: ${{ matrix.os == 'windows-latest' && '' || '1' }}
FRY_TEST_NETWORK: '1'

- name: Sidecar tests
run: dotnet test Tests.Sidecar/FrySharp.Sidecar.Tests.csproj
env:
FRY_REQUIRE_PYTHON: '1'
FRY_REQUIRE_SQL: ${{ matrix.os == 'windows-latest' && '' || '1' }}
FRY_REQUIRE_FSHARP: '1'
FRY_REQUIRE_JAVA: '1'
FRY_REQUIRE_RUST: '1'
FRY_REQUIRE_GO: '1'
# a Windows runner's MSVC needs its own environment: the C++ tests are skipped there, with the reason
FRY_REQUIRE_CPP: ${{ matrix.os == 'windows-latest' && '' || '1' }}
FRY_TEST_NETWORK: '1'

extension:
name: Extension (lint, types, tests)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup .NET 10
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
cache-dependency-path: vscode-frysharp/package-lock.json

# The client's tests start the real engine.
- name: Build the sidecar
run: dotnet build FrySharp.Sidecar/FrySharp.Sidecar.csproj

- name: Install
working-directory: vscode-frysharp
run: npm ci

- name: Lint
working-directory: vscode-frysharp
run: npm run lint

- name: Type check
working-directory: vscode-frysharp
run: npm run typecheck

- name: Tests
working-directory: vscode-frysharp
run: npm test
env:
FRY_REQUIRE_SIDECAR: '1'

# The tests write the notebooks the extension saves (test/fixtures/written), which Tests.Core reads back with the
# desktop's reader: they are committed, so a change in what the extension writes shows up here.
- name: The notebooks the extension writes are the committed ones
run: git diff --exit-code -- vscode-frysharp/test/fixtures

- name: Bundle
working-directory: vscode-frysharp
run: node esbuild.js --production
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,8 @@ Desktop.ini
*.log
.cache/
*.metadata.json

## FrySharp web mode
# Never commit a server token (older builds wrote one into the workspace).
.frysharp-token
web/node_modules/
3 changes: 3 additions & 0 deletions CSharpEditorPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ public class CSharpEditorPlugin : IFryPlugin

public Task ApplyAsync(IFryPluginContext ctx, CancellationToken ct = default)
{
// Scripts compile against the desktop API (Display, charts, Avalonia), so fix that before anything compiles.
PdfEditorApp.Plugins.CSharpEditor.Services.Roslyn.DesktopScriptHost.Configure();

CSharpStudioHostViewModel? activeHost = null;

void NavigateToStudio(IServiceProvider sp)
Expand Down
30 changes: 8 additions & 22 deletions CSharpEditorPlugin.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<Version>2.0.4</Version>
<Company>Code Fry Dev</Company>
<Description>Full-featured interactive FrySharp Script Automation and Polyglot Workspace for FryPDF</Description>
<DefaultItemExcludes>$(DefaultItemExcludes);Runner/**;Tests/**;tools/**;samples/**</DefaultItemExcludes>
<DefaultItemExcludes>$(DefaultItemExcludes);Runner/**;Tests/**;tools/**;samples/**;FrySharp.Core/**;FrySharp.Sidecar/**;FrySharp.Cli/**;Tests.Core/**;Tests.Sidecar/**;vscode-frysharp/**</DefaultItemExcludes>
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

Expand All @@ -26,6 +26,10 @@
<None Include="samples/**" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="FrySharp.Core\FrySharp.Core.csproj" />
</ItemGroup>

<ItemGroup>
<!-- The words of the visual contract (ChartType, ElementState, ...): charts, 3D plots and visualizers all use them. -->
<Using Include="PdfEditorApp.Plugins.CSharpEditor.Visuals.Spec" />
Expand Down Expand Up @@ -58,29 +62,9 @@

<ItemGroup>
<None Include="plugin.json" CopyToOutputDirectory="PreserveNewest" />
<!-- ECharts and echarts-gl are read by HtmlAssets from the plugin's resources, so they ship once, not as Avalonia resources too. -->
<AvaloniaResource Include="Assets\**" Exclude="Assets\echarts\**" />
<EmbeddedResource Include="Assets/echarts/*.js" LogicalName="ECharts.%(Filename)%(Extension)" />
<!-- The notebook's Python kernel program, written out next to the plugin's data when a Python cell first runs. -->
<EmbeddedResource Include="Services/Languages/Python/Kernel/*.py" LogicalName="PythonKernel.%(Filename)%(Extension)" />
<EmbeddedResource Include="Services/Languages/Python/Runtime/*.py" LogicalName="PythonKernel.%(Filename)%(Extension)" />
<EmbeddedResource Include="Services/Languages/Python/Runtime/*.py" LogicalName="PythonRuntime.%(Filename)%(Extension)" />
<!-- The notebook's JavaScript kernel program, written out next to the plugin's data when a JavaScript cell first runs. -->
<EmbeddedResource Include="Services/Languages/JavaScript/Kernel/*.js" LogicalName="JavaScriptKernel.%(Filename)%(Extension)" />
<EmbeddedResource Include="Services/Languages/JavaScript/Runtime/*.js" LogicalName="JavaScriptKernel.%(Filename)%(Extension)" />
<EmbeddedResource Include="Services/Languages/JavaScript/Runtime/*.js" LogicalName="JavaScriptRuntime.%(Filename)%(Extension)" />
<!-- The notebook's Java kernel program, written out next to the plugin's data when a Java cell first runs. -->
<EmbeddedResource Include="Services/Languages/Java/Kernel/*.java" LogicalName="JavaKernel.%(Filename)%(Extension)" />
<EmbeddedResource Include="Services/Languages/Java/Runtime/*.java" LogicalName="JavaKernel.%(Filename)%(Extension)" />
<EmbeddedResource Include="Services/Languages/Java/Runtime/*.java" LogicalName="JavaRuntime.%(Filename)%(Extension)" />
<AvaloniaResource Include="Assets\**" />
<!-- The Go display and visual runtime package -->
<EmbeddedResource Include="Services/Languages/Go/Runtime/*.go" LogicalName="GoRuntime.%(Filename)%(Extension)" />
<!-- The Rust display and visual runtime package -->
<EmbeddedResource Include="Services/Languages/Rust/Runtime/*.*" LogicalName="RustRuntime.%(Filename)%(Extension)" />
<!-- The C++ display and visual runtime header -->
<EmbeddedResource Include="Services/Languages/Cpp/Runtime/*.hpp" LogicalName="CppRuntime.%(Filename)%(Extension)" />
<!-- The F# display and visual runtime script -->
<EmbeddedResource Include="Services/Languages/FSharp/Runtime/*.fsx" LogicalName="FSharpRuntime.%(Filename)%(Extension)" />
</ItemGroup>

<!-- Packaging into .fryplugin distribution archive -->
Expand All @@ -97,6 +81,8 @@
<PluginFiles Include="$(TargetDir)CSharpEditorPlugin.dll" />
<PluginFiles Include="$(TargetDir)CSharpEditorPlugin.xml" Condition="Exists('$(TargetDir)CSharpEditorPlugin.xml')" />
<PluginFiles Include="$(TargetDir)CSharpEditorPlugin.deps.json" Condition="Exists('$(TargetDir)CSharpEditorPlugin.deps.json')" />
<PluginFiles Include="$(TargetDir)FrySharp.Core.dll" />
<PluginFiles Include="$(TargetDir)FrySharp.Core.xml" Condition="Exists('$(TargetDir)FrySharp.Core.xml')" />
<PluginFiles Include="$(TargetDir)AvaloniaEdit.dll" Condition="Exists('$(TargetDir)AvaloniaEdit.dll')" />
<PluginFiles Include="$(TargetDir)AvaloniaEdit.TextMate.dll" Condition="Exists('$(TargetDir)AvaloniaEdit.TextMate.dll')" />
<PluginFiles Include="$(TargetDir)TextMateSharp.dll" Condition="Exists('$(TargetDir)TextMateSharp.dll')" />
Expand Down
5 changes: 5 additions & 0 deletions CSharpEditorPlugin.slnx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
<Solution>
<Project DefaultStartup="true" Path="Runner/CSharpEditorPlugin.Runner.csproj" />
<Project Path="CSharpEditorPlugin.csproj" />
<Project Path="FrySharp.Core/FrySharp.Core.csproj" />
<Project Path="FrySharp.Sidecar/FrySharp.Sidecar.csproj" />
<Project Path="FrySharp.Cli/FrySharp.Cli.csproj" />
<Project Path="Tests/CSharpEditorPlugin.Tests.csproj" />
<Project Path="Tests.Core/FrySharp.Core.Tests.csproj" />
<Project Path="Tests.Sidecar/FrySharp.Sidecar.Tests.csproj" />
<Project Path="tools/UiSnapshots/UiSnapshots.csproj" />
</Solution>
2 changes: 1 addition & 1 deletion Charting/Controls/ChartCanvasControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ private void RenderHoverTooltip(DrawingContext context, ChartHitTestResult hit)
var color = ChartPaletteService.ParseColor(
!string.IsNullOrEmpty(hit.Point.CustomColor)
? hit.Point.CustomColor
: (!string.IsNullOrEmpty(hit.Series.Color) ? hit.Series.Color : Options?.PrimaryColor ?? "#4ec9b0"));
: (!string.IsNullOrEmpty(hit.Series.Color) ? hit.Series.Color : ChartPaletteService.GetSeriesColor(Options?.Series.IndexOf(hit.Series) ?? 0)));

var haloBrush = new SolidColorBrush(Color.FromArgb(50, color.R, color.G, color.B));
var dotBrush = new SolidColorBrush(color);
Expand Down
27 changes: 4 additions & 23 deletions Charting/Services/ChartPaletteService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,9 @@

namespace PdfEditorApp.Plugins.CSharpEditor.Charting.Services;

/// <summary>Chart colours as Avalonia brushes, and series colours from the active desktop theme (<see cref="ChartPalette"/> otherwise).</summary>
public static class ChartPaletteService
{
public static readonly string DefaultCyan = "#4ec9b0";
public static readonly string AccentAmber = "#f59e0b";
public static readonly string AccentIndigo = "#6366f1";
public static readonly string AccentEmerald = "#10b981";
public static readonly string AccentRose = "#f43f5e";
public static readonly string AccentSky = "#0ea5e9";
public static readonly string AccentPurple = "#a855f7";

public static readonly IReadOnlyList<string> ExpressivePalette = new[]
{
"#4ec9b0", // Cyan / Teal
"#60a5fa", // Blue
"#f59e0b", // Amber
"#f43f5e", // Rose
"#10b981", // Emerald
"#a855f7", // Purple
"#38bdf8", // Sky
"#fb923c" // Orange
};

public static IBrush GetBrush(string? colorHexOrName, double opacity = 1.0)
{
var color = ParseColor(colorHexOrName);
Expand All @@ -39,7 +20,7 @@ public static IBrush GetBrush(string? colorHexOrName, double opacity = 1.0)
public static Color ParseColor(string? colorHexOrName)
{
if (string.IsNullOrWhiteSpace(colorHexOrName))
return Color.Parse(DefaultCyan);
return Color.Parse(ChartPalette.DefaultCyan);

try
{
Expand All @@ -50,7 +31,7 @@ public static Color ParseColor(string? colorHexOrName)
}
catch
{
return Color.Parse(DefaultCyan);
return Color.Parse(ChartPalette.DefaultCyan);
}
}

Expand All @@ -68,7 +49,7 @@ public static string GetSeriesColor(int seriesIndex)
return themed;
}

return ExpressivePalette[seriesIndex % ExpressivePalette.Count];
return ChartPalette.DefaultSeriesColor(seriesIndex);
}

private const int ThemeSeriesCount = 8;
Expand Down
Loading
Loading