Compare commits
21 Commits
6ae34369b4
...
8aca73cd9c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8aca73cd9c | ||
|
|
e8bfcacacb | ||
|
|
86b67f36e4 | ||
|
|
93041a370e | ||
|
|
2b6ea24d5e | ||
|
|
02e6b033c2 | ||
|
|
076816bd12 | ||
|
|
1ad2e8ad4c | ||
|
|
3b5d304845 | ||
|
|
cade0046bf | ||
|
|
881a723f4e | ||
|
|
673a71de43 | ||
|
|
6d16af7143 | ||
|
|
04a8a15f23 | ||
|
|
7744bcf67d | ||
|
|
3512bf0fac | ||
|
|
ef740ca9dd | ||
|
|
15cdc77eea | ||
|
|
2ad8b7fffb | ||
|
|
327608cc36 | ||
|
|
6c4235a52f |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -40,3 +40,6 @@ testem.log
|
|||||||
# System files
|
# System files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
|
TextTestExe/
|
||||||
|
data/
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -1,7 +1,20 @@
|
|||||||
# Tauri + Angular
|
# Tauri + Angular
|
||||||
|
|
||||||
This template should help get you started developing with Tauri and Angular.
|
On Windows, Tauri requires the the msvc toolchain:
|
||||||
|
```
|
||||||
|
rustup default stable-msvc
|
||||||
|
```
|
||||||
|
|
||||||
## Recommended IDE Setup
|
## Recommended IDE Setup
|
||||||
|
|
||||||
[VS Code](https://code.visualstudio.com/) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) + [Angular Language Service](https://marketplace.visualstudio.com/items?itemName=Angular.ng-template).
|
[VS Code](https://code.visualstudio.com/) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) + [Angular Language Service](https://marketplace.visualstudio.com/items?itemName=Angular.ng-template).
|
||||||
|
|
||||||
|
## Keyboard Emulation
|
||||||
|
Keyboard emulation on most systems requires workarounds for two issues:
|
||||||
|
- keeping the window with the keyboard on top while another (the input target) is in focus
|
||||||
|
- not receiving focus when clicked
|
||||||
|
- sending input
|
||||||
|
|
||||||
|
## Logging and Statistics
|
||||||
|
For logging, either use [TextTestExe](https://depts.washington.edu/acelab/proj/texttest/) (local program) or [TextTestPP](https://drustz.com/TextTestPP/) (online).
|
||||||
|
The theoretical framework is inspired by https://www.yorku.ca/mack/bit95.html.
|
||||||
|
|||||||
1
doc/.Rprofile
Normal file
1
doc/.Rprofile
Normal file
@@ -0,0 +1 @@
|
|||||||
|
source("renv/activate.R")
|
||||||
374
doc/.gitignore
vendored
Normal file
374
doc/.gitignore
vendored
Normal file
@@ -0,0 +1,374 @@
|
|||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/tex,r
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=tex,r
|
||||||
|
|
||||||
|
### R ###
|
||||||
|
# History files
|
||||||
|
.Rhistory
|
||||||
|
.Rapp.history
|
||||||
|
|
||||||
|
# Session Data files
|
||||||
|
.RData
|
||||||
|
.RDataTmp
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
.Ruserdata
|
||||||
|
|
||||||
|
# Example code in package build process
|
||||||
|
*-Ex.R
|
||||||
|
|
||||||
|
# Output files from R CMD build
|
||||||
|
/*.tar.gz
|
||||||
|
|
||||||
|
# Output files from R CMD check
|
||||||
|
/*.Rcheck/
|
||||||
|
|
||||||
|
# RStudio files
|
||||||
|
.Rproj.user/
|
||||||
|
|
||||||
|
# produced vignettes
|
||||||
|
vignettes/*.html
|
||||||
|
vignettes/*.pdf
|
||||||
|
|
||||||
|
# OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3
|
||||||
|
.httr-oauth
|
||||||
|
|
||||||
|
# knitr and R markdown default cache directories
|
||||||
|
*_cache/
|
||||||
|
/cache/
|
||||||
|
|
||||||
|
# Temporary files created by R markdown
|
||||||
|
*.utf8.md
|
||||||
|
*.knit.md
|
||||||
|
|
||||||
|
# R Environment Variables
|
||||||
|
.Renviron
|
||||||
|
|
||||||
|
# pkgdown site
|
||||||
|
docs/
|
||||||
|
|
||||||
|
# translation temp files
|
||||||
|
po/*~
|
||||||
|
|
||||||
|
# RStudio Connect folder
|
||||||
|
rsconnect/
|
||||||
|
|
||||||
|
### R.Bookdown Stack ###
|
||||||
|
# R package: bookdown caching files
|
||||||
|
/*_files/
|
||||||
|
|
||||||
|
### TeX ###
|
||||||
|
## Core latex/pdflatex auxiliary files:
|
||||||
|
*.aux
|
||||||
|
*.lof
|
||||||
|
*.log
|
||||||
|
*.lot
|
||||||
|
*.fls
|
||||||
|
*.out
|
||||||
|
*.toc
|
||||||
|
*.fmt
|
||||||
|
*.fot
|
||||||
|
*.cb
|
||||||
|
*.cb2
|
||||||
|
.*.lb
|
||||||
|
|
||||||
|
## Intermediate documents:
|
||||||
|
*.dvi
|
||||||
|
*.xdv
|
||||||
|
*-converted-to.*
|
||||||
|
# these rules might exclude image files for figures etc.
|
||||||
|
# *.ps
|
||||||
|
# *.eps
|
||||||
|
*.pdf
|
||||||
|
|
||||||
|
## Generated if empty string is given at "Please type another file name for output:"
|
||||||
|
.pdf
|
||||||
|
|
||||||
|
## Bibliography auxiliary files (bibtex/biblatex/biber):
|
||||||
|
*.bbl
|
||||||
|
*.bcf
|
||||||
|
*.blg
|
||||||
|
*-blx.aux
|
||||||
|
*-blx.bib
|
||||||
|
*.run.xml
|
||||||
|
|
||||||
|
## Build tool auxiliary files:
|
||||||
|
*.fdb_latexmk
|
||||||
|
*.synctex
|
||||||
|
*.synctex(busy)
|
||||||
|
*.synctex.gz
|
||||||
|
*.synctex.gz(busy)
|
||||||
|
*.pdfsync
|
||||||
|
|
||||||
|
## Build tool directories for auxiliary files
|
||||||
|
# latexrun
|
||||||
|
latex.out/
|
||||||
|
|
||||||
|
## Auxiliary and intermediate files from other packages:
|
||||||
|
# algorithms
|
||||||
|
*.alg
|
||||||
|
*.loa
|
||||||
|
|
||||||
|
# achemso
|
||||||
|
acs-*.bib
|
||||||
|
|
||||||
|
# amsthm
|
||||||
|
*.thm
|
||||||
|
|
||||||
|
# beamer
|
||||||
|
*.nav
|
||||||
|
*.pre
|
||||||
|
*.snm
|
||||||
|
*.vrb
|
||||||
|
|
||||||
|
# changes
|
||||||
|
*.soc
|
||||||
|
|
||||||
|
# comment
|
||||||
|
*.cut
|
||||||
|
|
||||||
|
# cprotect
|
||||||
|
*.cpt
|
||||||
|
|
||||||
|
# elsarticle (documentclass of Elsevier journals)
|
||||||
|
*.spl
|
||||||
|
|
||||||
|
# endnotes
|
||||||
|
*.ent
|
||||||
|
|
||||||
|
# fixme
|
||||||
|
*.lox
|
||||||
|
|
||||||
|
# feynmf/feynmp
|
||||||
|
*.mf
|
||||||
|
*.mp
|
||||||
|
*.t[1-9]
|
||||||
|
*.t[1-9][0-9]
|
||||||
|
*.tfm
|
||||||
|
|
||||||
|
#(r)(e)ledmac/(r)(e)ledpar
|
||||||
|
*.end
|
||||||
|
*.?end
|
||||||
|
*.[1-9]
|
||||||
|
*.[1-9][0-9]
|
||||||
|
*.[1-9][0-9][0-9]
|
||||||
|
*.[1-9]R
|
||||||
|
*.[1-9][0-9]R
|
||||||
|
*.[1-9][0-9][0-9]R
|
||||||
|
*.eledsec[1-9]
|
||||||
|
*.eledsec[1-9]R
|
||||||
|
*.eledsec[1-9][0-9]
|
||||||
|
*.eledsec[1-9][0-9]R
|
||||||
|
*.eledsec[1-9][0-9][0-9]
|
||||||
|
*.eledsec[1-9][0-9][0-9]R
|
||||||
|
|
||||||
|
# glossaries
|
||||||
|
*.acn
|
||||||
|
*.acr
|
||||||
|
*.glg
|
||||||
|
*.glo
|
||||||
|
*.gls
|
||||||
|
*.glsdefs
|
||||||
|
*.lzo
|
||||||
|
*.lzs
|
||||||
|
*.slg
|
||||||
|
*.slo
|
||||||
|
*.sls
|
||||||
|
|
||||||
|
# uncomment this for glossaries-extra (will ignore makeindex's style files!)
|
||||||
|
# *.ist
|
||||||
|
|
||||||
|
# gnuplot
|
||||||
|
*.gnuplot
|
||||||
|
*.table
|
||||||
|
|
||||||
|
# gnuplottex
|
||||||
|
*-gnuplottex-*
|
||||||
|
|
||||||
|
# gregoriotex
|
||||||
|
*.gaux
|
||||||
|
*.glog
|
||||||
|
*.gtex
|
||||||
|
|
||||||
|
# htlatex
|
||||||
|
*.4ct
|
||||||
|
*.4tc
|
||||||
|
*.idv
|
||||||
|
*.lg
|
||||||
|
*.trc
|
||||||
|
*.xref
|
||||||
|
|
||||||
|
# hyperref
|
||||||
|
*.brf
|
||||||
|
|
||||||
|
# knitr
|
||||||
|
*-concordance.tex
|
||||||
|
# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files
|
||||||
|
# *.tikz
|
||||||
|
*-tikzDictionary
|
||||||
|
|
||||||
|
# listings
|
||||||
|
*.lol
|
||||||
|
|
||||||
|
# luatexja-ruby
|
||||||
|
*.ltjruby
|
||||||
|
|
||||||
|
# makeidx
|
||||||
|
*.idx
|
||||||
|
*.ilg
|
||||||
|
*.ind
|
||||||
|
|
||||||
|
# minitoc
|
||||||
|
*.maf
|
||||||
|
*.mlf
|
||||||
|
*.mlt
|
||||||
|
*.mtc[0-9]*
|
||||||
|
*.slf[0-9]*
|
||||||
|
*.slt[0-9]*
|
||||||
|
*.stc[0-9]*
|
||||||
|
|
||||||
|
# minted
|
||||||
|
_minted*
|
||||||
|
*.pyg
|
||||||
|
|
||||||
|
# morewrites
|
||||||
|
*.mw
|
||||||
|
|
||||||
|
# newpax
|
||||||
|
*.newpax
|
||||||
|
|
||||||
|
# nomencl
|
||||||
|
*.nlg
|
||||||
|
*.nlo
|
||||||
|
*.nls
|
||||||
|
|
||||||
|
# pax
|
||||||
|
*.pax
|
||||||
|
|
||||||
|
# pdfpcnotes
|
||||||
|
*.pdfpc
|
||||||
|
|
||||||
|
# sagetex
|
||||||
|
*.sagetex.sage
|
||||||
|
*.sagetex.py
|
||||||
|
*.sagetex.scmd
|
||||||
|
|
||||||
|
# scrwfile
|
||||||
|
*.wrt
|
||||||
|
|
||||||
|
# svg
|
||||||
|
svg-inkscape/
|
||||||
|
|
||||||
|
# sympy
|
||||||
|
*.sout
|
||||||
|
*.sympy
|
||||||
|
sympy-plots-for-*.tex/
|
||||||
|
|
||||||
|
# pdfcomment
|
||||||
|
*.upa
|
||||||
|
*.upb
|
||||||
|
|
||||||
|
# pythontex
|
||||||
|
*.pytxcode
|
||||||
|
pythontex-files-*/
|
||||||
|
|
||||||
|
# tcolorbox
|
||||||
|
*.listing
|
||||||
|
|
||||||
|
# thmtools
|
||||||
|
*.loe
|
||||||
|
|
||||||
|
# TikZ & PGF
|
||||||
|
*.dpth
|
||||||
|
*.md5
|
||||||
|
*.auxlock
|
||||||
|
|
||||||
|
# titletoc
|
||||||
|
*.ptc
|
||||||
|
|
||||||
|
# todonotes
|
||||||
|
*.tdo
|
||||||
|
|
||||||
|
# vhistory
|
||||||
|
*.hst
|
||||||
|
*.ver
|
||||||
|
|
||||||
|
# easy-todo
|
||||||
|
*.lod
|
||||||
|
|
||||||
|
# xcolor
|
||||||
|
*.xcp
|
||||||
|
|
||||||
|
# xmpincl
|
||||||
|
*.xmpi
|
||||||
|
|
||||||
|
# xindy
|
||||||
|
*.xdy
|
||||||
|
|
||||||
|
# xypic precompiled matrices and outlines
|
||||||
|
*.xyc
|
||||||
|
*.xyd
|
||||||
|
|
||||||
|
# endfloat
|
||||||
|
*.ttt
|
||||||
|
*.fff
|
||||||
|
|
||||||
|
# Latexian
|
||||||
|
TSWLatexianTemp*
|
||||||
|
|
||||||
|
## Editors:
|
||||||
|
# WinEdt
|
||||||
|
*.bak
|
||||||
|
*.sav
|
||||||
|
|
||||||
|
# Texpad
|
||||||
|
.texpadtmp
|
||||||
|
|
||||||
|
# LyX
|
||||||
|
*.lyx~
|
||||||
|
|
||||||
|
# Kile
|
||||||
|
*.backup
|
||||||
|
|
||||||
|
# gummi
|
||||||
|
.*.swp
|
||||||
|
|
||||||
|
# KBibTeX
|
||||||
|
*~[0-9]*
|
||||||
|
|
||||||
|
# TeXnicCenter
|
||||||
|
*.tps
|
||||||
|
|
||||||
|
# auto folder when using emacs and auctex
|
||||||
|
./auto/*
|
||||||
|
*.el
|
||||||
|
|
||||||
|
# expex forward references with \gathertags
|
||||||
|
*-tags.tex
|
||||||
|
|
||||||
|
# standalone packages
|
||||||
|
*.sta
|
||||||
|
|
||||||
|
# Makeindex log files
|
||||||
|
*.lpz
|
||||||
|
|
||||||
|
# xwatermark package
|
||||||
|
*.xwm
|
||||||
|
|
||||||
|
# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib
|
||||||
|
# option is specified. Footnotes are the stored in a file with suffix Notes.bib.
|
||||||
|
# Uncomment the next line to have this generated file ignored.
|
||||||
|
#*Notes.bib
|
||||||
|
|
||||||
|
### TeX Patch ###
|
||||||
|
# LIPIcs / OASIcs
|
||||||
|
*.vtc
|
||||||
|
|
||||||
|
# glossaries
|
||||||
|
*.glstex
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/tex,r
|
||||||
|
|
||||||
|
*.html
|
||||||
|
bib
|
||||||
|
# Sweave-specific
|
||||||
|
*.tex
|
||||||
13
doc/hci-doc.Rproj
Normal file
13
doc/hci-doc.Rproj
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
Version: 1.0
|
||||||
|
|
||||||
|
RestoreWorkspace: Default
|
||||||
|
SaveWorkspace: Default
|
||||||
|
AlwaysSaveHistory: Default
|
||||||
|
|
||||||
|
EnableCodeIndexing: Yes
|
||||||
|
UseSpacesForTab: Yes
|
||||||
|
NumSpacesForTab: 2
|
||||||
|
Encoding: UTF-8
|
||||||
|
|
||||||
|
RnwWeave: knitr
|
||||||
|
LaTeX: pdfLaTeX
|
||||||
962
doc/renv.lock
Normal file
962
doc/renv.lock
Normal file
File diff suppressed because one or more lines are too long
7
doc/renv/.gitignore
vendored
Normal file
7
doc/renv/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
library/
|
||||||
|
local/
|
||||||
|
cellar/
|
||||||
|
lock/
|
||||||
|
python/
|
||||||
|
sandbox/
|
||||||
|
staging/
|
||||||
1403
doc/renv/activate.R
Normal file
1403
doc/renv/activate.R
Normal file
File diff suppressed because it is too large
Load Diff
20
doc/renv/settings.json
Normal file
20
doc/renv/settings.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"bioconductor.version": null,
|
||||||
|
"external.libraries": [],
|
||||||
|
"ignored.packages": [],
|
||||||
|
"package.dependency.fields": [
|
||||||
|
"Imports",
|
||||||
|
"Depends",
|
||||||
|
"LinkingTo"
|
||||||
|
],
|
||||||
|
"ppm.enabled": null,
|
||||||
|
"ppm.ignored.urls": [],
|
||||||
|
"r.version": null,
|
||||||
|
"snapshot.dev": false,
|
||||||
|
"snapshot.type": "implicit",
|
||||||
|
"use.cache": true,
|
||||||
|
"vcs.ignore.cellar": true,
|
||||||
|
"vcs.ignore.library": true,
|
||||||
|
"vcs.ignore.local": true,
|
||||||
|
"vcs.manage.ignores": true
|
||||||
|
}
|
||||||
34
doc/report.Rnw
Normal file
34
doc/report.Rnw
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
\documentclass{article}
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
\section{Abstract}\label{abstract}
|
||||||
|
|
||||||
|
\section{Introduction}\label{introduction}
|
||||||
|
|
||||||
|
\section{Keyboard Designs}\label{keyboard-designs}
|
||||||
|
|
||||||
|
\section{Experiment}\label{experiment}
|
||||||
|
|
||||||
|
\subsection{Participants}\label{participants}
|
||||||
|
|
||||||
|
\subsection{Apparatus}\label{apparatus}
|
||||||
|
|
||||||
|
\subsection{Procedure}\label{procedure}
|
||||||
|
|
||||||
|
\section{Results}\label{results}
|
||||||
|
|
||||||
|
\subsection{Descriptive Statistics}\label{descriptive-statistics}
|
||||||
|
|
||||||
|
\subsubsection{Objective Measures}\label{objective-measures}
|
||||||
|
|
||||||
|
\subsubsection{Subjective Measures}\label{subjective-measures}
|
||||||
|
|
||||||
|
\subsection{Inferential Statistics}\label{inferential-statistics}
|
||||||
|
|
||||||
|
\subsubsection{Objective Measures}\label{objective-measures-1}
|
||||||
|
|
||||||
|
\subsubsection{Subjective Measures}\label{subjective-measures-1}
|
||||||
|
|
||||||
|
\section{Discussion}\label{discussion}
|
||||||
|
|
||||||
|
\end{document}
|
||||||
3982
package-lock.json
generated
3982
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
35
package.json
35
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "keeb",
|
"name": "keeb",
|
||||||
"version": "0.1.0",
|
"version": "1.0.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
"start": "ng serve",
|
"start": "ng serve",
|
||||||
@@ -10,24 +10,26 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^17.0.0",
|
"@angular/animations": "^18.2.14",
|
||||||
"@angular/common": "^17.0.0",
|
"@angular/common": "^18.2.14",
|
||||||
"@angular/compiler": "^17.0.0",
|
"@angular/compiler": "^18.2.14",
|
||||||
"@angular/core": "^17.0.0",
|
"@angular/core": "^18.2.14",
|
||||||
"@angular/forms": "^17.0.0",
|
"@angular/forms": "^18.2.14",
|
||||||
"@angular/platform-browser": "^17.0.0",
|
"@angular/platform-browser": "^18.2.14",
|
||||||
"@angular/platform-browser-dynamic": "^17.0.0",
|
"@angular/platform-browser-dynamic": "^18.2.14",
|
||||||
"@angular/router": "^17.0.0",
|
"@angular/router": "^18.2.14",
|
||||||
|
"@tauri-apps/api": "^2",
|
||||||
|
"@tauri-apps/plugin-cli": "^2.4.1",
|
||||||
|
"@tauri-apps/plugin-opener": "^2",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"zone.js": "~0.14.2",
|
"zone.js": "~0.14.2"
|
||||||
"@tauri-apps/api": "^2",
|
|
||||||
"@tauri-apps/plugin-opener": "^2"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "^17.0.0",
|
"@angular-devkit/build-angular": "^18.2.21",
|
||||||
"@angular/cli": "^17.0.0",
|
"@angular/cli": "^18.2.21",
|
||||||
"@angular/compiler-cli": "^17.0.0",
|
"@angular/compiler-cli": "^18.2.14",
|
||||||
|
"@tauri-apps/cli": "^2",
|
||||||
"@types/jasmine": "~5.1.0",
|
"@types/jasmine": "~5.1.0",
|
||||||
"jasmine-core": "~5.1.0",
|
"jasmine-core": "~5.1.0",
|
||||||
"karma": "~6.4.0",
|
"karma": "~6.4.0",
|
||||||
@@ -35,7 +37,6 @@
|
|||||||
"karma-coverage": "~2.2.0",
|
"karma-coverage": "~2.2.0",
|
||||||
"karma-jasmine": "~5.1.0",
|
"karma-jasmine": "~5.1.0",
|
||||||
"karma-jasmine-html-reporter": "~2.1.0",
|
"karma-jasmine-html-reporter": "~2.1.0",
|
||||||
"typescript": "~5.4.0",
|
"typescript": "~5.4.0"
|
||||||
"@tauri-apps/cli": "^2"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
195
src-tauri/Cargo.lock
generated
195
src-tauri/Cargo.lock
generated
@@ -41,6 +41,56 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.6.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"anstyle-parse",
|
||||||
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"is_terminal_polyfill",
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "3.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"once_cell_polyfill",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.100"
|
version = "1.0.100"
|
||||||
@@ -456,6 +506,39 @@ dependencies = [
|
|||||||
"windows-link 0.2.1",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.5.54"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.5.54"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.7.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "combine"
|
name = "combine"
|
||||||
version = "4.6.7"
|
version = "4.6.7"
|
||||||
@@ -840,7 +923,7 @@ dependencies = [
|
|||||||
"objc2 0.6.3",
|
"objc2 0.6.3",
|
||||||
"objc2-app-kit",
|
"objc2-app-kit",
|
||||||
"objc2-foundation 0.3.2",
|
"objc2-foundation 0.3.2",
|
||||||
"windows",
|
"windows 0.61.3",
|
||||||
"x11rb",
|
"x11rb",
|
||||||
"xkbcommon",
|
"xkbcommon",
|
||||||
"xkeysym",
|
"xkeysym",
|
||||||
@@ -1750,6 +1833,12 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is_terminal_polyfill"
|
||||||
|
version = "1.70.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.15"
|
version = "1.0.15"
|
||||||
@@ -1843,7 +1932,9 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-build",
|
"tauri-build",
|
||||||
|
"tauri-plugin-cli",
|
||||||
"tauri-plugin-opener",
|
"tauri-plugin-opener",
|
||||||
|
"windows 0.62.2",
|
||||||
"x11",
|
"x11",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2433,6 +2524,12 @@ version = "1.21.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell_polyfill"
|
||||||
|
version = "1.70.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "open"
|
name = "open"
|
||||||
version = "5.3.2"
|
version = "5.3.2"
|
||||||
@@ -3598,7 +3695,7 @@ dependencies = [
|
|||||||
"tao-macros",
|
"tao-macros",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"url",
|
"url",
|
||||||
"windows",
|
"windows 0.61.3",
|
||||||
"windows-core 0.61.2",
|
"windows-core 0.61.2",
|
||||||
"windows-version",
|
"windows-version",
|
||||||
"x11-dl",
|
"x11-dl",
|
||||||
@@ -3670,7 +3767,7 @@ dependencies = [
|
|||||||
"webkit2gtk",
|
"webkit2gtk",
|
||||||
"webview2-com",
|
"webview2-com",
|
||||||
"window-vibrancy",
|
"window-vibrancy",
|
||||||
"windows",
|
"windows 0.61.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3753,6 +3850,21 @@ dependencies = [
|
|||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tauri-plugin-cli"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28e78fb2c09a81546bcd376d34db4bda5769270d00990daa9f0d6e7ac1107e25"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tauri",
|
||||||
|
"tauri-plugin",
|
||||||
|
"thiserror 2.0.17",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-opener"
|
name = "tauri-plugin-opener"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
@@ -3771,7 +3883,7 @@ dependencies = [
|
|||||||
"tauri-plugin",
|
"tauri-plugin",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
"url",
|
"url",
|
||||||
"windows",
|
"windows 0.61.3",
|
||||||
"zbus",
|
"zbus",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -3797,7 +3909,7 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
"webkit2gtk",
|
"webkit2gtk",
|
||||||
"webview2-com",
|
"webview2-com",
|
||||||
"windows",
|
"windows 0.61.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3823,7 +3935,7 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
"webkit2gtk",
|
"webkit2gtk",
|
||||||
"webview2-com",
|
"webview2-com",
|
||||||
"windows",
|
"windows 0.61.3",
|
||||||
"wry",
|
"wry",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4319,6 +4431,12 @@ version = "1.0.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.18.1"
|
version = "1.18.1"
|
||||||
@@ -4550,7 +4668,7 @@ checksum = "d4ba622a989277ef3886dd5afb3e280e3dd6d974b766118950a08f8f678ad6a4"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"webview2-com-macros",
|
"webview2-com-macros",
|
||||||
"webview2-com-sys",
|
"webview2-com-sys",
|
||||||
"windows",
|
"windows 0.61.3",
|
||||||
"windows-core 0.61.2",
|
"windows-core 0.61.2",
|
||||||
"windows-implement",
|
"windows-implement",
|
||||||
"windows-interface",
|
"windows-interface",
|
||||||
@@ -4574,7 +4692,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "36695906a1b53a3bf5c4289621efedac12b73eeb0b89e7e1a89b517302d5d75c"
|
checksum = "36695906a1b53a3bf5c4289621efedac12b73eeb0b89e7e1a89b517302d5d75c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
"windows",
|
"windows 0.61.3",
|
||||||
"windows-core 0.61.2",
|
"windows-core 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4630,11 +4748,23 @@ version = "0.61.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
|
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-collections",
|
"windows-collections 0.2.0",
|
||||||
"windows-core 0.61.2",
|
"windows-core 0.61.2",
|
||||||
"windows-future",
|
"windows-future 0.2.1",
|
||||||
"windows-link 0.1.3",
|
"windows-link 0.1.3",
|
||||||
"windows-numerics",
|
"windows-numerics 0.2.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows"
|
||||||
|
version = "0.62.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
|
||||||
|
dependencies = [
|
||||||
|
"windows-collections 0.3.2",
|
||||||
|
"windows-core 0.62.2",
|
||||||
|
"windows-future 0.3.2",
|
||||||
|
"windows-numerics 0.3.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4646,6 +4776,15 @@ dependencies = [
|
|||||||
"windows-core 0.61.2",
|
"windows-core 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-collections"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
|
||||||
|
dependencies = [
|
||||||
|
"windows-core 0.62.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-core"
|
name = "windows-core"
|
||||||
version = "0.61.2"
|
version = "0.61.2"
|
||||||
@@ -4680,7 +4819,18 @@ checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-core 0.61.2",
|
"windows-core 0.61.2",
|
||||||
"windows-link 0.1.3",
|
"windows-link 0.1.3",
|
||||||
"windows-threading",
|
"windows-threading 0.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-future"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-core 0.62.2",
|
||||||
|
"windows-link 0.2.1",
|
||||||
|
"windows-threading 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4727,6 +4877,16 @@ dependencies = [
|
|||||||
"windows-link 0.1.3",
|
"windows-link 0.1.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-numerics"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
|
||||||
|
dependencies = [
|
||||||
|
"windows-core 0.62.2",
|
||||||
|
"windows-link 0.2.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-result"
|
name = "windows-result"
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
@@ -4856,6 +5016,15 @@ dependencies = [
|
|||||||
"windows-link 0.1.3",
|
"windows-link 0.1.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-threading"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link 0.2.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-version"
|
name = "windows-version"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
@@ -5082,7 +5251,7 @@ dependencies = [
|
|||||||
"webkit2gtk",
|
"webkit2gtk",
|
||||||
"webkit2gtk-sys",
|
"webkit2gtk-sys",
|
||||||
"webview2-com",
|
"webview2-com",
|
||||||
"windows",
|
"windows 0.61.3",
|
||||||
"windows-core 0.61.2",
|
"windows-core 0.61.2",
|
||||||
"windows-version",
|
"windows-version",
|
||||||
"x11-dl",
|
"x11-dl",
|
||||||
|
|||||||
@@ -23,9 +23,20 @@ tauri-plugin-opener = "2"
|
|||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
enigo = "0.6.1"
|
enigo = "0.6.1"
|
||||||
x11 = { version = "2.21.0", features = ["xlib"] }
|
|
||||||
raw-window-handle = "0.6.2"
|
raw-window-handle = "0.6.2"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
|
windows = { version = "0.62.2", features = [
|
||||||
|
"Win32_UI_WindowsAndMessaging",
|
||||||
|
"Win32_Foundation"
|
||||||
|
] }
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
x11 = { version = "2.21.0", features = ["xlib"] }
|
||||||
|
|
||||||
|
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
||||||
|
tauri-plugin-cli = "2"
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
incremental = true # Compile your binary in smaller steps.
|
incremental = true # Compile your binary in smaller steps.
|
||||||
|
|
||||||
|
|||||||
14
src-tauri/capabilities/desktop.json
Normal file
14
src-tauri/capabilities/desktop.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"identifier": "desktop-capability",
|
||||||
|
"platforms": [
|
||||||
|
"macOS",
|
||||||
|
"windows",
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"windows": [
|
||||||
|
"main"
|
||||||
|
],
|
||||||
|
"permissions": [
|
||||||
|
"cli:default"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,25 +1,102 @@
|
|||||||
use enigo::{Direction::Click, Enigo, Key, Keyboard, Settings};
|
use enigo::{Direction, Enigo, Key, Keyboard, Settings};
|
||||||
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle, WindowHandle};
|
#[cfg(target_os = "linux")]
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use tauri::Manager;
|
use tauri::Manager;
|
||||||
|
|
||||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn greet(name: &str) -> String {
|
fn greet(name: &str) -> String {
|
||||||
let mut enigo = Enigo::new(&Settings::default()).unwrap();
|
|
||||||
enigo.key(Key::Unicode('a'), Click);
|
|
||||||
format!("Hello, {}! You've been greeted from Rust!", name)
|
format!("Hello, {}! You've been greeted from Rust!", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn send_key(key: String) -> Result<(), String> {
|
||||||
|
let mut enigo = Enigo::new(&Settings::default()).map_err(|e| format!("Enigo error: {}", e))?;
|
||||||
|
|
||||||
|
if key.len() == 1 {
|
||||||
|
let ch = key.chars().next().unwrap();
|
||||||
|
|
||||||
|
// Check if uppercase letter
|
||||||
|
if ch.is_uppercase() && ch.is_alphabetic() {
|
||||||
|
// Send Shift + lowercase letter
|
||||||
|
enigo
|
||||||
|
.key(Key::Shift, Direction::Press)
|
||||||
|
.map_err(|e| format!("Shift press error: {}", e))?;
|
||||||
|
enigo
|
||||||
|
.key(
|
||||||
|
Key::Unicode(ch.to_lowercase().next().unwrap()),
|
||||||
|
Direction::Click,
|
||||||
|
)
|
||||||
|
.map_err(|e| format!("Key error: {}", e))?;
|
||||||
|
enigo
|
||||||
|
.key(Key::Shift, Direction::Release)
|
||||||
|
.map_err(|e| format!("Shift release error: {}", e))?;
|
||||||
|
} else {
|
||||||
|
// Send character as-is (lowercase or non-letter)
|
||||||
|
enigo
|
||||||
|
.key(Key::Unicode(ch), Direction::Click)
|
||||||
|
.map_err(|e| format!("Key error: {}", e))?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Special keys
|
||||||
|
match key.as_str() {
|
||||||
|
"Enter" => enigo
|
||||||
|
.key(Key::Return, Direction::Click)
|
||||||
|
.map_err(|e| format!("Key error: {}", e))?,
|
||||||
|
"Space" => enigo
|
||||||
|
.key(Key::Space, Direction::Click)
|
||||||
|
.map_err(|e| format!("Key error: {}", e))?,
|
||||||
|
"Backspace" => enigo
|
||||||
|
.key(Key::Backspace, Direction::Click)
|
||||||
|
.map_err(|e| format!("Key error: {}", e))?,
|
||||||
|
_ => return Err("Unknown key".to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
|
.plugin(tauri_plugin_cli::init())
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
let window = app.get_webview_window("main").unwrap();
|
let window = app.get_webview_window("main").unwrap();
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
#![allow(deprecated)]
|
||||||
|
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
|
||||||
|
use windows::Win32::Foundation::HWND;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::{GetWindowLongPtrW, SetWindowLongPtrW, GWL_EXSTYLE, WS_EX_NOACTIVATE,};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
if let Ok(RawWindowHandle::Win32(handle)) = window.raw_window_handle() {
|
||||||
|
let hwnd = HWND(handle.hwnd.get() as *mut core::ffi::c_void);
|
||||||
|
|
||||||
|
// Get current extended window style
|
||||||
|
let ex_style = GetWindowLongPtrW(hwnd, GWL_EXSTYLE) as u32;
|
||||||
|
|
||||||
|
// Add WS_EX_NOACTIVATE flag
|
||||||
|
let new_style = ex_style | WS_EX_NOACTIVATE.0;
|
||||||
|
SetWindowLongPtrW(hwnd, GWL_EXSTYLE, new_style as isize);
|
||||||
|
|
||||||
|
// Position window as topmost without activating
|
||||||
|
/*
|
||||||
|
SetWindowPos(
|
||||||
|
hwnd,
|
||||||
|
Some(HWND_TOPMOST),
|
||||||
|
0, 0, 0, 0,
|
||||||
|
SWP_NOACTIVATE
|
||||||
|
); */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
|
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
|
||||||
use x11::xlib;
|
use x11::xlib;
|
||||||
// raw X11 pointers here
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let handle = window.raw_window_handle();
|
let handle = window.raw_window_handle();
|
||||||
if let Ok(RawWindowHandle::Xlib(h)) = handle {
|
if let Ok(RawWindowHandle::Xlib(h)) = handle {
|
||||||
@@ -28,7 +105,7 @@ pub fn run() {
|
|||||||
let hints = xlib::XAllocWMHints();
|
let hints = xlib::XAllocWMHints();
|
||||||
if !hints.is_null() {
|
if !hints.is_null() {
|
||||||
(*hints).flags = xlib::InputHint;
|
(*hints).flags = xlib::InputHint;
|
||||||
(*hints).input = 0; // Set input hint to False
|
(*hints).input = 0;
|
||||||
xlib::XSetWMHints(display, h.window, hints);
|
xlib::XSetWMHints(display, h.window, hints);
|
||||||
xlib::XFree(hints as *mut _);
|
xlib::XFree(hints as *mut _);
|
||||||
}
|
}
|
||||||
@@ -37,10 +114,11 @@ pub fn run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.plugin(tauri_plugin_opener::init())
|
.plugin(tauri_plugin_opener::init())
|
||||||
.invoke_handler(tauri::generate_handler![greet])
|
.invoke_handler(tauri::generate_handler![greet, send_key])
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,13 +14,41 @@
|
|||||||
{
|
{
|
||||||
"title": "keeb",
|
"title": "keeb",
|
||||||
"width": 800,
|
"width": 800,
|
||||||
"height": 600
|
"height": 700,
|
||||||
|
"minWidth": 800,
|
||||||
|
"minHeight": 700,
|
||||||
|
"resizable": true,
|
||||||
|
"center": true,
|
||||||
|
"alwaysOnTop": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"security": {
|
"security": {
|
||||||
"csp": null
|
"csp": null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"plugins": {
|
||||||
|
"cli":{
|
||||||
|
"description": "On-Screen keyboard for Human-Computer Interface studies",
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "order",
|
||||||
|
"short": "o",
|
||||||
|
"takesValue": true,
|
||||||
|
"multiple": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "limit",
|
||||||
|
"short": "l",
|
||||||
|
"takesValue": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "demo",
|
||||||
|
"short": "d",
|
||||||
|
"takesValue": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"bundle": {
|
"bundle": {
|
||||||
"active": true,
|
"active": true,
|
||||||
"targets": "all",
|
"targets": "all",
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
.logo.angular:hover {
|
|
||||||
filter: drop-shadow(0 0 2em #e32727);
|
|
||||||
}
|
|
||||||
:root {
|
:root {
|
||||||
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@@ -33,10 +31,6 @@
|
|||||||
transition: 0.75s;
|
transition: 0.75s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo.tauri:hover {
|
|
||||||
filter: drop-shadow(0 0 2em #24c8db);
|
|
||||||
}
|
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -109,4 +103,56 @@ button {
|
|||||||
button:active {
|
button:active {
|
||||||
background-color: #0f0f0f69;
|
background-color: #0f0f0f69;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.key-button {
|
||||||
|
background: rgb(58, 56, 56);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin: 20px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.key-button {
|
||||||
|
min-width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
border: 2px solid #ddd;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.key-button:hover {
|
||||||
|
background: #b1b1b1;
|
||||||
|
border-color: #3d3d3d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.key-button:active {
|
||||||
|
background: #d0d0d0;
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shift-key.active {
|
||||||
|
background: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
border-color: #45a049;
|
||||||
|
}
|
||||||
|
|
||||||
|
.space-key {
|
||||||
|
min-width: 300px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,30 @@
|
|||||||
<main class="container">
|
<main class="container">
|
||||||
<h1>Welcome to Tauri + Angular!</h1>
|
<!-- Layout Switcher -->
|
||||||
|
<div class="layout-controls">
|
||||||
<div class="row">
|
<button (click)="switchLayout()" class="layout-button">
|
||||||
<a href="https://tauri.app" target="_blank">
|
Switch Layout: {{ currentLayout.toString() }}
|
||||||
<img src="/assets/tauri.svg" class="logo tauri" alt="Tauri logo" />
|
</button>
|
||||||
</a>
|
|
||||||
<a href="https://angular.io" target="_blank">
|
|
||||||
<img src="/assets/angular.svg" class="logo angular" alt="Angular logo" />
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
<p>Click on the logos to learn more about the frameworks</p>
|
|
||||||
|
|
||||||
<form class="row" (submit)="greet($event, greetInput.value)">
|
<!-- Conditional Rendering basierend auf currentLayout -->
|
||||||
<input #greetInput id="greet-input" placeholder="Enter a name..." />
|
<app-qwerty-keyboard
|
||||||
<button type="submit">Greet</button>
|
*ngIf="currentLayout === Keyboards.QWERTY"
|
||||||
</form>
|
[shiftActive]="shiftActive"
|
||||||
<p>{{ greetingMessage }}</p>
|
(keyPressed)="handleKeyPress($event)"
|
||||||
|
(shiftToggled)="toggleShift()">
|
||||||
|
</app-qwerty-keyboard>
|
||||||
|
|
||||||
|
<app-dvorak-keyboard
|
||||||
|
*ngIf="currentLayout === Keyboards.DVORAK"
|
||||||
|
[shiftActive]="shiftActive"
|
||||||
|
(keyPressed)="handleKeyPress($event)"
|
||||||
|
(shiftToggled)="toggleShift()">
|
||||||
|
</app-dvorak-keyboard>
|
||||||
|
|
||||||
|
<app-circle-keyboard
|
||||||
|
*ngIf="currentLayout === Keyboards.CIRCLE"
|
||||||
|
[shiftActive]="shiftActive"
|
||||||
|
(keyPressed)="handleKeyPress($event)"
|
||||||
|
(shiftToggled)="toggleShift()">
|
||||||
|
</app-circle-keyboard>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -1,24 +1,66 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component, ViewChild, ElementRef, AfterViewInit, OnInit } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { RouterOutlet } from '@angular/router';
|
import { RouterOutlet } from '@angular/router';
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
|
import { getMatches } from '@tauri-apps/plugin-cli';
|
||||||
|
import { QwertyKeyboardComponent } from './keyboards/qwerty-keyboard.component';
|
||||||
|
import { DvorakKeyboardComponent } from './keyboards/dvorak-keyboard.component';
|
||||||
|
import { CircleKeyboardComponent } from './keyboards/circle-keyboard.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, RouterOutlet],
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
RouterOutlet,
|
||||||
|
QwertyKeyboardComponent,
|
||||||
|
DvorakKeyboardComponent,
|
||||||
|
CircleKeyboardComponent
|
||||||
|
],
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrl: './app.component.css'
|
styleUrl: './app.component.css',
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent implements OnInit {
|
||||||
greetingMessage = "";
|
Keyboards = Keyboards;
|
||||||
|
currentLayout: Keyboards = Keyboards.QWERTY;
|
||||||
|
shiftActive = false;
|
||||||
|
async ngOnInit() {
|
||||||
|
|
||||||
greet(event: SubmitEvent, name: string): void {
|
const cli = await getMatches();
|
||||||
event.preventDefault();
|
}
|
||||||
|
|
||||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
@ViewChild('greetInput') inputElement!: ElementRef;
|
||||||
invoke<string>("greet", { name }).then((text) => {
|
|
||||||
this.greetingMessage = text;
|
toggleShift(): void {
|
||||||
});
|
this.shiftActive = !this.shiftActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
switchLayout(): void {
|
||||||
|
if (this.currentLayout === Keyboards.QWERTY){
|
||||||
|
this.currentLayout = Keyboards.DVORAK;
|
||||||
|
} else if (this.currentLayout === Keyboards.DVORAK){
|
||||||
|
this.currentLayout = Keyboards.CIRCLE;
|
||||||
|
} else if (this.currentLayout === Keyboards.CIRCLE){
|
||||||
|
this.currentLayout = Keyboards.QWERTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleKeyPress(key: string): Promise<void> {
|
||||||
|
|
||||||
|
let finalKey = key;
|
||||||
|
if (key.length === 1) {
|
||||||
|
finalKey = this.shiftActive ? key.toUpperCase() : key.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
await invoke("send_key", { key: finalKey });
|
||||||
|
|
||||||
|
if (this.shiftActive && key.length === 1) {
|
||||||
|
this.shiftActive = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
enum Keyboards{
|
||||||
|
QWERTY,
|
||||||
|
DVORAK,
|
||||||
|
CIRCLE
|
||||||
|
}
|
||||||
|
|||||||
161
src/app/keyboards/circle-keyboard.component.css
Normal file
161
src/app/keyboards/circle-keyboard.component.css
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
.circle-keyboard-container {
|
||||||
|
position: relative;
|
||||||
|
width: 400px;
|
||||||
|
height: 400px;
|
||||||
|
margin: 50px auto;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Center Button Container */
|
||||||
|
.center-button-container {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 140px;
|
||||||
|
height: 140px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Center Button Sections */
|
||||||
|
.center-section {
|
||||||
|
position: absolute;
|
||||||
|
background: white;
|
||||||
|
color: #0f0f0f;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s;
|
||||||
|
border: 2px solid #ddd;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 10;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Top-left: Backspace (triangle) */
|
||||||
|
.top-left{
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 70px;
|
||||||
|
height: 70px;
|
||||||
|
border-radius: 70px 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Top-right: Enter*/
|
||||||
|
.top-right {
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 70px;
|
||||||
|
height: 70px;
|
||||||
|
border-radius: 0 70px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bottom: Space */
|
||||||
|
.bottom-left {
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 70px;
|
||||||
|
height: 70px;
|
||||||
|
border-radius: 0 0 0 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-left.active {
|
||||||
|
background: #4CAF50 !important;
|
||||||
|
color: white;
|
||||||
|
border-color: #45a049;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-right {
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 70px;
|
||||||
|
height: 70px;
|
||||||
|
border-radius: 0 0 70px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.middle-left {
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-100%, -50%);
|
||||||
|
width: 30px;
|
||||||
|
height: 55px;
|
||||||
|
border-radius: 35px 0 0 35px;
|
||||||
|
z-index: 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
.middle-right {
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(0%, -50%);
|
||||||
|
width: 30px;
|
||||||
|
height: 55px;
|
||||||
|
border-radius: 0 35px 35px 0;
|
||||||
|
z-index: 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 8 Circle Buttons */
|
||||||
|
.circle-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
width: 70px;
|
||||||
|
height: 70px;
|
||||||
|
margin: -35px 0 0 -35px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2px solid #ddd;
|
||||||
|
background: white;
|
||||||
|
color: #0f0f0f;
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s, border-color 0.2s;
|
||||||
|
box-shadow: 0 2px 2px rgba(0,0,0,0.2);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.second-ring {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: normal;
|
||||||
|
margin: -25px 0 0 -25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.third-ring {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: normal;
|
||||||
|
margin: -20px 0 0 -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fourth-ring {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: lighter;
|
||||||
|
margin: -20px 0 0 -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle-button:hover, .center-section:hover {
|
||||||
|
background: #b1b1b1;
|
||||||
|
border-color: #3d3d3d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle-button:active, .center-section:active {
|
||||||
|
background: #d0d0d0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.center-section {
|
||||||
|
background: rgb(58, 56, 56);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle-button {
|
||||||
|
background: rgb(58, 56, 56);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
104
src/app/keyboards/circle-keyboard.component.ts
Normal file
104
src/app/keyboards/circle-keyboard.component.ts
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
import { Component, Output, EventEmitter, Input } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
//TODO: Second and fourth ring of keys
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-circle-keyboard',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule],
|
||||||
|
template: `
|
||||||
|
<div class="circle-keyboard-container">
|
||||||
|
<!-- Center Button - Large with 3 sections -->
|
||||||
|
<div class="center-button-container">
|
||||||
|
<!-- Top half split: Backspace left, Enter right -->
|
||||||
|
<button class="center-section top-left"
|
||||||
|
(click)="keyPressed.emit('Backspace')"
|
||||||
|
title="Backspace">
|
||||||
|
⌫
|
||||||
|
</button>
|
||||||
|
<button class="center-section top-right"
|
||||||
|
(click)="keyPressed.emit('Enter')"
|
||||||
|
title="Enter">
|
||||||
|
↵
|
||||||
|
</button>
|
||||||
|
<!-- Bottom half: Space -->
|
||||||
|
<button class="center-section bottom-right"
|
||||||
|
(click)="keyPressed.emit('Space')"
|
||||||
|
title="Space">
|
||||||
|
␣
|
||||||
|
</button>
|
||||||
|
<button (click)="shiftToggled.emit()"
|
||||||
|
[class.active]="shiftActive"
|
||||||
|
class="center-section bottom-left">
|
||||||
|
⇧
|
||||||
|
</button>
|
||||||
|
<button class="center-section middle-left"
|
||||||
|
(click)="keyPressed.emit('.')"
|
||||||
|
title="Period">
|
||||||
|
.
|
||||||
|
</button>
|
||||||
|
<button class="center-section middle-right"
|
||||||
|
(click)="keyPressed.emit(',')"
|
||||||
|
title="Comma">
|
||||||
|
,
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 8 Surrounding Buttons in Circle -->
|
||||||
|
<button *ngFor="let key of circleKeysFirst; let i = index"
|
||||||
|
class="circle-button"
|
||||||
|
[style.transform]="getCirclePositionFromTop(i, 110)"
|
||||||
|
(click)="keyPressed.emit(key)">
|
||||||
|
{{ shiftActive ? key : key.toLowerCase() }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button *ngFor="let key of circleKeysSecond; let i = index"
|
||||||
|
class="circle-button second-ring"
|
||||||
|
[style.transform]="getCirclePositionFromTop(i, 150, 22.5)"
|
||||||
|
(click)="keyPressed.emit(key)">
|
||||||
|
{{ shiftActive ? key : key.toLowerCase() }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button *ngFor="let key of circleKeysThird; let i = index"
|
||||||
|
class="circle-button third-ring"
|
||||||
|
[style.transform]="getCirclePositionFromTop(i, 170)"
|
||||||
|
(click)="keyPressed.emit(key)">
|
||||||
|
{{ shiftActive ? key : key.toLowerCase() }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="circle-button fourth-ring"
|
||||||
|
[style.transform]="getCirclePositionFromTop(0, 182, -12)"
|
||||||
|
(click)="keyPressed.emit(circleKeysFourth[0])">
|
||||||
|
{{ shiftActive ? circleKeysFourth[0] : circleKeysFourth[0].toLowerCase() }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="circle-button fourth-ring"
|
||||||
|
[style.transform]="getCirclePositionFromTop(0, 182, 14)"
|
||||||
|
(click)="keyPressed.emit(circleKeysFourth[1])">
|
||||||
|
{{ shiftActive ? circleKeysFourth[1] : circleKeysFourth[1].toLowerCase() }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
styleUrl: './circle-keyboard.component.css'
|
||||||
|
})
|
||||||
|
export class CircleKeyboardComponent {
|
||||||
|
@Input() shiftActive = false;
|
||||||
|
@Output() keyPressed = new EventEmitter<string>();
|
||||||
|
@Output() shiftToggled = new EventEmitter<void>();
|
||||||
|
|
||||||
|
// 8 keys arranged in circle
|
||||||
|
circleKeysFirst = ['E', 'T', 'A', 'O', 'N', 'I', 'H', 'S'];
|
||||||
|
circleKeysSecond = ['R', 'L', 'D', 'U', 'C', 'M', 'W', 'Y'];
|
||||||
|
circleKeysThird = ['F', 'G', 'P', 'B', 'V', 'K', 'J', 'X'];
|
||||||
|
circleKeysFourth = ['Q', 'Z'];
|
||||||
|
|
||||||
|
// Calculate position for each button in circle
|
||||||
|
getCirclePositionFromTop(index: number, radius: number, offset: number = 0): string {
|
||||||
|
const angle = ((index * 45) - 90) + offset; // Start from top (0°), 45° apart
|
||||||
|
const angleRad = (angle * Math.PI) / 180;
|
||||||
|
const x = Math.cos(angleRad) * radius;
|
||||||
|
const y = Math.sin(angleRad) * radius;
|
||||||
|
return `translate(${x}px, ${y}px)`;
|
||||||
|
}
|
||||||
|
}
|
||||||
68
src/app/keyboards/dvorak-keyboard.component.ts
Normal file
68
src/app/keyboards/dvorak-keyboard.component.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import { Component, Output, EventEmitter, Input } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-dvorak-keyboard',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule],
|
||||||
|
template: `
|
||||||
|
<div class="keyboard">
|
||||||
|
<div class="keyboard-row">
|
||||||
|
<button *ngFor="let key of row1"
|
||||||
|
(click)="keyPressed.emit(key)"
|
||||||
|
class="key-button">
|
||||||
|
{{ shiftActive ? key : key.toLowerCase() }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="keyboard-row">
|
||||||
|
<button *ngFor="let key of row2"
|
||||||
|
(click)="keyPressed.emit(key)"
|
||||||
|
class="key-button">
|
||||||
|
{{ shiftActive ? key : key.toLowerCase() }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="keyboard-row">
|
||||||
|
<button *ngFor="let key of row3"
|
||||||
|
(click)="keyPressed.emit(key)"
|
||||||
|
class="key-button">
|
||||||
|
{{ shiftActive ? key : key.toLowerCase() }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="keyboard-row special-keys">
|
||||||
|
<button (click)="shiftToggled.emit()"
|
||||||
|
[class.active]="shiftActive"
|
||||||
|
class="key-button shift-key">
|
||||||
|
⇧ Shift
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button (click)="keyPressed.emit('Space')"
|
||||||
|
class="key-button space-key">
|
||||||
|
Leertaste
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button (click)="keyPressed.emit('Backspace')"
|
||||||
|
class="key-button">
|
||||||
|
⌫ Delete
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button (click)="keyPressed.emit('Enter')"
|
||||||
|
class="key-button">
|
||||||
|
↵ Enter
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
styleUrl: '../app.component.css'
|
||||||
|
})
|
||||||
|
export class DvorakKeyboardComponent {
|
||||||
|
@Input() shiftActive = false;
|
||||||
|
@Output() keyPressed = new EventEmitter<string>();
|
||||||
|
@Output() shiftToggled = new EventEmitter<void>();
|
||||||
|
|
||||||
|
row1 = [',', '.', 'P', 'Y', 'F', 'G', 'C', 'R', 'L'];
|
||||||
|
row2 = ['A', 'O', 'E', 'U', 'I', 'D', 'H', 'T', 'N', 'S'];
|
||||||
|
row3 = ['Q', 'J', 'K', 'X', 'B', 'M', 'W', 'V', 'Z'];
|
||||||
|
}
|
||||||
68
src/app/keyboards/qwerty-keyboard.component.ts
Normal file
68
src/app/keyboards/qwerty-keyboard.component.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import { Component, Output, EventEmitter, Input } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-qwerty-keyboard',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule],
|
||||||
|
template: `
|
||||||
|
<div class="keyboard">
|
||||||
|
<div class="keyboard-row">
|
||||||
|
<button *ngFor="let key of row1"
|
||||||
|
(click)="keyPressed.emit(key)"
|
||||||
|
class="key-button">
|
||||||
|
{{ shiftActive ? key : key.toLowerCase() }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="keyboard-row">
|
||||||
|
<button *ngFor="let key of row2"
|
||||||
|
(click)="keyPressed.emit(key)"
|
||||||
|
class="key-button">
|
||||||
|
{{ shiftActive ? key : key.toLowerCase() }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="keyboard-row">
|
||||||
|
<button *ngFor="let key of row3"
|
||||||
|
(click)="keyPressed.emit(key)"
|
||||||
|
class="key-button">
|
||||||
|
{{ shiftActive ? key : key.toLowerCase() }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="keyboard-row special-keys">
|
||||||
|
<button (click)="shiftToggled.emit()"
|
||||||
|
[class.active]="shiftActive"
|
||||||
|
class="key-button shift-key">
|
||||||
|
⇧ Shift
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button (click)="keyPressed.emit('Space')"
|
||||||
|
class="key-button space-key">
|
||||||
|
Leertaste
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button (click)="keyPressed.emit('Backspace')"
|
||||||
|
class="key-button">
|
||||||
|
⌫ Delete
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button (click)="keyPressed.emit('Enter')"
|
||||||
|
class="key-button">
|
||||||
|
↵ Enter
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
styleUrl: '../app.component.css'
|
||||||
|
})
|
||||||
|
export class QwertyKeyboardComponent {
|
||||||
|
@Input() shiftActive = false;
|
||||||
|
@Output() keyPressed = new EventEmitter<string>();
|
||||||
|
@Output() shiftToggled = new EventEmitter<void>();
|
||||||
|
|
||||||
|
row1 = ['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'];
|
||||||
|
row2 = ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'];
|
||||||
|
row3 = ['Z', 'X', 'C', 'V', 'B', 'N', 'M', ',', '.'];
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user