Обновить

Все потоки

Сначала показывать
Период

Dcnfk yt c njq yjub& Yt ,tlf! :vb СфзыДщсл и не придётся перепечатывать текст заново.

Ставим AutoHotKey - фреймворк для клавиатурных утилит. Потом пишем скрипт ruen.ahk:

#Requires AutoHotkey v2.0
#SingleInstance Force

; пробегаемся по всем клавишам, удаляем одинаковые в разных раскладках, экраниуем через `
en := "QWERTYUIOP{}|ASDFGHJKL:`"ZXCVBNM<>?~@#$^&qwertyuiop[]asdfghjkl;'zxcvbnm,./``"
ru := "ЙЦУКЕНГШЩЗХЪ/ФЫВАПРОЛДЖЭЯЧСМИТЬБЮ,Ё`"№;:?йцукенгшщзхъфывапролджэячсмитьбю.ё"

; собираем карты переключения в обе стороны
en2ru := Map()
for ch in StrSplit(en)
    en2ru[ ch ] := SubStr( ru, A_Index, 1 )
ru2en := Map()
for ch in StrSplit(ru)
    ru2en[ ch ] := SubStr( en, A_Index, 1 )

; общий код для всех хоткеев
Correct() {
    
    backup := ClipboardAll() ; сохраняем буфер, потом восстановим
    
    A_Clipboard := "" ; чистим буфер
    Send "#{Space}^c" ; Жмём Win+Space и Ctrl+C
    ClipWait 0.5 ; дожидаемся копирования в буфер
    
    if A_Clipboard { ; буфер не пустой
        
        result := ""
        mode := "en" ; по дефолту переводим в английский
        
        Loop Parse A_Clipboard { ; для каждого символа из буфера
            
            ; ищем варианты переключений для текущего симовола
            ru := en2ru.Get( A_LoopField, "" )
            en := ru2en.Get( A_LoopField, "" )
            
            if en && ru { ; возможны оба варианта - выбираем прошлый
                result .= mode = "en" ? en : ru
            } else if en {
                result .= en
                mode := "en"
            } else if ru {
                result .= ru
                mode := "ru"
            } else { ; оставляем как есть
                result .= A_LoopField
            }
            
        }
        
        A_Clipboard := result ; пишем в буфер
        Send "^v" ; жмём Ctrl+V
        Sleep 100 ; ждём пока данные заберут
        
    }
    
    A_Clipboard := backup ; восстанавливаем буфер
    
}

; CapsLock - хоткей коррекци выделения
CapsLock:: {
    Correct()
}

; Shift + CapsLock - хоткей коррекции последнего слова
+CapsLock:: {
    Send "^+{Left}" ; Жмём Ctrl+Shift+Left
    Correct()
}

; Ctrl + CapsLock - хоткей коррекции до конца строки
^CapsLock:: {
    Send "+{End}" ; Жмём Shift+End
    Correct()
}

; Ctrl + Shift + CapsLock - хоткей коррекции с начала строки
^+CapsLock:: {
    Send "+{Home}" ; Жмём Shift+Home
    Correct()
}

Даблкликаем по нему, и получаем:

  • переключение раскладки выделенного текста по CapsLock

  • последнего слова - по Shift + CapsLock

  • до конца строки - по Ctrl + CapsLock

  • от начала строки - по Ctrl + Shift + CapsLock

При желании его можно скомпилировать в exe-шник. Интерфейс самого AHK так и сделан.

Из уникальных фичей: переключает одновременно и раскладку введённого текста, и текущую раскладку клавиатуры.

Известные альтернативы:

  • Punto Switcher - 100 метровый звездолёт против 12 у AHK. Автопереключение часто не к месту, а горячую клавишу Break пойди найти на ноуте. Про интеграцию вашего буфра обмена с сервисами Яндекса я даже не заикаюсь.

  • Caramba Switcher - 60 метров и никаких настроек. Переключения по шифту - тот ещё глюкодром, с которым борятся через самообучение.

  • Mahou - поди найти клавиши Scroll и Pause.

  • Остальные лень ковырять - расскажите в комментах кому не влом.

Теги:
+5
Комментарии2

Глава Pocketpair (компании-создателя Palworld) Такуро Мидзобэ (Takuro Mizobe) сообщил о нестандартном требовании к кандидатам на должности геймдизайнеров в компании — соискатели должны быть активными пользователями платформы Steam с обширной игровой историей. На собеседовании кандидатов просят предоставить скриншоты своей игровой истории Steam и подробно проанализировать игры, в которых они провели больше всего времени. Студия ищет специалистов, которые понимают особенности жанра не только как разработчики, но и как заядлые игроки. Соискатель должен объяснить, почему были приняты те или иные дизайнерские решения в любимых играх и чем они отличаются от конкурентов в своём жанре.

Теги:
+2
Комментарии0

Открытый проект Mirror ищет скрытые процессы на macOS и следит за тем, чтобы никакой вредный код не потреблял память ПК. Умеет находить скрытые процессы, убивает процессы за один клик, включая даже «невидимые» процессы.

Теги:
+2
Комментарии3

Коллеги привет, искал себе решение как реагировать на изменения в объекте и нашел отличный сервис, который используется внутри директив таких как NgClass и NgStyle.

KeyValueDiffers позволяет создать KeyValueDiffer для сравнения изменений текущих пар ключ-значение с новыми. Если вы используете иммутабельные объекты, то можно просто обернуть все в эффект, ну а если вы наследники крутого легаси, где все объекты мутируются по ссылке, тогда проверку нужно вешать в DoCheck, чтобы реагировать на каждый тик change detection.

Накидал оба примера, чтобы поделиться с вами:

Иммутабельный с effect:


@Component({
  selector: 'app-test',
  template: ''
})
export class TestComponent {
  public state = input.required<Record<string, string | number>>();
  
  private differs = inject(KeyValueDiffers);
  private differ: KeyValueDiffer<string, string | number> | undefined;

  constructor() {
    effect(() => {
      const currentState = this.state();
      
      // создаем диффер, если он еще не создан
      if (!this.differ) {
        this.differ = this.differs.find(currentState).create();
      }

      // Эффект будет перезапускаться при изменении инпут-сигнала.
      const changes = this.differ.diff(currentState);

      // только если есть изменения
      if (changes) {
        changes.forEachAddedItem((record) => {
          console.log(`В объект добавлена запись: Ключ: ${record.key} | Значение: ${record.currentValue}`)
        });

        changes.forEachChangedItem((record) => {
          console.log(`Изменено: ${record.key} | Новое значение: ${record.currentValue}`)
        });

        changes.forEachRemovedItem((record) => {
          console.log(`Удалено: ${record.key}`)
        });
        
        // Остальные методы forEachItem и forEachPreviousItem по необходимости
      }
    })
  }
}

Легаси подход, которого, надеюсь, ни у кого нет, но на всякий случай :)

@Component({
  selector: 'app-legacy',
  template: ''
})
export class LegacyComponent implements OnInit, DoCheck {
  @Input({ required: true }) state!: Record<string, string | number>;
  
  private differs = inject(KeyValueDiffers);
  private differ: KeyValueDiffer<string, string | number> | undefined;

  ngOnInit() {
    // Создаем диффер при инициализации
    this.differ = this.differs.find(this.state).create();
  }

  // Запускается на каждый тик change detection, так как мутации по-другому не отследим.
  ngDoCheck(): void {
    const changes = this.differ?.diff(this.state);

    if (changes) {
      changes.forEachAddedItem((record) => {
        console.log(`В объект добавлена запись: Ключ: ${record.key} | Значение: ${record.currentValue}`)
      });

      changes.forEachChangedItem((record) => {
        console.log(`Значение изменилось: ${record.key}`)
      });

      changes.forEachRemovedItem((record) => {
        console.log(`Запись удалена: ${record.key}`)
      });

      // Остальные методы forEachItem и forEachPreviousItem по необходимости
    }
  }
}
Теги:
+1
Комментарии0