merge-notes
parent
640a2bfaba
commit
6f2f7f27fc
@ -0,0 +1 @@
|
|||||||
|
{"plan":[{"area_key":"1_1_town","notes":""},{"area_key":"1_1_1","notes":""},{"area_key":"1_1_2","notes":""},{"area_key":"1_1_2a","notes":""},{"area_key":"1_1_3","notes":""},{"area_key":"1_1_4_0","notes":""},{"area_key":"1_1_4_1","notes":""}],"current":0}
|
After Width: | Height: | Size: 4.2 KiB |
@ -0,0 +1,189 @@
|
|||||||
|
use std::{
|
||||||
|
fs::{File, OpenOptions},
|
||||||
|
io::{Read, Seek, SeekFrom},
|
||||||
|
path::PathBuf,
|
||||||
|
sync::mpsc::{channel, Receiver, Sender},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use notify::{event, Config, PollWatcher, RecursiveMode, Watcher, Event};
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
use std::os::windows::fs::OpenOptionsExt;
|
||||||
|
|
||||||
|
struct LogFileReader {
|
||||||
|
tx: Sender<String>,
|
||||||
|
file_path: PathBuf,
|
||||||
|
contents: Vec<u8>,
|
||||||
|
position: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for LogFileReader {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
log::debug!("LogFileReader dropped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn share_mode(options: &mut OpenOptions) {
|
||||||
|
options.share_mode(0x00000004 | 0x00000001 | 0x00000002);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
fn share_mode(options: &mut OpenOptions) {}
|
||||||
|
|
||||||
|
impl LogFileReader {
|
||||||
|
fn new(file: &PathBuf, tx: Sender<String>) -> Result<Self, std::io::Error> {
|
||||||
|
let mut lfr = LogFileReader {
|
||||||
|
tx,
|
||||||
|
file_path: file.to_path_buf(),
|
||||||
|
contents: Vec::new(),
|
||||||
|
position: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut options = OpenOptions::new();
|
||||||
|
|
||||||
|
options.read(true).write(false);
|
||||||
|
|
||||||
|
share_mode(&mut options);
|
||||||
|
|
||||||
|
let mut file = options.open(file)?;
|
||||||
|
lfr.position = file
|
||||||
|
.seek(SeekFrom::End(0))
|
||||||
|
.expect("Could not start at end of file");
|
||||||
|
|
||||||
|
Ok(lfr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_write_handler(&mut self) {
|
||||||
|
self.contents.truncate(0);
|
||||||
|
|
||||||
|
let mut options = OpenOptions::new();
|
||||||
|
|
||||||
|
options.read(true).write(false);
|
||||||
|
|
||||||
|
share_mode(&mut options);
|
||||||
|
|
||||||
|
let mut file = options
|
||||||
|
.open(&self.file_path)
|
||||||
|
.expect("Could not re-open file");
|
||||||
|
|
||||||
|
let _ = file.seek(SeekFrom::Start(self.position));
|
||||||
|
|
||||||
|
self.position += file.read_to_end(&mut self.contents).unwrap() as u64;
|
||||||
|
|
||||||
|
for line in String::from_utf8_lossy(&self.contents).lines() {
|
||||||
|
let line = line.to_string();
|
||||||
|
if !line.is_empty() {
|
||||||
|
self.tx.send(line).expect("Could not send line to receiver");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// do_process(contents)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn entered_strings_receiver(path: PathBuf) -> (Receiver<String>, PollWatcher) {
|
||||||
|
let (tx, rx) = channel();
|
||||||
|
let mut lfr = LogFileReader::new(&path, tx).unwrap();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(rx, watch_file_for_writes(path, Box::new(move || lfr.on_write_handler())))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const POE_STEAM_APP_ID: u32 = 238960;
|
||||||
|
pub fn get_poe_path() -> Option<PathBuf> {
|
||||||
|
if let Some(mut steam_dir) = steamlocate::SteamDir::locate() {
|
||||||
|
if let Some(app) = steam_dir.app(&POE_STEAM_APP_ID) {
|
||||||
|
let mut path = app.path.clone();
|
||||||
|
path.push("logs");
|
||||||
|
path.push("Client.txt");
|
||||||
|
return Some(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn entered_filtered_rx<'a>(rx: &'a Receiver<String>) -> impl Iterator<Item = String> + 'a {
|
||||||
|
rx.iter().filter_map(|str| {
|
||||||
|
if str.contains("You have entered ") {
|
||||||
|
Some(str)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator of strings with the id
|
||||||
|
pub fn area_filtered_rx<'a>(rx: &'a Receiver<String>) -> impl Iterator<Item = String> + 'a {
|
||||||
|
// Generating level 68 area "1_SideArea5_6" with seed 4103532853
|
||||||
|
let reg = Regex::new("Generating level ([0-9]+) area \"(.+)\"").unwrap();
|
||||||
|
let area_id_location = 2;
|
||||||
|
rx.try_iter().filter_map(move |str| {
|
||||||
|
let caps = reg.captures(&str);
|
||||||
|
if let Some(caps) = caps {
|
||||||
|
Some(caps[area_id_location].to_string())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn filter_func(str : String) -> Option<String> {
|
||||||
|
let reg = Regex::new("Generating level ([0-9]+) area \"(.+)\"").unwrap();
|
||||||
|
let area_id_location = 2;
|
||||||
|
|
||||||
|
let caps = reg.captures(&str);
|
||||||
|
if let Some(caps) = caps {
|
||||||
|
Some(caps[area_id_location].to_string())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blocking_area_filtered_rx<'a>(rx: &'a Receiver<String>) -> impl Iterator<Item = String> + 'a {
|
||||||
|
// Generating level 68 area "1_SideArea5_6" with seed 4103532853
|
||||||
|
|
||||||
|
rx.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn watch_file_for_writes(path: PathBuf, mut on_write_handler: Box<dyn FnMut() + Send>) -> PollWatcher
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
// Create a watcher object, delivering debounced events.
|
||||||
|
// The notification back-end is selected based on the platform.
|
||||||
|
let config = Config::default().with_poll_interval(Duration::from_millis(150));
|
||||||
|
let mut watcher = PollWatcher::new(
|
||||||
|
move |e: Result<Event, _>| match e {
|
||||||
|
Ok(e2) => {
|
||||||
|
match e2.kind {
|
||||||
|
|
||||||
|
notify::EventKind::Modify(_) => {
|
||||||
|
on_write_handler()
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
e => log::error!("Error watching for file something something: {e:?}"),
|
||||||
|
},
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
//let mut watcher = watcher(tx, Duration::from_millis(100)).unwrap();
|
||||||
|
|
||||||
|
// Add a path to be watched. All files and directories at that path and
|
||||||
|
// below will be monitored for changes.
|
||||||
|
watcher
|
||||||
|
.watch(path.as_path(), RecursiveMode::NonRecursive)
|
||||||
|
.unwrap();
|
||||||
|
watcher
|
||||||
|
}
|
@ -1,25 +1,23 @@
|
|||||||
|
|
||||||
import { Directive, EventEmitter, HostListener, Input, Output } from '@angular/core';
|
import { Directive, EventEmitter, HostListener, Output } from '@angular/core';
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[RecordKeyChord]'
|
selector: '[RecordKeyChord]',
|
||||||
|
standalone: true
|
||||||
})
|
})
|
||||||
export class RecordKeyChord {
|
export class RecordKeyChord {
|
||||||
@Output() finishedKeyChord = new EventEmitter<string[]>;
|
@Output() chord = new EventEmitter<string[]>;
|
||||||
|
|
||||||
chordStack: string[] = [];
|
chordStack: string[] = [];
|
||||||
|
|
||||||
@HostListener('window:keydown', ['$event'])
|
@HostListener('window:keydown', ['$event'])
|
||||||
handleKeyDown(event: KeyboardEvent) {
|
handleKeyDown(event: KeyboardEvent) {
|
||||||
console.log("keydown:", event);
|
|
||||||
this.chordStack.push(event.key);
|
this.chordStack.push(event.key);
|
||||||
|
this.chord.next(this.chordStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('window:keyup', ['$event'])
|
@HostListener('window:keyup', ['$event'])
|
||||||
handleKeyUp(event: KeyboardEvent) {
|
handleKeyUp(event: KeyboardEvent) {
|
||||||
console.log("keyup:", event);
|
|
||||||
this.finishedKeyChord.next(this.chordStack);
|
|
||||||
this.chordStack = [];
|
this.chordStack = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
<div mat-dialog-content>
|
||||||
|
<p>What's your favorite animal?</p>
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-label>data.title</mat-label>
|
||||||
|
<input matInput [(ngModel)]="data.chord" readonly RecordKeyChord (chord)="onChord($event)">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div mat-dialog-actions>
|
||||||
|
<button mat-button (click)="onNoClick()">Cancel</button>
|
||||||
|
<button mat-button [mat-dialog-close]="data.chord" cdkFocusInitial>Save</button>
|
||||||
|
</div>
|
Loading…
Reference in new issue