Repository: FireCubeStudios/Clippy
Branch: master
Commit: 5734b6ee2e89
Files: 79
Total size: 7.4 MB
Directory structure:
gitextract_9yfi5vry/
├── .gitattributes
├── .gitignore
├── Clippy/
│ ├── App.xaml
│ ├── App.xaml.cs
│ ├── Clippy.csproj
│ ├── Controls/
│ │ ├── APIBox.xaml
│ │ ├── APIBox.xaml.cs
│ │ ├── MessageTriangle.xaml
│ │ ├── MessageTriangle.xaml.cs
│ │ ├── Messages/
│ │ │ ├── ClippyMessage.xaml
│ │ │ ├── ClippyMessage.xaml.cs
│ │ │ ├── SystemMessage.xaml
│ │ │ ├── SystemMessage.xaml.cs
│ │ │ ├── UserMessage.xaml
│ │ │ └── UserMessage.xaml.cs
│ │ ├── ShineUITextblock.xaml
│ │ └── ShineUITextblock.xaml.cs
│ ├── Helpers/
│ │ ├── ClippyInputHelper.cs
│ │ ├── ClippyKeyboardListener.cs
│ │ ├── KeyboardHelper.cs
│ │ └── MessageSelector.cs
│ ├── MainWindow.xaml
│ ├── MainWindow.xaml.cs
│ ├── NativeMethods.txt
│ ├── Package.appxmanifest
│ ├── Properties/
│ │ └── launchSettings.json
│ ├── Services/
│ │ ├── KeyService.cs
│ │ └── SettingsService.cs
│ ├── SettingsWindow.xaml
│ ├── SettingsWindow.xaml.cs
│ ├── Windows/
│ │ ├── MicaWindow.cs
│ │ ├── TransparentBackdrop.cs
│ │ └── WindowsSystemDispatcherQueueHelper.cs
│ └── app.manifest
├── Clippy.Core/
│ ├── Classes/
│ │ └── Message.cs
│ ├── Clippy.Core.csproj
│ ├── Constants.cs
│ ├── Enums/
│ │ └── Role.cs
│ ├── Factories/
│ │ └── MessageFactory.cs
│ ├── Interfaces/
│ │ └── IMessage.cs
│ ├── Runtime/
│ │ └── IsExternalInit.cs
│ ├── Services/
│ │ ├── IChatService.cs
│ │ ├── IKeyService.cs
│ │ └── ISettingsService.cs
│ └── ViewModels/
│ ├── ClippyViewModel.cs
│ └── Messages/
│ ├── ClippyMessageViewModel.Streaming.cs
│ ├── ClippyMessageViewModel.cs
│ ├── MessageViewModel.cs
│ ├── SystemMessageViewModel.cs
│ └── UserMessageViewModel.cs
├── Clippy.sln
├── CubeKit.UI/
│ ├── Controls/
│ │ ├── Settings/
│ │ │ ├── Converters.cs
│ │ │ ├── SettingsBlockControl.xaml
│ │ │ ├── SettingsBlockControl.xaml.cs
│ │ │ ├── SettingsDisplayControl.xaml
│ │ │ └── SettingsDisplayControl.xaml.cs
│ │ └── Toolkit/
│ │ ├── DesignTimeHelpers.cs
│ │ ├── DropShadowPanel.Properties.cs
│ │ ├── DropShadowPanel.xaml
│ │ ├── DropShadowPanel.xaml.cs
│ │ └── IAlphaMaskProvider.cs
│ ├── Converters/
│ │ └── BooleanToVisibilityConverter.cs
│ ├── CubeKit.UI.csproj
│ ├── Helpers/
│ │ └── NativeHelper.cs
│ ├── Icons/
│ │ ├── FluentIconElement.cs
│ │ ├── FluentIconSource.cs
│ │ ├── FluentSymbol.cs
│ │ ├── FluentSymbolIcon.Icons.cs
│ │ └── FluentSymbolIcon.cs
│ ├── Materials/
│ │ ├── Bloom.html
│ │ ├── BloomView.xaml
│ │ └── BloomView.xaml.cs
│ ├── Styles/
│ │ ├── CubeThemeDictionary.xaml
│ │ ├── CubeUI.xaml
│ │ ├── GlowGradientUI.xaml
│ │ └── WindowChrome.xaml
│ └── Themes/
│ └── Generic.xaml
├── LICENSE.txt
└── README.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain
================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
/Clippy.Core/Services/ChatService.cs
/Clippy/Tray
================================================
FILE: Clippy/App.xaml
================================================
================================================
FILE: Clippy/App.xaml.cs
================================================
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using Microsoft.UI.Xaml.Shapes;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Clippy.Core.ViewModels;
using Clippy.Core.Services;
using Clippy.Services;
using System.Threading.Tasks;
using System.Runtime.ExceptionServices;
using WinUIEx;
using Clippy.Tray;
using System.Threading;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace Clippy
{
///
/// Provides application-specific behavior to supplement the default Application class.
///
public partial class App : Application
{
private const string MutexID = "Clippy2025Mutex";
private static Mutex? SingleInstanceMutex;
///
/// Gets the current instance in use
///
public new static App Current => (App)Application.Current;
///
/// Gets the instance to resolve application services.
///
public IServiceProvider Services { get; }
///
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
///
public App()
{
Services = ConfigureServices();
this.InitializeComponent();
UnhandledException += OnUnhandledException;
TaskScheduler.UnobservedTaskException += OnUnobservedException;
AppDomain.CurrentDomain.FirstChanceException += CurrentDomain_FirstChanceException;
CheckSingleInstance();
}
private void CheckSingleInstance()
{
bool isNewInstance;
SingleInstanceMutex = new Mutex(true, MutexID, out isNewInstance);
if (!isNewInstance)
System.Environment.Exit(0);
}
private static IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
return services.BuildServiceProvider();
}
///
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used such as when the application is launched to open a specific file.
///
/// Details about the launch request and process.
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
if (AppInstance.GetActivatedEventArgs().Kind != ActivationKind.StartupTask)
{
ShowClippy();
}
TrayWindow = new TrayFlyoutWindow();
}
public void ShowClippy()
{
if (m_window is null)
m_window = new MainWindow();
m_window.Activate();
m_window.Show();
}
public void OpenSettings()
{
if (s_window is null)
s_window = new SettingsWindow();
s_window.Activate();
s_window.Closed += (sender, e) => { s_window = null; };
}
private MainWindow m_window;
private Window s_window;
private TrayFlyoutWindow TrayWindow;
private static void OnUnobservedException(object? sender, UnobservedTaskExceptionEventArgs e) => e.SetObserved();
private static void OnUnhandledException(object? sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e) => e.Handled = true;
private void CurrentDomain_FirstChanceException(object? sender, FirstChanceExceptionEventArgs e)
{
}
}
}
================================================
FILE: Clippy/Clippy.csproj
================================================
WinExenet9.0-windows10.0.22621.010.0.18362.010.0.18362.0Clippyapp.manifestx86;x64;arm64win-x86;win-x64;win-arm64win-$(Platform).pubxmltruetrueFalseTrue3E4148A36277D5E342AEAE4759BDF4C13A5E8EBASHA256TrueD:\Repos\Clippy\Clippy\AppPackagesSideloading\FalseTrueAlways0Truex86|x64|arm64allruntime; build; native; contentfiles; analyzers; buildtransitiveAlwaysMSBuild:CompileMSBuild:CompileMSBuild:CompileMSBuild:CompileMSBuild:CompileMSBuild:CompileMSBuild:CompileAlways
================================================
FILE: Clippy/Controls/APIBox.xaml
================================================
================================================
FILE: Clippy/Controls/APIBox.xaml.cs
================================================
using Clippy.Core.Services;
using Clippy.Services;
using CubeKit.UI.Icons;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.System;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace Clippy.Controls
{
public sealed partial class APIBox : UserControl
{
private KeyService Keys = (KeyService)App.Current.Services.GetService();
public APIBox()
{
this.InitializeComponent();
KeyBox.Password = Keys.GetKey();
}
private void AddApi()
{
if (string.IsNullOrEmpty(KeyBox.Password))
{
Reject();
return;
}
try
{
/* OpenAIService AI = new OpenAIService(new OpenAiOptions()
{
ApiKey = KeyBox.Password
});*/
Keys.SetKey(KeyBox.Password);
}
catch
{
Reject();
}
}
private FluentSymbol PrivacyToIcon(bool? boolean) => (boolean ?? false) ? FluentSymbol.EyeShow20 : FluentSymbol.EyeHide20;
private PasswordRevealMode PrivacyToPassword(bool? boolean) => (boolean ?? false) ? PasswordRevealMode.Visible : PasswordRevealMode.Hidden;
private void ApiBox_KeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key == VirtualKey.Enter)
AddApi();
}
private void Submit_Click(object sender, RoutedEventArgs e) => AddApi();
private void Accept() => KeyBox.Foreground = GreenLinearGradientBrush;
private void Reject()
{
KeyBox.Foreground = RedLinearGradientBrush;
KeyBox.Focus(FocusState.Programmatic);
PasswordLoadAnimation.Start();
}
}
}
================================================
FILE: Clippy/Controls/MessageTriangle.xaml
================================================
================================================
FILE: Clippy/Controls/MessageTriangle.xaml.cs
================================================
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace Clippy.Controls
{
public sealed partial class MessageTriangle : UserControl
{
public MessageTriangle()
{
this.InitializeComponent();
}
}
}
================================================
FILE: Clippy/Controls/Messages/ClippyMessage.xaml
================================================
================================================
FILE: Clippy/Controls/Messages/ClippyMessage.xaml.cs
================================================
using Clippy.Core.Services;
using Clippy.Core.ViewModels.Messages;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.WinUI.UI.Controls;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.System;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace Clippy.Controls.Messages
{
public sealed partial class ClippyMessage : UserControl
{
public ClippyMessageViewModel ViewModel
{
get => (ClippyMessageViewModel)GetValue(ViewModelProperty);
set
{
if (value is not null)
value.UIThread = action => DispatcherQueue.TryEnqueue(() => action());
SetValue(ViewModelProperty, value);
}
}
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register(
nameof(ViewModel),
typeof(ClippyMessageViewModel),
typeof(ClippyMessage),
new PropertyMetadata(null));
public bool IsSendEnabled
{
get => (bool)GetValue(IsSendEnabledProperty);
set => SetValue(IsSendEnabledProperty, value);
}
public static readonly DependencyProperty IsSendEnabledProperty =
DependencyProperty.Register("IsSendEnabled", typeof(bool), typeof(UserMessage), null);
public ClippyMessage()
{
this.InitializeComponent();
}
private void Send_Click(object sender, RoutedEventArgs e)
{
//if (SendBox.Text is not null && !String.IsNullOrEmpty(Message))
// Chat.SendAsync(new Clippy.Core.Classes.UserMessage(SendBox.Text));
}
private void SendBox_KeyDown(object sender, KeyRoutedEventArgs e)
{
// if (e.Key == VirtualKey.Enter && SendBox.Text is not null && !String.IsNullOrEmpty(Message))
// Chat.SendAsync(new Clippy.Core.Classes.UserMessage(SendBox.Text));
}
// Bool to Visibility
public Visibility BoolToVis(bool b) => b ? Visibility.Visible : Visibility.Collapsed;
// Bool to inverted visibility
public Visibility InvertBoolToVis(bool b) => b ? Visibility.Collapsed : Visibility.Visible;
}
}
================================================
FILE: Clippy/Controls/Messages/SystemMessage.xaml
================================================
================================================
FILE: Clippy/Controls/Messages/SystemMessage.xaml.cs
================================================
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace Clippy.Controls.Messages
{
public sealed partial class SystemMessage : UserControl
{
public SystemMessage()
{
this.InitializeComponent();
}
}
}
================================================
FILE: Clippy/Controls/Messages/UserMessage.xaml
================================================
================================================
FILE: Clippy/Controls/Messages/UserMessage.xaml.cs
================================================
using Clippy.Core.ViewModels.Messages;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace Clippy.Controls.Messages
{
public sealed partial class UserMessage : UserControl
{
public UserMessageViewModel ViewModel
{
get => (UserMessageViewModel)GetValue(ViewModelProperty);
set => SetValue(ViewModelProperty, value);
}
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register(
nameof(ViewModel),
typeof(UserMessageViewModel),
typeof(UserMessage),
new PropertyMetadata(null));
public UserMessage()
{
this.InitializeComponent();
}
}
}
================================================
FILE: Clippy/Controls/ShineUITextblock.xaml
================================================
================================================
FILE: Clippy/Controls/ShineUITextblock.xaml.cs
================================================
using CommunityToolkit.WinUI.UI.Controls;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.System;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace Clippy.Controls
{
public sealed partial class ShineUITextblock : UserControl
{
public string Text
{
get => (string)GetValue(TextProperty);
set
{
SetValue(TextProperty, value);
if(value.Length == 0)
{
MarkdownBlock.Visibility = Visibility.Collapsed;
Shimmer.Visibility = Visibility.Visible;
}
else
{
MarkdownBlock.Visibility = Visibility.Visible;
Shimmer.Visibility = Visibility.Collapsed;
}
}
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(ShineUITextblock), null);
public ShineUITextblock()
{
this.InitializeComponent();
}
private async void MarkdownTextBlock_LinkClicked(object sender, LinkClickedEventArgs e) => await Launcher.LaunchUriAsync(new Uri(e.Link));
}
}
================================================
FILE: Clippy/Helpers/ClippyInputHelper.cs
================================================
using CubeKit.UI.Helpers;
using Microsoft.UI.Xaml.Input;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection.Metadata;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Core;
using WinUIEx;
using static CubeKit.UI.Helpers.NativeHelper;
using static WindowsInput.Native.SystemMetrics;
namespace Clippy.Helpers
{
public class ClippyInputHelper
{
public static async void PointerPress(IntPtr WindowToIgnore)
{
return;
await Task.Run(async () =>
{
// NativeHelper.GetCursorPos(out Point point);
// Perform hit testing to determine the target
// IntPtr hWnd = await GetWindowHandleAtPoint(point, WindowToIgnore);
// Forward the pointer event to the target window
// NativeHelper.SendMessage(hWnd, NativeHelper.WM_LBUTTONDOWN, (IntPtr)point.X, (IntPtr)point.Y);
// NativeHelper.SendMessage(hWnd, NativeHelper.WM_LBUTTONUP, (IntPtr)point.X, (IntPtr)point.Y);
});
}
public static async void PointerHover(IntPtr WindowToIgnore)
{
return;
await Task.Run(async () =>
{
NativeHelper.GetCursorPos(out Point point);
// Perform hit testing to determine the target
IntPtr hWnd = await GetWindowHandleAtPoint(point, WindowToIgnore);
// Forward the pointer event to the target window
NativeHelper.SendMessage(hWnd, NativeHelper.WM_MOUSEMOVE, (IntPtr)point.X, (IntPtr)point.Y);
});
}
public static async Task GetWindowHandleAtPoint(Point point, IntPtr WindowToIgnore)
{
IntPtr hWnd = WindowFromPoint(point);
await Task.Run(() =>
{
while (hWnd != IntPtr.Zero && hWnd != WindowToIgnore)
{
RECT rect;
GetWindowRect(hWnd, out rect);
if (rect.Left <= point.X && rect.Top <= point.Y && rect.Right >= point.X && rect.Bottom >= point.Y)
{
// Check if there is a child window at the point
IntPtr childHwnd = ChildWindowFromPointEx(hWnd, point, GW_CHILD);
if (childHwnd != IntPtr.Zero)
hWnd = childHwnd;
else
break;
}
else
{
hWnd = GetWindow(hWnd, GW_HWNDNEXT);
}
}
});
return hWnd;
}
}
}
================================================
FILE: Clippy/Helpers/ClippyKeyboardListener.cs
================================================
using Clippy.Core.Services;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WinUIEx;
namespace Clippy.Helpers
{
public class ClippyKeyboardListener
{
private static WindowEx Clippy;
private static bool IsWin = false;
private static bool IsC = false;
private const uint VK_WINDOWS = 0x5B;
private const uint VK_C = 0x43;
private static KeyboardHelper KeyboardHook;
private static ISettingsService Settings = App.Current.Services.GetService();
public static void Setup(WindowEx clippy)
{
Clippy = clippy;
KeyboardHook = new KeyboardHelper();
KeyboardHook.KeyboardPressed += OnKeyPressed;
}
private static void OnKeyPressed(object sender, KeyboardHelperEventArgs e)
{
if (!Settings.KeyboardEnabled)
return;
if (e.KeyboardState == KeyboardHelper.KeyboardState.KeyDown)
{
if (e.KeyboardData.VirtualCode == VK_WINDOWS)
IsWin = true;
if (e.KeyboardData.VirtualCode == VK_C)
IsC = true;
if (IsWin && IsC)
{
Clippy.Show();
Clippy.SetForegroundWindow();
Clippy.BringToFront();
}
}
else if (e.KeyboardState == KeyboardHelper.KeyboardState.KeyUp)
{
if (e.KeyboardData.VirtualCode == VK_WINDOWS)
IsWin = false;
if (e.KeyboardData.VirtualCode == VK_C)
IsC = false;
}
}
}
}
================================================
FILE: Clippy/Helpers/KeyboardHelper.cs
================================================
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Clippy.Helpers
{
public class KeyboardHelper : IDisposable
{
public event EventHandler KeyboardPressed;
public KeyboardHelper()
{
_windowsHookHandle = IntPtr.Zero;
_user32LibraryHandle = IntPtr.Zero;
_hookProc = LowLevelKeyboardProc; // we must keep alive _hookProc, because GC is not aware about SetWindowsHookEx behaviour.
_user32LibraryHandle = LoadLibrary("User32");
if (_user32LibraryHandle == IntPtr.Zero)
{
int errorCode = Marshal.GetLastWin32Error();
throw new Win32Exception(errorCode, $"Failed to load library 'User32.dll'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
}
_windowsHookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, _hookProc, _user32LibraryHandle, 0);
if (_windowsHookHandle == IntPtr.Zero)
{
int errorCode = Marshal.GetLastWin32Error();
throw new Win32Exception(errorCode, $"Failed to adjust keyboard hooks for '{Process.GetCurrentProcess().ProcessName}'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
}
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// because we can unhook only in the same thread, not in garbage collector thread
if (_windowsHookHandle != IntPtr.Zero)
{
if (!UnhookWindowsHookEx(_windowsHookHandle))
{
int errorCode = Marshal.GetLastWin32Error();
throw new Win32Exception(errorCode, $"Failed to remove keyboard hooks for '{Process.GetCurrentProcess().ProcessName}'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
}
_windowsHookHandle = IntPtr.Zero;
// ReSharper disable once DelegateSubtraction
_hookProc -= LowLevelKeyboardProc;
}
}
if (_user32LibraryHandle != IntPtr.Zero)
{
if (!FreeLibrary(_user32LibraryHandle)) // reduces reference to library by 1.
{
int errorCode = Marshal.GetLastWin32Error();
throw new Win32Exception(errorCode, $"Failed to unload library 'User32.dll'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
}
_user32LibraryHandle = IntPtr.Zero;
}
}
~KeyboardHelper()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private IntPtr _windowsHookHandle;
private IntPtr _user32LibraryHandle;
private HookProc _hookProc;
delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
private static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern bool FreeLibrary(IntPtr hModule);
///
/// The SetWindowsHookEx function installs an application-defined hook procedure into a hook chain.
/// You would install a hook procedure to monitor the system for certain types of events. These events are
/// associated either with a specific thread or with all threads in the same desktop as the calling thread.
///
/// hook type
/// hook procedure
/// handle to application instance
/// thread identifier
/// If the function succeeds, the return value is the handle to the hook procedure.
[DllImport("USER32", SetLastError = true)]
static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, int dwThreadId);
///
/// The UnhookWindowsHookEx function removes a hook procedure installed in a hook chain by the SetWindowsHookEx function.
///
/// handle to hook procedure
/// If the function succeeds, the return value is true.
[DllImport("USER32", SetLastError = true)]
public static extern bool UnhookWindowsHookEx(IntPtr hHook);
///
/// The CallNextHookEx function passes the hook information to the next hook procedure in the current hook chain.
/// A hook procedure can call this function either before or after processing the hook information.
///
/// handle to current hook
/// hook code passed to hook procedure
/// value passed to hook procedure
/// value passed to hook procedure
/// If the function succeeds, the return value is true.
[DllImport("USER32", SetLastError = true)]
static extern IntPtr CallNextHookEx(IntPtr hHook, int code, IntPtr wParam, IntPtr lParam);
[StructLayout(LayoutKind.Sequential)]
public struct LowLevelKeyboardInputEvent
{
///
/// A virtual-key code. The code must be a value in the range 1 to 254.
///
public int VirtualCode;
///
/// A hardware scan code for the key.
///
public int HardwareScanCode;
///
/// The extended-key flag, event-injected Flags, context code, and transition-state flag. This member is specified as follows. An application can use the following values to test the keystroke Flags. Testing LLKHF_INJECTED (bit 4) will tell you whether the event was injected. If it was, then testing LLKHF_LOWER_IL_INJECTED (bit 1) will tell you whether or not the event was injected from a process running at lower integrity level.
///
public int Flags;
///
/// The time stamp stamp for this message, equivalent to what GetMessageTime would return for this message.
///
public int TimeStamp;
///
/// Additional information associated with the message.
///
public IntPtr AdditionalInformation;
}
public const int WH_KEYBOARD_LL = 13;
//const int HC_ACTION = 0;
public enum KeyboardState
{
KeyDown = 0x0100,
KeyUp = 0x0101,
SysKeyDown = 0x0104,
SysKeyUp = 0x0105
}
public const int VkSnapshot = 0x5b;
//const int VkLwin = 0x5b;
//const int VkRwin = 0x5c;
//const int VkTab = 0x09;
//const int VkEscape = 0x18;
//const int VkControl = 0x11;
const int KfAltdown = 0x2000;
public const int LlkhfAltdown = (KfAltdown >> 8);
public IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam)
{
bool fEatKeyStroke = false;
var wparamTyped = wParam.ToInt32();
if (Enum.IsDefined(typeof(KeyboardState), wparamTyped))
{
object o = Marshal.PtrToStructure(lParam, typeof(LowLevelKeyboardInputEvent));
LowLevelKeyboardInputEvent p = (LowLevelKeyboardInputEvent)o;
var eventArguments = new KeyboardHelperEventArgs(p, (KeyboardState)wparamTyped);
EventHandler handler = KeyboardPressed;
handler?.Invoke(this, eventArguments);
fEatKeyStroke = eventArguments.Handled;
}
return fEatKeyStroke ? (IntPtr)1 : CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
}
}
public class KeyboardHelperEventArgs : HandledEventArgs
{
public KeyboardHelper.KeyboardState KeyboardState { get; private set; }
public KeyboardHelper.LowLevelKeyboardInputEvent KeyboardData { get; private set; }
public KeyboardHelperEventArgs(
KeyboardHelper.LowLevelKeyboardInputEvent keyboardData,
KeyboardHelper.KeyboardState keyboardState)
{
KeyboardData = keyboardData;
KeyboardState = keyboardState;
}
}
}
================================================
FILE: Clippy/Helpers/MessageSelector.cs
================================================
using Clippy.Core.Classes;
using Clippy.Core.ViewModels.Messages;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Clippy.Helpers
{
public partial class MessageSelector : DataTemplateSelector
{
public DataTemplate ClippyMessageTemplate { get; set; }
public DataTemplate UserMessageTemplate { get; set; }
public DataTemplate SystemMessageTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item)
{
return item switch
{
ClippyMessageViewModel => ClippyMessageTemplate,
UserMessageViewModel => UserMessageTemplate,
SystemMessageViewModel => SystemMessageTemplate,
_ => base.SelectTemplateCore(item)
};
}
}
}
================================================
FILE: Clippy/MainWindow.xaml
================================================
================================================
FILE: Clippy/MainWindow.xaml.cs
================================================
using CubeKit.UI.Helpers;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using WinUIEx;
using Windows.Win32;
using WinRT.Interop;
using Clippy.Windows;
using WinUIEx.Messaging;
using Windows.Win32.UI.WindowsAndMessaging;
using Microsoft.UI;
using Clippy.Core.ViewModels;
using Microsoft.Extensions.DependencyInjection;
using Clippy.Services;
using Clippy.Helpers;
using Clippy.Core.Services;
using Windows.UI.Input.Preview.Injection;
using Windows.UI.Input;
using Windows.Devices.Input;
using Windows.System;
using Windows.UI.Core;
using System.Diagnostics.Eventing.Reader;
using TerraFX.Interop.Windows;
using static TerraFX.Interop.Windows.WS;
using static TerraFX.Interop.Windows.Windows;
using static TerraFX.Interop.Windows.GWL;
using static TerraFX.Interop.Windows.SWP;
using static TerraFX.Interop.Windows.SW;
using System.Reflection.Metadata;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace Clippy
{
///
/// An empty window that can be used on its own or navigated to within a Frame.
///
public sealed partial class MainWindow : WindowEx
{
private SettingsService Settings = (SettingsService)App.Current.Services.GetService();
private ClippyViewModel Clippy = App.Current.Services.GetService();
WindowMessageMonitor m;
public MainWindow()
{
this.InitializeComponent();
m = new(this);
unsafe
{
var hwnd = (HWND)this.GetWindowHandle();
int lExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, lExStyle | WS_EX_LAYERED);
}
m.WindowMessageReceived += WindowMessageReceived;
SystemBackdrop = new TransparentBackdrop();
Content.Background = new SolidColorBrush(Colors.Red);
Content.Background = new SolidColorBrush(Colors.Transparent);
ClippyKeyboardListener.Setup(this);
Collapse();
this.BringToFront();
if (Clippy.IsPinned) Pin();
else Unpin();
Clippy.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) =>
{
if(e.PropertyName == "IsPinned")
{
if (Clippy.IsPinned) Pin();
else Unpin();
}
};
}
private unsafe void Pin()
{
var presenter = this.AppWindow.Presenter as OverlappedPresenter;
var hwnd = (HWND)this.GetWindowHandle();
if (presenter is not null) presenter.IsAlwaysOnTop = true;
// Add the extended window styles for always on top
int lExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, lExStyle | WS_EX_TOPMOST);
// Move window to top
SetWindowPos(hwnd, HWND.HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
private unsafe void Unpin()
{
var presenter = this.AppWindow.Presenter as OverlappedPresenter;
var hwnd = (HWND)this.GetWindowHandle();
if (presenter is not null) presenter.IsAlwaysOnTop = false;
// Add the extended window styles for always on top
int lExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, lExStyle & ~WS_EX_TOPMOST);
this.BringToFront();
}
private void WindowMessageReceived(object? sender, WindowMessageEventArgs e)
{
if (e.Message.MessageId == PInvoke.WM_ERASEBKGND)
{
e.Handled = true;
e.Result = 1;
}
}
private double GetScale()
{
var progmanWindow = NativeHelper.FindWindow("Shell_TrayWnd", null);
var monitor = NativeHelper.MonitorFromWindow(progmanWindow, NativeHelper.MONITOR_DEFAULTTOPRIMARY);
NativeHelper.DeviceScaleFactor scale;
NativeHelper.GetScaleFactorForMonitor(monitor, out scale);
if (scale == NativeHelper.DeviceScaleFactor.DEVICE_SCALE_FACTOR_INVALID)
scale = NativeHelper.DeviceScaleFactor.SCALE_100_PERCENT;
return Convert.ToDouble(scale) / 100;
}
private void Settings_Click(object sender, RoutedEventArgs e)
{
App.Current.OpenSettings();
}
private Visibility BtoV(bool b) => b ? Visibility.Visible : Visibility.Collapsed;
private void Clippy_Checked(object sender, RoutedEventArgs e) => Expand();
private void Clippy_Unchecked(object sender, RoutedEventArgs e) => Collapse();
private void Collapse()
{
this.Height = 150;
this.Width = 150;
double Scale = GetScale();
double DisplayHeight = (DisplayArea.Primary.OuterBounds.Height) - 100;
double DisplayWidth = (DisplayArea.Primary.OuterBounds.Width) - 200;
double W = this.Width * Scale;
double H = this.Height * Scale;
this.MoveAndResize(DisplayWidth - W, DisplayHeight - H, this.Width, this.Height);
}
private void Expand()
{
this.Height = 1000;
this.Width = 380;
double Scale = GetScale();
double DisplayHeight = (DisplayArea.Primary.OuterBounds.Height) - 100;
double DisplayWidth = (DisplayArea.Primary.OuterBounds.Width) - 200;
double W = this.Width * Scale;
double H = this.Height * Scale;
this.MoveAndResize(DisplayWidth - W, DisplayHeight - H, this.Width, this.Height);
Content.MaxHeight = this.Height;
}
// Bool to Visibility
public Visibility BoolToVis(bool b) => b ? Visibility.Visible : Visibility.Collapsed;
// Bool to inverted visibility
public Visibility InvertBoolToVis(bool b) => b ? Visibility.Collapsed : Visibility.Visible;
private void Background_PointerPressed(object sender, PointerRoutedEventArgs e) => ClippyInputHelper.PointerPress(this.GetWindowHandle());
private void Background_PointerMoved(object sender, PointerRoutedEventArgs e) => ClippyInputHelper.PointerHover(this.GetWindowHandle());
private void TextBox_PreviewKeyDown(object sender, KeyRoutedEventArgs e)
{
// NOTE - AcceptsReturn is set to true in XAML.
/*if (e.Key == VirtualKey.Enter)
{
// If SHIFT is pressed, this next IF is skipped over, so the default behavior of "AcceptsReturn" is used.
var keyState = CoreWindow.GetForCurrentThread().GetKeyState(VirtualKey.Shift);
if ((keyState & CoreVirtualKeyStates.Down) != CoreVirtualKeyStates.Down)
e.Handled = true; // Mark the event as handled
} */
}
private void TextBox_KeyUp(object sender, KeyRoutedEventArgs e)
{
/*if (e.Key == VirtualKey.Enter)
{
// If SHIFT is pressed, this next IF is skipped over, so the default behavior of "AcceptsReturn" is used.
var keyState = CoreWindow.GetForCurrentThread().GetKeyState(VirtualKey.Shift);
if ((keyState & CoreVirtualKeyStates.Down) != CoreVirtualKeyStates.Down)
{
// Force update x:Bind text
Clippy.CurrentText = (sender as TextBox).Text;
if (Clippy.SendPromptCommand.CanExecute(this))
Clippy.SendPromptCommand.Execute(this);
}
} */
}
private void Exit_Click(object sender, RoutedEventArgs e) => Application.Current.Exit();
private void Hide_Click(object sender, RoutedEventArgs e)
{
this.Hide();
}
}
}
================================================
FILE: Clippy/NativeMethods.txt
================================================
WINDOW_EX_STYLE
GetWindowLong
WM_ERASEBKGND
SetWindowLong
================================================
FILE: Clippy/Package.appxmanifest
================================================
Paperclip by FireCubeFireCubeStudiosAssets\StoreLogo.png
================================================
FILE: Clippy/Properties/launchSettings.json
================================================
{
"profiles": {
"Clippy (Package)": {
"commandName": "MsixPackage",
"nativeDebugging": true
},
"Clippy (Unpackaged)": {
"commandName": "Project",
"nativeDebugging": true
}
}
}
================================================
FILE: Clippy/Services/KeyService.cs
================================================
using Clippy.Core.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Windows.Security.Credentials;
namespace Clippy.Services
{
public class KeyService : IKeyService
{
private const string Name = "Key";
private const string Resource = "R";
private PasswordVault Vault = new PasswordVault();
public string GetKey()
{
try
{
return Vault.Retrieve(Resource, Name).Password;
}
catch
{
return "";
}
}
public void SetKey(string key) => Vault.Add(new PasswordCredential(Resource, Name, key));
}
}
================================================
FILE: Clippy/Services/SettingsService.cs
================================================
using Clippy.Core.Services;
using Clippy.Helpers;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Storage;
namespace Clippy.Services
{
public class SettingsService : ObservableObject, ISettingsService
{
private static ApplicationDataContainer Settings = ApplicationData.Current.LocalSettings;
private bool autoPin = (bool)(Settings.Values["AutoPin"] ?? true);
public bool AutoPin
{
get => autoPin;
set
{
Settings.Values["AutoPin"] = value;
SetProperty(ref autoPin, value);
}
}
private bool trayClippy = (bool)(Settings.Values["TrayClippy"] ?? true);
public bool TrayClippy
{
get => trayClippy;
set
{
Settings.Values["TrayClippy"] = value;
SetProperty(ref trayClippy, value);
//if (value)
// ClippyTrayListener.Recreate();
// else
// ClippyTrayListener.Remove();
}
}
private bool translucentBackground = (bool)(Settings.Values["TranslucentBackground"] ?? true);
public bool TranslucentBackground
{
get => translucentBackground;
set
{
Settings.Values["TranslucentBackground"] = value;
SetProperty(ref translucentBackground, value);
}
}
private int tokens = (int)(Settings.Values["Tokens"] ?? 100);
public int Tokens
{
get => tokens;
set
{
if (value > 50 && value < 2000)
{
Settings.Values["Tokens"] = value;
SetProperty(ref tokens, value);
}
else
SetProperty(ref tokens, 100);
}
}
private bool keyboardEnabled = (bool)(Settings.Values["KeyboardEnabled"] ?? true);
public bool KeyboardEnabled
{
get => keyboardEnabled;
set
{
Settings.Values["KeyboardEnabled"] = value;
SetProperty(ref keyboardEnabled, value);
}
}
}
}
================================================
FILE: Clippy/SettingsWindow.xaml
================================================
Clippy by FireCube - 1.0Clippy OptionsPersonalisation OptionsInformationAbout this appClippy by FireCube - Preview 4Developed by FireCubeStudiosJoin Clippy DiscordStar the GitHub repositoryRate the appFollow me on TwitterFollow me on Bluesky
================================================
FILE: Clippy/SettingsWindow.xaml.cs
================================================
using Clippy.Core.Services;
using Clippy.Services;
using Clippy.Windows;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Composition.SystemBackdrops;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.ApplicationModel.Core;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.System;
using WinUIEx;
using WinRT;
using Windows.ApplicationModel;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace Clippy
{
///
/// An empty window that can be used on its own or navigated to within a Frame.
///
public sealed partial class SettingsWindow : WindowEx
{
private SettingsService Settings = (SettingsService)App.Current.Services.GetService();
public SettingsWindow()
{
this.InitializeComponent();
this.ExtendsContentIntoTitleBar = true;
SetTitleBar(AppTitleBar);
SetupStartup();
}
private async void SetupStartup()
{
var startup = await StartupTask.GetAsync("ClippyStartupTaskId");
UpdateToggleState(startup.State);
}
private async void Star_Click(object sender, RoutedEventArgs e) => await Launcher.LaunchUriAsync(new Uri("ms-windows-store://review/?ProductId=9NWK37S35V5T"));
private async void Hub_Click(object sender, RoutedEventArgs e) => await Launcher.LaunchUriAsync(new Uri("https://discord.gg/3WYcKat"));
private async void GitHub_Click(object sender, RoutedEventArgs e) => await Launcher.LaunchUriAsync(new Uri("https://github.com/FireCubeStudios/Clippy"));
private void Exit_Click(object sender, RoutedEventArgs e) => Application.Current.Exit();
private void UpdateToggleState(StartupTaskState state)
{
StartupToggle.IsEnabled = true;
StartupErrorText.Visibility = Visibility.Collapsed;
switch (state)
{
case StartupTaskState.Enabled:
StartupToggle.IsOn = true;
break;
case StartupTaskState.Disabled:
break;
case StartupTaskState.DisabledByUser:
StartupToggle.IsOn = false;
StartupErrorText.Visibility = Visibility.Visible;
StartupErrorText.Text = "Unable to change state of startup task via the application - enable via Startup page in Windows Settings";
break;
default:
StartupToggle.IsEnabled = false;
break;
}
}
private async void StartupToggle_Toggled(object sender, RoutedEventArgs e)
{
bool enable = StartupToggle.IsOn;
var startup = await StartupTask.GetAsync("ClippyStartupTaskId");
StartupErrorText.Visibility = Visibility.Collapsed;
switch (startup.State)
{
case StartupTaskState.Enabled when !enable:
startup.Disable();
break;
case StartupTaskState.Disabled when enable:
var updatedState = await startup.RequestEnableAsync();
UpdateToggleState(updatedState);
break;
case StartupTaskState.DisabledByUser when enable:
StartupToggle.IsOn = false;
StartupErrorText.Visibility = Visibility.Visible;
StartupErrorText.Text = "Unable to change state of startup task via the application - enable via Startup page in Windows Settings";
break;
default:
break;
}
}
}
}
================================================
FILE: Clippy/Windows/MicaWindow.cs
================================================
using Microsoft.UI.Composition.SystemBackdrops;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using WinRT;
using WinUIEx;
namespace Clippy.Windows
{
public class MicaWindow
{
MicaController? m_micaController;
SystemBackdropConfiguration? m_configurationSource;
public bool TrySetMicaBackdrop(WindowEx W)
{
try
{
if (MicaController.IsSupported())
{
WindowsSystemDispatcherQueueHelper.EnsureWindowsSystemDispatcherQueueController();
// Hooking up the policy object
m_configurationSource = new SystemBackdropConfiguration();
// Initial configuration state.
m_configurationSource.IsInputActive = true;
m_micaController = new MicaController();
// Enable the system backdrop.
// Note: Be sure to have "using WinRT;" to support the Window.As<...>() call.
m_micaController.AddSystemBackdropTarget(W.As());
m_micaController.SetSystemBackdropConfiguration(m_configurationSource);
return true; // succeeded
}
return false; // Mica is not supported on this system
}
catch
{
return false;
}
}
}
}
================================================
FILE: Clippy/Windows/TransparentBackdrop.cs
================================================
using System;
using Microsoft.UI.Xaml.Media;
using Compositor = Windows.UI.Composition.Compositor;
using Windows.UI.Composition;
using ICompositionSupportsSystemBackdrop = Microsoft.UI.Composition.ICompositionSupportsSystemBackdrop;
using Windows.UI;
namespace Clippy.Windows
{
internal class TransparentBackdrop : SystemBackdrop
{
static readonly Lazy _Compositor = new(() =>
{
WindowsSystemDispatcherQueueHelper.EnsureWindowsSystemDispatcherQueueController();
return new();
});
static Compositor Compositor => _Compositor.Value;
protected override void OnTargetConnected(ICompositionSupportsSystemBackdrop connectedTarget, Microsoft.UI.Xaml.XamlRoot xamlRoot)
{
connectedTarget.SystemBackdrop = Compositor.CreateColorBrush(
Color.FromArgb(0, 255, 255, 255)
);
}
protected override void OnTargetDisconnected(ICompositionSupportsSystemBackdrop disconnectedTarget)
{
disconnectedTarget.SystemBackdrop = null;
}
}
}
================================================
FILE: Clippy/Windows/WindowsSystemDispatcherQueueHelper.cs
================================================
using System.Runtime.InteropServices;
using Windows.System;
namespace Clippy.Windows
{
public static class WindowsSystemDispatcherQueueHelper
{
[StructLayout(LayoutKind.Sequential)]
struct DispatcherQueueOptions
{
internal int dwSize;
internal int threadType;
internal int apartmentType;
}
[DllImport("CoreMessaging.dll")]
private static extern int CreateDispatcherQueueController([In] DispatcherQueueOptions options, [In, Out, MarshalAs(UnmanagedType.IUnknown)] ref object? dispatcherQueueController);
static object? m_dispatcherQueueController = null;
public static void EnsureWindowsSystemDispatcherQueueController()
{
if (DispatcherQueue.GetForCurrentThread() != null)
// one already exists, so we'll just use it.
return;
if (m_dispatcherQueueController == null)
{
DispatcherQueueOptions options;
options.dwSize = Marshal.SizeOf(typeof(DispatcherQueueOptions));
options.threadType = 2; // DQTYPE_THREAD_CURRENT
options.apartmentType = 2; // DQTAT_COM_STA
_ = CreateDispatcherQueueController(options, ref m_dispatcherQueueController);
}
}
}
}
================================================
FILE: Clippy/app.manifest
================================================
true/PMPerMonitorV2, PerMonitor
================================================
FILE: Clippy.Core/Classes/Message.cs
================================================
using Clippy.Core.Enums;
using Clippy.Core.Interfaces;
using System;
using System.Collections.Generic;
using System.Text;
namespace Clippy.Core.Classes
{
public record Message(Role Role, string MessageText, DateTime MessageDate) : IMessage
{
public Message(Role Role, string MessageText) : this(Role, MessageText, DateTime.Now) { }
}
}
================================================
FILE: Clippy.Core/Clippy.Core.csproj
================================================
netstandard2.1enable11.0
================================================
FILE: Clippy.Core/Constants.cs
================================================
using System;
using System.Collections.Generic;
using System.Text;
namespace Clippy.Core
{
public class Constants
{
public const string FIRST_CLIPPY_MESSAGE = "Hi! I'm Clippy, your Windows assistant. Would you like to get some assistance?";
public const string DEFAULT_SYSTEM_PROMPT = "You are in an app that revives Microsoft Clippy in Windows. Speak in a Clippy style";// and try to stay as concise/short as possible and not output long messages.";
}
}
================================================
FILE: Clippy.Core/Enums/Role.cs
================================================
using System;
using System.Collections.Generic;
using System.Text;
namespace Clippy.Core.Enums
{
public enum Role
{
User = 0,
Assistant = 1,
System = 2,
}
}
================================================
FILE: Clippy.Core/Factories/MessageFactory.cs
================================================
using Clippy.Core.Interfaces;
using Clippy.Core.ViewModels.Messages;
using Clippy.Core.ViewModels;
using System;
using System.Collections.Generic;
using System.Text;
namespace Clippy.Core.Factories
{
public class MessageFactory
{
public static MessageViewModel GetMessageViewModel(IMessage message)
{
switch (message.Role)
{
case Enums.Role.User:
return new UserMessageViewModel(message);
case Enums.Role.Assistant:
return new ClippyMessageViewModel(message);
default:
return new SystemMessageViewModel(message);
}
}
}
}
================================================
FILE: Clippy.Core/Interfaces/IMessage.cs
================================================
using Clippy.Core.Enums;
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
namespace Clippy.Core.Interfaces
{
public interface IMessage
{
public Role Role { get; init; }
public DateTime MessageDate { get; init; }
public string MessageText { get; init; }
}
}
================================================
FILE: Clippy.Core/Runtime/IsExternalInit.cs
================================================
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
namespace System.Runtime.CompilerServices
{
///
/// Reserved to be used by the compiler for tracking metadata.
/// This class should not be used by developers in source code.
/// This dummy class is required to compile records when targeting .NET Standard 2.1
///
[EditorBrowsable(EditorBrowsableState.Never)]
public static class IsExternalInit
{
}
}
================================================
FILE: Clippy.Core/Services/IChatService.cs
================================================
using Clippy.Core.Classes;
using Clippy.Core.Interfaces;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Clippy.Core.Services
{
public interface IChatService
{
Task SendChatAsync(IEnumerable Messages);
IAsyncEnumerable StreamChatAsync(IEnumerable Messages, CancellationToken cancellationToken = default);
}
}
================================================
FILE: Clippy.Core/Services/IKeyService.cs
================================================
using System;
using System.Collections.Generic;
using System.Text;
namespace Clippy.Core.Services
{
public interface IKeyService
{
public string GetKey();
public void SetKey(string key);
}
}
================================================
FILE: Clippy.Core/Services/ISettingsService.cs
================================================
using System;
using System.Collections.Generic;
using System.Text;
namespace Clippy.Core.Services
{
public interface ISettingsService
{
public bool AutoPin { get; set; }
public bool TrayClippy { get; set; }
public bool TranslucentBackground { get; set; }
public bool KeyboardEnabled { get; set; }
public int Tokens { get; set; }
}
}
================================================
FILE: Clippy.Core/ViewModels/ClippyViewModel.cs
================================================
using Clippy.Core.Classes;
using Clippy.Core.Services;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using Clippy.Core.Interfaces;
using Clippy.Core.Enums;
using Clippy.Core.ViewModels.Messages;
using System.Collections.ObjectModel;
using Clippy.Core.Factories;
using System.Linq.Expressions;
using System.Diagnostics;
namespace Clippy.Core.ViewModels
{
public partial class ClippyViewModel : ObservableObject
{
public ObservableCollection MessagesVM = new();
public ObservableCollection Messages = new();
[ObservableProperty]
private bool isClippyEnabled = true;
[ObservableProperty]
private bool isPinned = true;
[ObservableProperty]
private string currentText = "";
[ObservableProperty]
private DateTime updatedAt = DateTime.Now;
public IChatService ChatService;
public ISettingsService SettingsService;
public ClippyViewModel(IChatService chatService, ISettingsService settingsService)
{
ChatService = chatService;
SettingsService = settingsService;
isPinned = SettingsService.AutoPin;
SetupChat();
}
private void SetupChat()
{
AddMessage(new Message(Role.System, Constants.DEFAULT_SYSTEM_PROMPT));
AddMessage(new Message(Role.Assistant, Constants.FIRST_CLIPPY_MESSAGE));
}
private MessageViewModel AddMessage(IMessage message)
{
var ViewModel = MessageFactory.GetMessageViewModel(message);
MessagesVM.Add(ViewModel);
Messages.Add(message);
return ViewModel;
}
[RelayCommand(IncludeCancelCommand = true)]
public async Task SendPrompt(CancellationToken cancellationToken)
{
try
{
if (!String.IsNullOrEmpty(CurrentText))
{
AddMessage(new Message(Role.User, CurrentText)); // update UI here
CurrentText = "";
await Task.Delay(300);
var messageVM = AddMessage(new Message(Role.Assistant, "")) as ClippyMessageViewModel;
UpdatedAt = DateTime.Now;
messageVM?.StartStreamText(cancellationToken);
try
{
await foreach (var chunk in ChatService.StreamChatAsync(Messages, cancellationToken))
messageVM?.AddStreamText(chunk);
}
catch (TaskCanceledException)
{
return; // Task cancelled so it is ok
}
catch (Exception e)
{
messageVM?.Exception(e);
}
messageVM?.EndStreamText();
}
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
}
}
[RelayCommand]
private void RefreshChat()
{
MessagesVM.Clear();
Messages.Clear();
SetupChat();
}
}
}
================================================
FILE: Clippy.Core/ViewModels/Messages/ClippyMessageViewModel.Streaming.cs
================================================
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
namespace Clippy.Core.ViewModels.Messages
{
public partial class ClippyMessageViewModel
{
[ObservableProperty]
private string streamingText = "";
[ObservableProperty]
private bool isStreaming = false;
private StringBuilder _streamBuffer = new(); // Holds text waiting to be typed
private Task? _typingTask;
private string TotalText = "";
///
/// Starts the typing animation loop.
///
public void StartStreamText(CancellationToken cancellationToken, int batchSize = 3, int delayMs = 15)
{
IsStreaming = true;
_typingTask = Task.Run(async () =>
{
try
{
while (!cancellationToken.IsCancellationRequested)
{
if (_streamBuffer.Length >= batchSize)
{
string chunk = _streamBuffer.ToString(0, batchSize);
_streamBuffer.Remove(0, batchSize);
UIThread?.Invoke(() => StreamingText += chunk);
await Task.Delay(delayMs, cancellationToken);
}
else
{
await Task.Delay(10, cancellationToken); // Wait for more input
}
}
}
catch (TaskCanceledException) { }
});
}
///
/// Adds new text to the animation buffer.
///
public void AddStreamText(string text)
{
TotalText += text;
_streamBuffer.Append(text);
}
///
/// Ends the typing stream and flushes remaining text instantly.
///
public void EndStreamText()
{
if (_streamBuffer.Length > 0)
{
StreamingText += _streamBuffer.ToString();
_streamBuffer.Clear();
}
MessageText = TotalText;
TotalText = "";
IsStreaming = false;
}
}
}
================================================
FILE: Clippy.Core/ViewModels/Messages/ClippyMessageViewModel.cs
================================================
using Clippy.Core.Interfaces;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace Clippy.Core.ViewModels.Messages
{
public partial class ClippyMessageViewModel : MessageViewModel
{
// Run code on the UI Thread in the ViewModel
// This is useful to update ObservableProperties from background threads
public Action? UIThread;
public ClippyMessageViewModel(IMessage message) : base(message)
{
}
public void Exception(Exception exception)
{
// Show error UI
}
}
}
================================================
FILE: Clippy.Core/ViewModels/Messages/MessageViewModel.cs
================================================
using CommunityToolkit.Mvvm.ComponentModel;
using Clippy.Core.Interfaces;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Clippy.Core.ViewModels
{
public abstract partial class MessageViewModel : ObservableObject
{
[ObservableProperty]
private string messageText = "";
protected IMessage Message;
public MessageViewModel(IMessage message)
{
this.Message = message;
MessageText = this.Message.MessageText;
}
}
}
================================================
FILE: Clippy.Core/ViewModels/Messages/SystemMessageViewModel.cs
================================================
using Clippy.Core.Interfaces;
using System;
using System.Collections.Generic;
using System.Text;
namespace Clippy.Core.ViewModels.Messages
{
public class SystemMessageViewModel : MessageViewModel
{
public SystemMessageViewModel(IMessage message) : base(message)
{
}
}
}
================================================
FILE: Clippy.Core/ViewModels/Messages/UserMessageViewModel.cs
================================================
using Clippy.Core.Interfaces;
using System;
using System.Collections.Generic;
using System.Text;
namespace Clippy.Core.ViewModels.Messages
{
public class UserMessageViewModel : MessageViewModel
{
public UserMessageViewModel(IMessage message) : base(message)
{
}
}
}
================================================
FILE: Clippy.sln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32616.157
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Clippy", "Clippy\Clippy.csproj", "{7F32CA32-9073-4B3B-974F-90967083B604}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CubeKit.UI", "CubeKit.UI\CubeKit.UI.csproj", "{AF04C3A3-C6EA-4079-9C32-52E3B64E62FE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Clippy.Core", "Clippy.Core\Clippy.Core.csproj", "{9BEE1DE6-3450-4BA9-AE52-A97778418B99}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|arm64 = Debug|arm64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|arm64 = Release|arm64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7F32CA32-9073-4B3B-974F-90967083B604}.Debug|Any CPU.ActiveCfg = Debug|x64
{7F32CA32-9073-4B3B-974F-90967083B604}.Debug|Any CPU.Build.0 = Debug|x64
{7F32CA32-9073-4B3B-974F-90967083B604}.Debug|Any CPU.Deploy.0 = Debug|x64
{7F32CA32-9073-4B3B-974F-90967083B604}.Debug|arm64.ActiveCfg = Debug|arm64
{7F32CA32-9073-4B3B-974F-90967083B604}.Debug|arm64.Build.0 = Debug|arm64
{7F32CA32-9073-4B3B-974F-90967083B604}.Debug|arm64.Deploy.0 = Debug|arm64
{7F32CA32-9073-4B3B-974F-90967083B604}.Debug|x64.ActiveCfg = Debug|x64
{7F32CA32-9073-4B3B-974F-90967083B604}.Debug|x64.Build.0 = Debug|x64
{7F32CA32-9073-4B3B-974F-90967083B604}.Debug|x64.Deploy.0 = Debug|x64
{7F32CA32-9073-4B3B-974F-90967083B604}.Debug|x86.ActiveCfg = Debug|x86
{7F32CA32-9073-4B3B-974F-90967083B604}.Debug|x86.Build.0 = Debug|x86
{7F32CA32-9073-4B3B-974F-90967083B604}.Debug|x86.Deploy.0 = Debug|x86
{7F32CA32-9073-4B3B-974F-90967083B604}.Release|Any CPU.ActiveCfg = Release|x64
{7F32CA32-9073-4B3B-974F-90967083B604}.Release|Any CPU.Build.0 = Release|x64
{7F32CA32-9073-4B3B-974F-90967083B604}.Release|Any CPU.Deploy.0 = Release|x64
{7F32CA32-9073-4B3B-974F-90967083B604}.Release|arm64.ActiveCfg = Release|arm64
{7F32CA32-9073-4B3B-974F-90967083B604}.Release|arm64.Build.0 = Release|arm64
{7F32CA32-9073-4B3B-974F-90967083B604}.Release|arm64.Deploy.0 = Release|arm64
{7F32CA32-9073-4B3B-974F-90967083B604}.Release|x64.ActiveCfg = Release|x64
{7F32CA32-9073-4B3B-974F-90967083B604}.Release|x64.Build.0 = Release|x64
{7F32CA32-9073-4B3B-974F-90967083B604}.Release|x64.Deploy.0 = Release|x64
{7F32CA32-9073-4B3B-974F-90967083B604}.Release|x86.ActiveCfg = Release|x86
{7F32CA32-9073-4B3B-974F-90967083B604}.Release|x86.Build.0 = Release|x86
{7F32CA32-9073-4B3B-974F-90967083B604}.Release|x86.Deploy.0 = Release|x86
{AF04C3A3-C6EA-4079-9C32-52E3B64E62FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AF04C3A3-C6EA-4079-9C32-52E3B64E62FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AF04C3A3-C6EA-4079-9C32-52E3B64E62FE}.Debug|arm64.ActiveCfg = Debug|Any CPU
{AF04C3A3-C6EA-4079-9C32-52E3B64E62FE}.Debug|arm64.Build.0 = Debug|Any CPU
{AF04C3A3-C6EA-4079-9C32-52E3B64E62FE}.Debug|x64.ActiveCfg = Debug|Any CPU
{AF04C3A3-C6EA-4079-9C32-52E3B64E62FE}.Debug|x64.Build.0 = Debug|Any CPU
{AF04C3A3-C6EA-4079-9C32-52E3B64E62FE}.Debug|x86.ActiveCfg = Debug|Any CPU
{AF04C3A3-C6EA-4079-9C32-52E3B64E62FE}.Debug|x86.Build.0 = Debug|Any CPU
{AF04C3A3-C6EA-4079-9C32-52E3B64E62FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AF04C3A3-C6EA-4079-9C32-52E3B64E62FE}.Release|Any CPU.Build.0 = Release|Any CPU
{AF04C3A3-C6EA-4079-9C32-52E3B64E62FE}.Release|arm64.ActiveCfg = Release|Any CPU
{AF04C3A3-C6EA-4079-9C32-52E3B64E62FE}.Release|arm64.Build.0 = Release|Any CPU
{AF04C3A3-C6EA-4079-9C32-52E3B64E62FE}.Release|x64.ActiveCfg = Release|Any CPU
{AF04C3A3-C6EA-4079-9C32-52E3B64E62FE}.Release|x64.Build.0 = Release|Any CPU
{AF04C3A3-C6EA-4079-9C32-52E3B64E62FE}.Release|x86.ActiveCfg = Release|Any CPU
{AF04C3A3-C6EA-4079-9C32-52E3B64E62FE}.Release|x86.Build.0 = Release|Any CPU
{9BEE1DE6-3450-4BA9-AE52-A97778418B99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9BEE1DE6-3450-4BA9-AE52-A97778418B99}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9BEE1DE6-3450-4BA9-AE52-A97778418B99}.Debug|arm64.ActiveCfg = Debug|Any CPU
{9BEE1DE6-3450-4BA9-AE52-A97778418B99}.Debug|arm64.Build.0 = Debug|Any CPU
{9BEE1DE6-3450-4BA9-AE52-A97778418B99}.Debug|x64.ActiveCfg = Debug|Any CPU
{9BEE1DE6-3450-4BA9-AE52-A97778418B99}.Debug|x64.Build.0 = Debug|Any CPU
{9BEE1DE6-3450-4BA9-AE52-A97778418B99}.Debug|x86.ActiveCfg = Debug|Any CPU
{9BEE1DE6-3450-4BA9-AE52-A97778418B99}.Debug|x86.Build.0 = Debug|Any CPU
{9BEE1DE6-3450-4BA9-AE52-A97778418B99}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9BEE1DE6-3450-4BA9-AE52-A97778418B99}.Release|Any CPU.Build.0 = Release|Any CPU
{9BEE1DE6-3450-4BA9-AE52-A97778418B99}.Release|arm64.ActiveCfg = Release|Any CPU
{9BEE1DE6-3450-4BA9-AE52-A97778418B99}.Release|arm64.Build.0 = Release|Any CPU
{9BEE1DE6-3450-4BA9-AE52-A97778418B99}.Release|x64.ActiveCfg = Release|Any CPU
{9BEE1DE6-3450-4BA9-AE52-A97778418B99}.Release|x64.Build.0 = Release|Any CPU
{9BEE1DE6-3450-4BA9-AE52-A97778418B99}.Release|x86.ActiveCfg = Release|Any CPU
{9BEE1DE6-3450-4BA9-AE52-A97778418B99}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {64213F1C-A0BB-481D-919F-D5209DF72A69}
EndGlobalSection
EndGlobal
================================================
FILE: CubeKit.UI/Controls/Settings/Converters.cs
================================================
using System;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Data;
#nullable enable
namespace CubeKit.UI.Controls.Settings
{
///
/// The generic base implementation of a value converter.
///
/// The source type.
/// The target type.
public abstract class ValueConverter
: IValueConverter
{
///
/// Converts a source value to the target type.
///
///
///
public TTarget? Convert(TSource? value)
{
return Convert(value, null, null);
}
///
/// Converts a target value back to the source type.
///
///
///
public TSource? ConvertBack(TTarget? value)
{
return ConvertBack(value, null, null);
}
///
/// Modifies the source data before passing it to the target for display in the UI.
///
///
///
///
///
///
public object? Convert(object? value, Type? targetType, object? parameter, string? language)
{
// CastExceptions will occur when invalid value, or target type provided.
return Convert((TSource?)value, parameter, language);
}
///
/// Modifies the target data before passing it to the source object. This method is called only in TwoWay bindings.
///
///
///
///
///
///
public object? ConvertBack(object? value, Type? targetType, object? parameter, string? language)
{
// CastExceptions will occur when invalid value, or target type provided.
return ConvertBack((TTarget?)value, parameter, language);
}
///
/// Converts a source value to the target type.
///
///
///
///
///
protected virtual TTarget? Convert(TSource? value, object? parameter, string? language)
{
throw new NotSupportedException();
}
///
/// Converts a target value back to the source type.
///
///
///
///
///
protected virtual TSource? ConvertBack(TTarget? value, object? parameter, string? language)
{
throw new NotSupportedException();
}
}
///
/// The base class for converting instances of type T to object and vice versa.
///
public abstract class ToObjectConverter
: ValueConverter
{
///
/// Converts a source value to the target type.
///
///
///
///
///
protected override object? Convert(T? value, object? parameter, string? language)
{
return value;
}
///
/// Converts a target value back to the source type.
///
///
///
///
///
protected override T? ConvertBack(object? value, object? parameter, string? language)
{
return (T?)value;
}
}
///
/// Converts a boolean to and from a visibility value.
///
public class InverseBooleanConverter
: ValueConverter
{
///
/// Converts a source value to the target type.
///
///
///
///
///
protected override bool Convert(bool value, object? parameter, string? language)
{
return !value;
}
///
/// Converts a target value back to the source type.
///
///
///
///
///
protected override bool ConvertBack(bool value, object? parameter, string? language)
{
return !value;
}
}
public class NullToTrueConverter
: ValueConverter