SilkyFowl.Avalonia.FuncUI.LiveView 0.0.3-preview01

This is a prerelease version of SilkyFowl.Avalonia.FuncUI.LiveView.
There is a newer version of this package available.
See the version list below for details.
dotnet add package SilkyFowl.Avalonia.FuncUI.LiveView --version 0.0.3-preview01                
NuGet\Install-Package SilkyFowl.Avalonia.FuncUI.LiveView -Version 0.0.3-preview01                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SilkyFowl.Avalonia.FuncUI.LiveView" Version="0.0.3-preview01" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add SilkyFowl.Avalonia.FuncUI.LiveView --version 0.0.3-preview01                
#r "nuget: SilkyFowl.Avalonia.FuncUI.LiveView, 0.0.3-preview01"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install SilkyFowl.Avalonia.FuncUI.LiveView as a Cake Addin
#addin nuget:?package=SilkyFowl.Avalonia.FuncUI.LiveView&version=0.0.3-preview01&prerelease

// Install SilkyFowl.Avalonia.FuncUI.LiveView as a Cake Tool
#tool nuget:?package=SilkyFowl.Avalonia.FuncUI.LiveView&version=0.0.3-preview01&prerelease                

Avalonia.FuncUI.LiveView

Live fs/fsx previewer for Avalonia.FuncUI.

日本語(Javanese language)

What is this ?

Avalonia.FuncUI.LiveView is an experimental FsAutoComplete extension that aims to provide a real-time preview of the UI of Avalonia.FuncUI. Analyzers.SDK, it displays a real-time preview of the content of the F# file you are editing in the editor. No need to save the file to update the preview.

How to use

Warning Avalonia.FuncUI.LiveView is incomplete. It has not been fully tested and is not intended for use in a production environment. Please use this tool at your own risk.

The following is a case of using VScode and Paket.

Preliminary Preparation

Network

Use localhost:8080 for communication between Analyzer and LivePreview. Communication method will be improved in the future.

VScode

Install Ionide.Ionide-fsharp in VScode.

install extension

FuncUI Setup

Create a working directory and launch VScode.

mkdir YourFuncUIApp
cd YourFuncUIApp
dotnet new tool-manifest
dotnet new gitignore
code .

Create a project.

dotnet new sln
dotnet new console -o ./src/YourFuncUIApp -lang f#
dotnet sln add ./src/YourFuncUIApp/YourFuncUIApp.fsproj
Setup F# formatter
dotnet tool install fantomas

Create .editorconfig. The contents are as follows:

root = true

[*]
indent_style=space
indent_size=4
charset=utf-8
trim_trailing_whitespace=true
insert_final_newline=false

[*.{fs,fsx}]
fsharp_experimental_elmish=true
Paket Setup
dotnet tool install paket

Create paket.dependencies. The contents are as follows:

source https://api.nuget.org/v3/index.json

storage: none

nuget FSharp.Core content: none
nuget Avalonia.FuncUI 1.0.1
nuget Avalonia.Desktop 11.0.3
nuget Avalonia.Themes.Fluent 11.0.3
nuget SilkyFowl.Avalonia.FuncUI.LiveView 0.0.1.1

group Analyzers
    source https://api.nuget.org/v3/index.json
    storage: storage
    nuget SilkyFowl.Avalonia.FuncUI.LiveView.Analyzer 0.0.1.1
dotnet paket convert-from-nuget --force --no-install --no-auto-restore
dotnet paket add -p ./src/YourFuncUIApp/YourFuncUIApp.fsproj Avalonia.FuncUI --no-install
dotnet paket add -p ./src/YourFuncUIApp/YourFuncUIApp.fsproj Avalonia.Desktop --no-install
dotnet paket add -p ./src/YourFuncUIApp/YourFuncUIApp.fsproj Avalonia.Themes.Fluent --no-install
dotnet paket add -p ./src/YourFuncUIApp/YourFuncUIApp.fsproj SilkyFowl.Avalonia.FuncUI.LiveView --no-install
dotnet paket install
Without Paket

Add a dependency to YourFuncUIApp.fsproj as follows:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="Program.fs" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Avalonia.Desktop" Version="11.0.3" />
    <PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.3" />
    <PackageReference Include="Avalonia.FuncUI" Version="1.0.1" />
    <PackageReference Include="SilkyFowl.Avalonia.FuncUI.LiveView" Version="0.0.1.1" />
    
    <PackageReference Include="MessagePack" Version="2.5.124" />
  </ItemGroup>
</Project>

Install analyzer with nuget.

nuget install SilkyFowl.Avalonia.FuncUI.LiveView.Analyzer -Version 0.0.1.1 -OutputDirectory packages/analyzers

Checking behavior

Rewrite Program.fs as follows:

namespace CounterApp

open Avalonia
open Avalonia.Controls.ApplicationLifetimes
open Avalonia.Themes.Fluent
open Avalonia.FuncUI.Hosts
open Avalonia.Controls
open Avalonia.FuncUI
open Avalonia.FuncUI.DSL
open Avalonia.Layout

open Avalonia.FuncUI.LiveView
open Avalonia.FuncUI.LiveView.Core.Types

module Main =
    [<LivePreview>]
    let view () =
        Component(fun ctx ->
            let state = ctx.useState 0

            DockPanel.create [
                DockPanel.children [
                    Button.create [
                        Button.dock Dock.Bottom
                        Button.onClick (fun _ -> state.Set(state.Current - 1))
                        Button.content "-"
                        Button.horizontalAlignment HorizontalAlignment.Stretch
                        Button.horizontalContentAlignment HorizontalAlignment.Center
                    ]
                    Button.create [
                        Button.dock Dock.Bottom
                        Button.onClick (fun _ -> state.Set(state.Current + 1))
                        Button.content "+"
                        Button.horizontalAlignment HorizontalAlignment.Stretch
                        Button.horizontalContentAlignment HorizontalAlignment.Center
                    ]
                    TextBlock.create [
                        TextBlock.dock Dock.Top
                        TextBlock.fontSize 48.0
                        TextBlock.verticalAlignment VerticalAlignment.Center
                        TextBlock.horizontalAlignment HorizontalAlignment.Center
                        TextBlock.text (string state.Current)
                    ]
                ]
            ])

type MainWindow() =
    inherit HostWindow()

    do
        base.Title <- "Counter Example"
        base.Content <- Main.view ()

module LiveView =
    let enabled =
        match System.Environment.GetEnvironmentVariable("FUNCUI_LIVEPREVIEW") with
        | null -> false
        | "1" -> true
        | _ -> false

type App() =
    inherit Application()

    override this.Initialize() =
        this.Styles.Add(FluentTheme())
        this.RequestedThemeVariant <- Styling.ThemeVariant.Dark

    override this.OnFrameworkInitializationCompleted() =
        match this.ApplicationLifetime with
        | :? IClassicDesktopStyleApplicationLifetime as desktopLifetime ->
            desktopLifetime.MainWindow <-
                if LiveView.enabled then
                    LiveViewWindow() :> Window
                else
                    MainWindow()
        | _ -> ()

#if DEBUG
        this.AttachDevTools()
#endif

module Program =

    [<EntryPoint>]
    let main (args: string[]) =
        AppBuilder
            .Configure<App>()
            .UsePlatformDetect()
            .UseSkia()
            .StartWithClassicDesktopLifetime(args)

Start the program and check if it works.

dotnet run --project ./src/YourFuncUIApp/YourFuncUIApp.fsproj

vscode settings

  • .vscode/launch.json
{
    "configurations": [
        {
            "name": "FuncUI Launch",
            "type": "coreclr",
            "request": "launch",
            "preLaunchTask": "build",
            "program": "${workspaceFolder}/src/YourFuncUIApp/bin/Debug/net7.0/YourFuncUIApp.dll",
            "args": [],
            "cwd": "${workspaceFolder}",
            "stopAtEntry": false,
            "console": "internalConsole"
        },
        {
            "name": "FuncUI Launch (Preview)",
            "type": "coreclr",
            "request": "launch",
            "preLaunchTask": "build",
            "program": "${workspaceFolder}/src/YourFuncUIApp/bin/Debug/net7.0/YourFuncUIApp.dll",
            "args": [],
            "env": {
                "FUNCUI_LIVEPREVIEW": "1"
            },
            "cwd": "${workspaceFolder}",
            "stopAtEntry": false,
            "console": "internalConsole"
        }
    ]
}
  • .vscode/tasks.json
{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "build",
            "command": "dotnet",
            "type": "shell",
            "args": [
                "build",
            ],
            "problemMatcher": [
                "$msCompile"
            ],
            "group": "build"
        },
    ]
}
  • .vscode/settings.json
{
    "FSharp.enableAnalyzers": true,
    "FSharp.analyzersPath": [
        "packages/analyzers"
    ],
}

Now start the debugger and make sure it works.

First-Debug

When launched, proceed to the next step.

First-Debug-success

Check if FuncUI Analyzer works

  • FSharp.enableAnalyzers is true
  • Analyzer Dll exists in FSharp.analyzersPath.

With these conditions, editing the F# code recognized in the Solution Explorer of Ionide for F# will start the FuncUi Analyzer.

Active Analyzer

Warning You can analyze fsx scripts, etc. that are not recognized by the Solution Explorer, but you cannot start the FuncUi Analyzer.

fsx-in-explorer

there-is-no-fsx-in-fs-explorer

Launch LivePreview

When using debugger

Change the debugger setting to FuncUI Launch(Live Preview) and start it.

Enjoy-It!!

When not using debugger

Set environment variables.

bash

export FUNCUI_LIVEPREVIEW=1

cmd

set FUNCUI_LIVEPREVIEW=1

powershell

$env:FUNCUI_LIVEPREVIEW = 1

Note Without the debugger, response to code changes is improved.

dotnet build -c Release
dotnet ./src/YourFuncUIApp/bin/Release/net7.0/YourFuncUIApp.dll

Known Issues, Limitations

  • Write quickly.

If there is a DU in the file that consists entirely of valued case labels, it cannot be previewed

cant...

workaround

Make it a DU that includes one or more cases with no value

DU-with-any-no-value-case

DU are defined in a separate file
// TypesDefinition.fs
module ElmishModule =
    type State = { watermark: string }
    let init = { watermark = "" }

    type Msg =
        | SetWatermark of string

    let update msg state =
        match msg with
        | SetWatermark test -> { state with watermark = test }
// OtherFile.fs
open Sample.ElmishModule

let view (state:State) dispatch =

    StackPanel.create [
        StackPanel.spacing 10.0
        StackPanel.children [
            TextBox.create [
                TextBox.watermark state.watermark
                TextBox.horizontalAlignment HorizontalAlignment.Stretch
            ]
            Button.create [
                Button.background "DarkBlue"
                Button.content "Set Watermark"
                Button.onClick (fun _ -> SetWatermark "test" |> dispatch)
                Button.horizontalAlignment HorizontalAlignment.Stretch
            ]


            Button.create [
                Button.content "Unset Watermark"
                Button.onClick (fun _ -> SetWatermark "" |> dispatch)
                Button.horizontalAlignment HorizontalAlignment.Stretch
            ]
        ]
    ]


type Host() as this =
    inherit Hosts.HostControl()

    do
        Elmish.Program.mkSimple (fun () -> init) update view
        |> Program.withHost this
        |> Elmish.Program.run

[<LivePreview>]
let preview () = ViewBuilder.Create<Host> []

mechanism

  • Write quickly.

Plan

→ SilkyFowl/Avalonia.FuncUI.LiveView#4

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.0.4-preview06 90 5/25/2024
0.0.4-preview05 101 3/10/2024
0.0.4-preview04 92 2/17/2024
0.0.4-preview03 106 2/12/2024
0.0.4-preview02 143 12/20/2023
0.0.4-preview01 164 11/3/2023
0.0.3 211 8/28/2023
0.0.3-preview01 126 8/28/2023
0.0.2-preview03 85 5/6/2023
0.0.2-preview02 77 5/6/2023
0.0.2-preview01 81 5/5/2023
0.0.1.1 151 8/14/2023
0.0.1 160 5/5/2023
0.0.1-alpha4 153 6/22/2022
0.0.1-alpha3 155 6/22/2022
0.0.1-alpha2 157 6/15/2022

* breaking: Analyzer changed from library to dotnet-tool.
* paket
 * change resolver strategy max to min.
   * see:https://fsprojects.github.io/Paket/nuget-dependencies.html#The-modifier-1
 * fix version range.