From 36fb4a48d4e84f72dfab50d8c05c89d2c2a5f7e3 Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Tue, 16 Jun 2026 22:09:38 +0300 Subject: [PATCH 01/26] support for invisibility and "virtual" items --- src/display/logical-line.lisp | 341 +++++++++++++++++++++++++-------- src/display/physical-line.lisp | 1 + 2 files changed, 257 insertions(+), 85 deletions(-) diff --git a/src/display/logical-line.lisp b/src/display/logical-line.lisp index 7545c6f50..7d3bd024a 100644 --- a/src/display/logical-line.lisp +++ b/src/display/logical-line.lisp @@ -2,9 +2,17 @@ (defvar *active-modes*) +(defstruct virtual-item + "a display-only string fragment injected at a character position within a logical line." + ;; 0-based position in the line's string where this fragment is inserted + charpos + string + attribute) + (defstruct logical-line string attributes + virtual-items left-content end-of-line-cursor-attribute extend-to-end @@ -77,7 +85,34 @@ over-attribute)))) (lem/buffer/line:normalization-elements merged-attributes))) +(defun splice-string (string attributes ov-start ov-end replacement replacement-attr) + "replace [OV-START, OV-END) in STRING with REPLACEMENT, adjusting ATTRIBUTES." + (let* ((rep-len (length replacement)) + (delta (- rep-len (- ov-end ov-start))) + (new-string (str:concat (subseq string 0 ov-start) + replacement + (subseq string ov-end))) + (pruned (lem/buffer/line:remove-elements attributes ov-start ov-end)) + (shifted (if (zerop delta) + pruned + (loop :for (start end attr) :in pruned + :collect (if (>= start ov-end) + (list (+ start delta) (+ end delta) attr) + (list start end attr))))) + (final (if (and replacement-attr (plusp rep-len)) + (lem/buffer/line:put-elements shifted ov-start (+ ov-start rep-len) replacement-attr) + shifted))) + (values new-string final))) + +(defun line-fully-invisible-p (point overlays) + "T if an :invisible overlay spans POINT's line without either endpoint on it." + (loop :for overlay :in overlays + :thereis (and (overlay-get overlay :invisible) + (not (same-line-p (overlay-start overlay) point)) + (not (same-line-p (overlay-end overlay) point))))) + (defun create-logical-line (point overlays active-modes) + "build a logical-line for POINT's line, or NIL if the line is entirely invisible." (flet ((overlay-start-charpos (overlay point) (if (same-line-p point (overlay-start overlay)) (point-charpos (overlay-start overlay)) @@ -85,67 +120,148 @@ (overlay-end-charpos (overlay point) (when (same-line-p point (overlay-end overlay)) (point-charpos (overlay-end overlay))))) - (let* ((end-of-line-cursor-attribute nil) - (extend-to-end-attribute nil) - (line-end-overlay nil) - (left-content - (compute-left-display-area-content active-modes - (point-buffer point) - point)) - (tab-width (variable-value 'tab-width :default point))) - (destructuring-bind (string . attributes) - (get-string-and-attributes-at-point point) - (loop :for overlay :in overlays - :when (overlay-within-point-p overlay point) - :do (cond ((typep overlay 'line-endings-overlay) - (when (same-line-p (overlay-end overlay) point) - (setf line-end-overlay overlay))) - ((typep overlay 'line-overlay) - (let ((attribute (overlay-attribute overlay))) + (let ((overlays (remove-if-not (lambda (ov) (overlay-within-point-p ov point)) + overlays))) + (when (line-fully-invisible-p point overlays) + (return-from create-logical-line nil)) + (let* ((end-of-line-cursor-attribute nil) + (extend-to-end-attribute nil) + (line-end-overlay nil) + (virtual-items) + (left-content + (compute-left-display-area-content active-modes + (point-buffer point) + point)) + (tab-width (variable-value 'tab-width :default point))) + (destructuring-bind (string . attributes) + (get-string-and-attributes-at-point point) + ;; collect string-splice operations from :invisible/:display overlays. + (let ((splice-ops)) + (loop :for overlay :in overlays + :for invisible := (overlay-get overlay :invisible) + :for display := (overlay-get overlay :display) + :do (when (or invisible display) + (let* ((ov-start (overlay-start-charpos overlay point)) + (ov-end (or (overlay-end-charpos overlay point) + (length string))) + (replacement + (cond + (display + (let ((d (alexandria:ensure-list display))) + (if (stringp (first d)) (first d) ""))) + ((eq invisible :ellipsis) "...") + (t ""))) + (repl-attr + (when (listp display) (second display)))) + (when (< ov-start ov-end) + (push (list ov-start ov-end replacement repl-attr) splice-ops))))) + ;; apply splices right-to-left for position stability + (when splice-ops + (dolist (op (sort splice-ops #'> :key #'first)) + (destructuring-bind (ov-start ov-end replacement repl-attr) op + (setf (values string attributes) + (splice-string string + attributes + ov-start + ov-end + replacement + repl-attr)))))) + ;; process all overlays for attributes (virtual text handled separately below). + (loop :for overlay :in overlays + :do (cond + ((typep overlay 'line-endings-overlay) + (when (same-line-p (overlay-end overlay) point) + (setf line-end-overlay overlay))) + ((typep overlay 'line-overlay) + (let ((attribute (overlay-attribute overlay))) + (setf attributes + (overlay-attributes attributes + 0 + (length string) + attribute)) + (setf extend-to-end-attribute attribute))) + ((typep overlay 'cursor-overlay) + (let* ((ov-start (overlay-start-charpos overlay point)) + (ov-end (1+ ov-start)) + (ov-attr (overlay-attribute overlay))) + (unless (cursor-overlay-fake-p overlay) + (set-cursor-attribute ov-attr)) + (if (<= (length string) ov-start) + (setf end-of-line-cursor-attribute ov-attr) + (setf attributes + (overlay-attributes attributes ov-start ov-end ov-attr))))) + (t + (let ((ov-start (overlay-start-charpos overlay point)) + (ov-end (overlay-end-charpos overlay point)) + (ov-attr (overlay-attribute overlay)) + (invisible (overlay-get overlay :invisible)) + (display (overlay-get overlay :display))) + ;; plain attribute (only when not replaced by invisible/display) + (when (and ov-attr (not invisible) (not display)) + (unless ov-end + (setf extend-to-end-attribute ov-attr)) (setf attributes (overlay-attributes attributes - 0 - (length string) - attribute)) - (setf extend-to-end-attribute attribute))) - ((typep overlay 'cursor-overlay) - (let* ((overlay-start-charpos (overlay-start-charpos overlay point)) - (overlay-end-charpos (1+ overlay-start-charpos)) - (overlay-attribute (overlay-attribute overlay))) - (unless (cursor-overlay-fake-p overlay) - (set-cursor-attribute overlay-attribute)) - (if (<= (length string) overlay-start-charpos) - (setf end-of-line-cursor-attribute overlay-attribute) - (setf attributes - (overlay-attributes - attributes - overlay-start-charpos - overlay-end-charpos - overlay-attribute))))) - (t - (let ((overlay-start-charpos (overlay-start-charpos overlay point)) - (overlay-end-charpos (overlay-end-charpos overlay point)) - (overlay-attribute (overlay-attribute overlay))) - (unless overlay-end-charpos - (setf extend-to-end-attribute - (overlay-attribute overlay))) - (setf attributes - (overlay-attributes - attributes - overlay-start-charpos - (or overlay-end-charpos (length string)) - overlay-attribute)))))) - (setf (values string attributes) (expand-tab string attributes tab-width)) - (let ((charpos (point-charpos point))) - (when (< 0 charpos) - (psetf string (subseq string charpos) - attributes (lem/buffer/line:subseq-elements attributes charpos (length string))))) - (make-logical-line :string string - :attributes attributes - :left-content left-content - :extend-to-end extend-to-end-attribute - :end-of-line-cursor-attribute end-of-line-cursor-attribute - :line-end-overlay line-end-overlay))))) + ov-start + (or ov-end (length string)) + ov-attr))))))) + ;; virtual text from :before-string/:after-string overlays. emit each overlay's + ;; :before then :after, visiting overlays in (end, start) order: at any shared + ;; charpos an overlay closing there (smaller end) is emitted before one opening + ;; there, so trailing :after-strings precede leading :before-strings, but a + ;; zero-length overlay's own pair stays adjacent. a final stable sort by charpos + ;; groups them without affecting this order. + (loop :for overlay :in (stable-sort + (loop :for overlay :in overlays + :when (or (overlay-get overlay :before-string) + (overlay-get overlay :after-string)) + :collect overlay) + (lambda (a b) + (let ((a-end (or (overlay-end-charpos a point) (length string))) + (b-end (or (overlay-end-charpos b point) (length string)))) + (if (= a-end b-end) + (< (overlay-start-charpos a point) + (overlay-start-charpos b point)) + (< a-end b-end))))) + :for before-str := (overlay-get overlay :before-string) + :for after-str := (overlay-get overlay :after-string) + :do (when (and before-str (same-line-p (overlay-start overlay) point)) + (let ((bs (alexandria:ensure-list before-str))) + (push (make-virtual-item :charpos (overlay-start-charpos overlay point) + :string (first bs) + :attribute (second bs)) + virtual-items))) + (when (and after-str (same-line-p (overlay-end overlay) point)) + (let ((as (alexandria:ensure-list after-str))) + (push (make-virtual-item :charpos (or (overlay-end-charpos overlay point) + (length string)) + :string (first as) + :attribute (second as)) + virtual-items)))) + (setf virtual-items + (stable-sort (nreverse virtual-items) #'< :key #'virtual-item-charpos)) + (setf (values string attributes) (expand-tab string attributes tab-width)) + (let ((charpos (point-charpos point))) + (when (< 0 charpos) + (psetf string (subseq string charpos) + attributes (lem/buffer/line:subseq-elements + attributes charpos (length string))) + ;; adjust virtual-item positions for the charpos clip (order preserved) + (setf virtual-items + (loop :for vi :in virtual-items + :when (>= (virtual-item-charpos vi) charpos) + :collect (make-virtual-item + :charpos (- (virtual-item-charpos vi) charpos) + :string (virtual-item-string vi) + :attribute (virtual-item-attribute vi)))))) + (make-logical-line + :string string + :attributes attributes + :virtual-items virtual-items + :left-content left-content + :extend-to-end extend-to-end-attribute + :end-of-line-cursor-attribute end-of-line-cursor-attribute + :line-end-overlay line-end-overlay)))))) (defstruct string-with-attribute-item string @@ -191,41 +307,95 @@ (defmethod item-attribute ((item extend-to-eol-item)) nil) +(defun add-or-merge-item (item items) + "add ITEM to the front of ITEMS, or merge it into the previous +string-with-attribute-item when both carry the same attribute. returns the updated list." + (let ((last-item (first items))) + (if (and (string-with-attribute-item-p last-item) + (string-with-attribute-item-p item) + (equal (string-with-attribute-item-attribute last-item) + (string-with-attribute-item-attribute item))) + (progn + (setf (string-with-attribute-item-string last-item) + (str:concat (string-with-attribute-item-string last-item) + (string-with-attribute-item-string item))) + items) + (cons item items)))) + (defun compute-items-from-string-and-attributes (string attributes) (handler-case (let ((items '())) - (flet ((add (item) - (if (null items) - (push item items) - (let ((last-item (first items))) - (if (and (string-with-attribute-item-p last-item) - (string-with-attribute-item-p item) - (equal (string-with-attribute-item-attribute last-item) - (string-with-attribute-item-attribute item))) - (setf (string-with-attribute-item-string (first items)) - (str:concat (string-with-attribute-item-string last-item) - (string-with-attribute-item-string item))) - (push item items)))))) - (loop :for last-pos := 0 :then end - :for (start end attribute) :in attributes - :do (unless (= last-pos start) - (add (make-string-with-attribute-item :string (subseq string last-pos start)))) - (add (if (cursor-attribute-p attribute) - (make-cursor-item :string (subseq string start end) :attribute attribute) - (make-string-with-attribute-item - :string (subseq string start end) - :attribute attribute))) - :finally (push (make-string-with-attribute-item :string (subseq string last-pos)) - items))) + (loop :for last-pos := 0 :then end + :for (start end attribute) :in attributes + :do (unless (= last-pos start) + (setf items (add-or-merge-item + (make-string-with-attribute-item :string (subseq string last-pos start)) + items))) + (setf items (add-or-merge-item + (if (cursor-attribute-p attribute) + (make-cursor-item :string (subseq string start end) :attribute attribute) + (make-string-with-attribute-item + :string (subseq string start end) + :attribute attribute)) + items)) + :finally (push (make-string-with-attribute-item :string (subseq string last-pos)) + items)) items) (error (e) (log:error e string attributes) nil))) +(defun inject-virtual-items (string attributes virtual-items) + "produce items from STRING and ATTRIBUTES, injecting VIRTUAL-ITEMS at their charposes. +VIRTUAL-ITEMS arrive in draw order (from `create-logical-line')." + (let* (;; all positions where we may need to split: attribute boundaries, virtual charposes. + (positions + (sort (remove-duplicates + (nconc (list 0 (length string)) + (mapcar #'virtual-item-charpos virtual-items) + (mapcan (lambda (span) (list (first span) (second span))) + attributes))) + #'<)) + ;; VIRTUAL-ITEMS are already sorted by charpos in draw order + (pending virtual-items) + (items)) + (flet ((add-virtuals-at (pos) + (loop :while (and pending (= (virtual-item-charpos (first pending)) pos)) + :do (let ((vi (pop pending))) + (setf items (add-or-merge-item + (make-string-with-attribute-item + :string (virtual-item-string vi) + :attribute (virtual-item-attribute vi)) + items)))))) + ;; walk segments between break positions, injecting virtual items at each boundary + (loop :for (pos . rest) :on positions + :while rest + :for next-pos := (first rest) + :do (add-virtuals-at pos) + (unless (= pos next-pos) + (let* ((seg (subseq string pos next-pos)) + (attr (loop :for (start end attribute) :in attributes + :when (and (<= start pos) (>= end next-pos)) + :return attribute))) + (unless (string= seg "") + (setf items (add-or-merge-item + (if (cursor-attribute-p attr) + (make-cursor-item :string seg :attribute attr) + (make-string-with-attribute-item :string seg + :attribute attr)) + items)))))) + ;; virtual items at the very end of the string + (add-virtuals-at (length string))) + items)) + (defun compute-items-from-logical-line (logical-line) (let ((items - (compute-items-from-string-and-attributes (logical-line-string logical-line) - (logical-line-attributes logical-line)))) + (if (logical-line-virtual-items logical-line) + (inject-virtual-items (logical-line-string logical-line) + (logical-line-attributes logical-line) + (logical-line-virtual-items logical-line)) + (compute-items-from-string-and-attributes (logical-line-string logical-line) + (logical-line-attributes logical-line))))) (alexandria:when-let (attribute (logical-line-extend-to-end logical-line)) (push (make-extend-to-eol-item :color (attribute-background-color attribute)) @@ -290,7 +460,8 @@ (active-modes (get-active-modes-class-instance (window-buffer window))) (*active-modes* active-modes)) (loop :for logical-line := (create-logical-line point overlays active-modes) - :do (funcall function logical-line) + :do (when logical-line + (funcall function logical-line)) (unless (line-offset point 1) (return)))))) diff --git a/src/display/physical-line.lisp b/src/display/physical-line.lisp index add39e7a5..9fb31f600 100644 --- a/src/display/physical-line.lisp +++ b/src/display/physical-line.lisp @@ -536,6 +536,7 @@ over the top-level spine and tolerant of improper (dotted) lists." (logical-line-end-of-line-cursor-attribute logical-line) (logical-line-extend-to-end logical-line) (logical-line-line-end-overlay logical-line) + (logical-line-virtual-items logical-line) scroll-start left-side-width)) From 535ef07dfc2557cf2890e6c164a4726bbad21a77 Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Fri, 19 Jun 2026 01:29:38 +0300 Subject: [PATCH 02/26] make previous-line/next-line skip hidden lines --- lem.asd | 14 ++++++------ src/commands/move.lisp | 44 ++++++++++++++++++++++++++++++++++++-- src/internal-packages.lisp | 7 ++++-- 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/lem.asd b/lem.asd index 1ddff52c0..3bcd3daf2 100644 --- a/lem.asd +++ b/lem.asd @@ -149,6 +149,13 @@ (:file "color-theme") + (:module "display" + :serial t + :components ((:file "base") + (:file "char-type") + (:file "logical-line") + (:file "physical-line"))) + (:module "commands" :serial t :components ((:file "move") @@ -168,13 +175,6 @@ (:file "frame") #+sbcl (:file "sprof"))) - (:module "display" - :serial t - :components ((:file "base") - (:file "char-type") - (:file "logical-line") - (:file "physical-line"))) - (:file "external-packages") (:module "ext" diff --git a/src/commands/move.lisp b/src/commands/move.lisp index 35011dadd..c7db8c360 100644 --- a/src/commands/move.lisp +++ b/src/commands/move.lisp @@ -45,6 +45,46 @@ (define-key *global-keymap* "C-x [" 'previous-page-char) (define-key *global-keymap* "M-g" 'goto-line) +(defun line-invisible-p (point) + "T if POINT's line is fully hidden by an :invisible overlay." + (let ((overlays (remove-if-not + (lambda (ov) + (overlay-within-point-p ov point)) + (buffer-overlays (point-buffer point))))) + (line-fully-invisible-p point overlays))) + +(defun skip-invisible-lines (point direction) + "move POINT past any fully invisible lines in DIRECTION (1 or -1). +returns POINT on success, or NIL if a buffer boundary is reached." + (loop :while (line-invisible-p point) + :do (unless (line-offset point direction) + (return-from skip-invisible-lines nil))) + point) + +(defun move-to-next-visible-virtual-line (point n) + "like `move-to-next-virtual-line' but skips fully invisible lines." + (let ((dir (if (plusp n) 1 -1)) + (steps (abs n))) + (loop :repeat steps + :do (unless (move-to-next-virtual-line point dir) + (return-from move-to-next-visible-virtual-line nil)) + (when (line-invisible-p point) + (unless (skip-invisible-lines point dir) + (return-from move-to-next-visible-virtual-line nil)))) + point)) + +(defun visible-line-offset (point n) + "like `line-offset' but skips fully invisible lines." + (let ((dir (if (plusp n) 1 -1)) + (steps (abs n))) + (loop :repeat steps + :do (unless (line-offset point dir) + (return-from visible-line-offset nil)) + (when (line-invisible-p point) + (unless (skip-invisible-lines point dir) + (return-from visible-line-offset nil)))) + point)) + (defun next-line-aux (n point-column-fn forward-line-fn @@ -67,14 +107,14 @@ "Move the cursor to next line." (next-line-aux n #'point-virtual-line-column - #'move-to-next-virtual-line + #'move-to-next-visible-virtual-line #'move-to-virtual-line-column)) (define-command (next-logical-line (:advice-classes movable-advice)) (&optional n) (:universal) "Move the cursor to the next logical line." (next-line-aux n #'point-column - #'line-offset + #'visible-line-offset #'move-to-column)) (define-command (previous-line (:advice-classes movable-advice)) (&optional (n 1)) (:universal) diff --git a/src/internal-packages.lisp b/src/internal-packages.lisp index 6dfa60cc2..92f4329b6 100644 --- a/src/internal-packages.lisp +++ b/src/internal-packages.lisp @@ -580,7 +580,9 @@ :overlay-put :overlay-get :clear-overlays - :point-overlays) + :point-overlays + :buffer-overlays + :overlay-within-point-p) ;; streams.lisp (:export :buffer-input-stream @@ -659,7 +661,8 @@ :compute-wrap-left-area-content) ;; display/logical-line.lisp (:export - :make-region-overlays-using-global-mode) + :make-region-overlays-using-global-mode + :line-fully-invisible-p) ;; interface.lisp (:export :with-implementation From ec4179741674c9a01ddba1913337f89008b091f0 Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Fri, 19 Jun 2026 23:33:40 +0300 Subject: [PATCH 03/26] add generic defun-folding and bind it to tab --- src/ext/language-mode.lisp | 70 +++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/src/ext/language-mode.lisp b/src/ext/language-mode.lisp index 573a6bd11..f04652f3a 100644 --- a/src/ext/language-mode.lisp +++ b/src/ext/language-mode.lisp @@ -6,6 +6,9 @@ :idle-function :beginning-of-defun-function :end-of-defun-function + :fold-region-function + :fold-toggle-at-point + :unfold-all :comment-region :uncomment-region :comment-or-uncomment-region @@ -51,6 +54,8 @@ (define-editor-variable idle-function nil) (define-editor-variable beginning-of-defun-function nil) (define-editor-variable end-of-defun-function nil) +(define-editor-variable fold-region-function 'fold-region-default + "function of one point returning (values start end) for the foldable region at point, or NIL.") (define-editor-variable line-comment nil) (define-editor-variable insertion-line-comment nil) (define-editor-variable find-definitions-function nil) @@ -61,6 +66,9 @@ (define-editor-variable root-uri-patterns '()) (define-editor-variable detective-search nil) +(define-attribute fold-attribute + (t :foreground :base04)) + (defun prompt-for-symbol (prompt history-name) (prompt-for-string prompt :history-symbol history-name)) @@ -86,7 +94,7 @@ (define-key *language-mode-keymap* "C-M-a" 'beginning-of-defun) (define-key *language-mode-keymap* "C-M-e" 'end-of-defun) -(define-key *language-mode-keymap* "Tab" 'indent-line-and-complete-symbol) +(define-key *language-mode-keymap* "Tab" 'fold-or-indent-or-complete) (define-key *global-keymap* "C-j" 'newline-and-indent) (define-key *global-keymap* "M-j" 'newline-and-indent) (define-key *language-mode-keymap* "C-M-\\" 'indent-region) @@ -115,6 +123,66 @@ (funcall fn (current-point) n) (beginning-of-defun-1 (- n))))) +(defun fold-region-default (point) + "return (values start end) spanning the defun at POINT, or NIL." + (let ((defun-begin (variable-value 'beginning-of-defun-function :buffer point)) + (defun-end (variable-value 'end-of-defun-function :buffer point))) + (when (and defun-begin defun-end) + (let ((start (copy-point point :temporary)) + (end (copy-point point :temporary))) + (funcall defun-end end 1) + (move-point start end) + (funcall defun-begin start 1) + (when (point< start end) + (values start end)))))) + +(defun fold-region (start end &optional (fold-marker "...")) + "hide the lines of the region [START, END), leaving START's line visible with a fold marker. +returns the fold overlay." + (with-point ((s start)) + (line-end s) + (let ((overlay (make-overlay s end 'fold-attribute))) + (overlay-put overlay :invisible t) + (overlay-put overlay :fold t) + (overlay-put overlay :before-string (list fold-marker 'fold-attribute)) + overlay))) + +(defun fold-overlay-at (point) + "the fold overlay whose header line is POINT's line, or NIL." + (find-if + (lambda (overlay) + (and (overlay-get overlay :fold) + (same-line-p (overlay-start overlay) point))) + (buffer-overlays (point-buffer point)))) + +(defun fold-defun-at (point) + "fold the defun at POINT. Returns T when something was folded." + (let ((fn (variable-value 'fold-region-function :default point))) + (multiple-value-bind (start end) (funcall fn point) + (when (and start end (not (same-line-p start end))) + (fold-region start end) + (move-point point start) + t)))) + +(defun fold-toggle-at-point (&optional (point (current-point))) + "toggle the fold at POINT. returns T when a fold was added or removed, and NIL when there was +nothing to fold." + (let ((fold (fold-overlay-at point))) + (cond (fold (delete-overlay fold) t) + ((fold-defun-at point) t) + (t nil)))) + +(define-command fold-or-indent-or-complete () () + "fold or unfold the defun at point. otherwise indent and complete the symbol." + (unless (fold-toggle-at-point) + (indent-line-and-complete-symbol))) + +(define-command unfold-all () () + "remove every fold in the current buffer." + (dolist (overlay (copy-list (buffer-overlays))) + (when (overlay-get overlay :fold) + (delete-overlay overlay)))) + (define-command (indent (:advice-classes editable-advice)) (&optional (n 1)) (:universal) (if (variable-value 'calc-indent-function) (indent-line (current-point)) From 8570dbb8365ed6f800385a75caef17249ca4aacd Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Sat, 27 Jun 2026 12:16:51 +0300 Subject: [PATCH 04/26] improve invisiblity behavior --- extensions/vi-mode/commands.lisp | 19 +- extensions/vi-mode/commands/utils.lisp | 5 +- lem-tests.asd | 3 +- src/commands/move.lisp | 68 ++++- src/display/logical-line.lisp | 392 +++++++++++++++---------- src/ext/language-mode.lisp | 9 +- src/internal-packages.lisp | 3 +- 7 files changed, 326 insertions(+), 173 deletions(-) diff --git a/extensions/vi-mode/commands.lisp b/extensions/vi-mode/commands.lisp index f8d8e2005..dcf4dafd8 100644 --- a/extensions/vi-mode/commands.lisp +++ b/extensions/vi-mode/commands.lisp @@ -198,16 +198,19 @@ (define-command vi-forward-char (&optional (n 1)) (:universal) (let* ((p (current-point)) - (max-offset (- (length (line-string p)) - (point-charpos p)))) + (max-offset (with-point ((e p)) + (visual-line-end e) + (count-characters p e)))) (character-offset p (min n max-offset)))) (define-command vi-backward-char (&optional (n 1)) (:universal) (let ((p (current-point))) - (dotimes (_ n) - (if (bolp p) - (return) - (character-offset p -1))))) + (with-point ((bol p)) + (visual-line-beginning bol) + (dotimes (_ n) + (if (point<= p bol) + (return) + (character-offset p -1)))))) (define-motion vi-next-line (&optional (n 1)) (:universal) (:type :line) @@ -309,7 +312,7 @@ (define-command vi-move-to-beginning-of-line () () (with-point ((start (current-point))) - (line-start start) + (visual-line-beginning start) (or (text-property-at (current-point) :field -1) (previous-single-property-change (current-point) :field @@ -318,7 +321,7 @@ (define-command vi-move-to-end-of-line (&optional (n 1)) (:universal) (vi-line n) - (line-end (current-point))) + (visual-line-end (current-point))) (define-command vi-move-to-last-nonblank () () (vi-move-to-end-of-line) diff --git a/extensions/vi-mode/commands/utils.lisp b/extensions/vi-mode/commands/utils.lisp index fa883655e..4018243c1 100644 --- a/extensions/vi-mode/commands/utils.lisp +++ b/extensions/vi-mode/commands/utils.lisp @@ -61,7 +61,10 @@ (1- len))))) (defun fall-within-line (point) - (when (eolp point) + "clamps the given point to the character before the newline at the end of the line its in." + ;; a buffer-line end whose newline is hidden by a fold is mid-visual-line, so we dont clamp. + (when (and (eolp point) + (not (invisible-overlay-covering point))) (line-end point) (unless (bolp point) (character-offset point *cursor-offset*)))) diff --git a/lem-tests.asd b/lem-tests.asd index c249c2bea..1c4857789 100644 --- a/lem-tests.asd +++ b/lem-tests.asd @@ -79,6 +79,7 @@ (:file "filer") (:file "listener-mode") (:file "interface") - (:file "display-cache")) + (:file "display-cache") + (:file "visual-line")) :perform (test-op (o c) (symbol-call :rove :run c))) diff --git a/src/commands/move.lisp b/src/commands/move.lisp index c7db8c360..385d99079 100644 --- a/src/commands/move.lisp +++ b/src/commands/move.lisp @@ -12,6 +12,8 @@ :move-to-beginning-of-logical-line :move-to-end-of-line :move-to-end-of-logical-line + :visual-line-beginning + :visual-line-end :next-page :previous-page :next-page-char @@ -45,18 +47,64 @@ (define-key *global-keymap* "C-x [" 'previous-page-char) (define-key *global-keymap* "M-g" 'goto-line) -(defun line-invisible-p (point) - "T if POINT's line is fully hidden by an :invisible overlay." - (let ((overlays (remove-if-not - (lambda (ov) - (overlay-within-point-p ov point)) - (buffer-overlays (point-buffer point))))) - (line-fully-invisible-p point overlays))) +(defvar *cursor-positions-before-command* + (make-hash-table :test 'eq) + "maps each cursor point to its absolute buffer position before the running +command, so `snap-cursors-out-of-invisible' can tell which direction it moved.") + +(add-hook *pre-command-hook* 'record-cursor-positions) +(add-hook *post-command-hook* 'snap-cursors-out-of-invisible) + +(defun record-cursor-positions () + "snapshot every cursor's position so a later snap can pick the visible edge on the far side +of a fold the cursor moves into." + (clrhash *cursor-positions-before-command*) + (dolist (point (buffer-cursors (current-buffer))) + (setf (gethash point *cursor-positions-before-command*) + (position-at-point point)))) + +(defun snap-cursors-out-of-invisible () + "a fold collapses its range, so no cursor may rest within it or at its start. move any such +cursor to the nearest visible position in its direction of travel, so the cursor never appears +stuck on hidden text." + (dolist (point (buffer-cursors (current-buffer))) + (let ((overlay (invisible-overlay-covering point))) + (when overlay + (let ((forwardp (let ((before (gethash point *cursor-positions-before-command*))) + (or (null before) + (<= before (position-at-point (overlay-start overlay))))))) + (loop :for ov := (invisible-overlay-covering point) + :while ov + :do (if forwardp + (move-point point (overlay-end ov)) + (progn + (move-point point (overlay-start ov)) + ;; dont continue looping if we are at the beginning of the buffer. + ;; it could cause an endless loop. + (unless (character-offset point -1) + (return)))))))))) + +(defun visual-line-end (point) + "move POINT to the end of its visual line. returns POINT." + (line-end point) + (loop :until (last-line-p point) + :while (invisible-overlay-covering point) + :do (line-offset point 1) + (line-end point)) + point) + +(defun visual-line-beginning (point) + "move POINT to the start of its visual line. returns POINT." + (line-start point) + (loop :while (line-continuation-p point) + :do (line-offset point -1) + (line-start point)) + point) (defun skip-invisible-lines (point direction) "move POINT past any fully invisible lines in DIRECTION (1 or -1). returns POINT on success, or NIL if a buffer boundary is reached." - (loop :while (line-invisible-p point) + (loop :while (line-continuation-p point) :do (unless (line-offset point direction) (return-from skip-invisible-lines nil))) point) @@ -68,7 +116,7 @@ returns POINT on success, or NIL if a buffer boundary is reached." (loop :repeat steps :do (unless (move-to-next-virtual-line point dir) (return-from move-to-next-visible-virtual-line nil)) - (when (line-invisible-p point) + (when (line-continuation-p point) (unless (skip-invisible-lines point dir) (return-from move-to-next-visible-virtual-line nil)))) point)) @@ -80,7 +128,7 @@ returns POINT on success, or NIL if a buffer boundary is reached." (loop :repeat steps :do (unless (line-offset point dir) (return-from visible-line-offset nil)) - (when (line-invisible-p point) + (when (line-continuation-p point) (unless (skip-invisible-lines point dir) (return-from visible-line-offset nil)))) point)) diff --git a/src/display/logical-line.lisp b/src/display/logical-line.lisp index 7d3bd024a..5b5f94d04 100644 --- a/src/display/logical-line.lisp +++ b/src/display/logical-line.lisp @@ -104,6 +104,20 @@ shifted))) (values new-string final))) +(defun adjust-charpos-for-splices (charpos splice-ops) + "map a raw-string CHARPOS to its position after SPLICE-OPS are applied. +SPLICE-OPS is a list of (START END REPLACEMENT . _) covering disjoint ranges, as collected +in `create-logical-line'. used to keep virtual-item markers anchored when several folds on one +visual line each splice text out." + (+ charpos + (loop :for (start end replacement) :in splice-ops + :sum (cond ((<= charpos start) + 0) + ((>= charpos end) + (- (length replacement) (- end start))) + (t + (- start charpos)))))) + (defun line-fully-invisible-p (point overlays) "T if an :invisible overlay spans POINT's line without either endpoint on it." (loop :for overlay :in overlays @@ -111,157 +125,232 @@ (not (same-line-p (overlay-start overlay) point)) (not (same-line-p (overlay-end overlay) point))))) +(defun invisible-overlay-covering (point &optional (overlays (buffer-overlays (point-buffer point)))) + "return the :invisible overlay covering POINT." + (loop :for overlay :in overlays + :thereis (and (overlay-get overlay :invisible) + (point<= (overlay-start overlay) point) + (point< point (overlay-end overlay)) + overlay))) + +(defun line-continuation-p (point) + "whether POINT's line continues a previous visual line. meaning the newline preceding it is +hidden by an :invisible overlay, so the line is not a visual line of its own. +a folded region may hide arbitrary character ranges, including the newlines that join several +buffer lines into one displayed line." + (and (not (first-line-p point)) + (with-point ((p point)) + (line-start p) + (character-offset p -1) + (invisible-overlay-covering p)))) + +(defun collect-visual-line-string-and-attributes (vstart vend) + (with-point ((p vstart)) + (let ((out (make-string-output-stream)) + (attributes) + (base 0)) + (loop + (destructuring-bind (string . attrs) (get-string-and-attributes-at-point p) + (write-string string out) + (loop :for (s e attr) :in attrs + :do (push (list (+ base s) (+ base e) attr) attributes)) + (incf base (length string)) + (when (same-line-p p vend) + (return)) + (write-char #\newline out) + (incf base) + (line-offset p 1))) + (values (get-output-stream-string out) + (nreverse attributes))))) + (defun create-logical-line (point overlays active-modes) - "build a logical-line for POINT's line, or NIL if the line is entirely invisible." - (flet ((overlay-start-charpos (overlay point) - (if (same-line-p point (overlay-start overlay)) - (point-charpos (overlay-start overlay)) - 0)) - (overlay-end-charpos (overlay point) - (when (same-line-p point (overlay-end overlay)) - (point-charpos (overlay-end overlay))))) - (let ((overlays (remove-if-not (lambda (ov) (overlay-within-point-p ov point)) - overlays))) - (when (line-fully-invisible-p point overlays) - (return-from create-logical-line nil)) - (let* ((end-of-line-cursor-attribute nil) - (extend-to-end-attribute nil) - (line-end-overlay nil) - (virtual-items) - (left-content - (compute-left-display-area-content active-modes - (point-buffer point) - point)) - (tab-width (variable-value 'tab-width :default point))) - (destructuring-bind (string . attributes) - (get-string-and-attributes-at-point point) - ;; collect string-splice operations from :invisible/:display overlays. - (let ((splice-ops)) - (loop :for overlay :in overlays - :for invisible := (overlay-get overlay :invisible) - :for display := (overlay-get overlay :display) - :do (when (or invisible display) - (let* ((ov-start (overlay-start-charpos overlay point)) - (ov-end (or (overlay-end-charpos overlay point) - (length string))) - (replacement - (cond - (display - (let ((d (alexandria:ensure-list display))) - (if (stringp (first d)) (first d) ""))) - ((eq invisible :ellipsis) "...") - (t ""))) - (repl-attr - (when (listp display) (second display)))) - (when (< ov-start ov-end) - (push (list ov-start ov-end replacement repl-attr) splice-ops))))) - ;; apply splices right-to-left for position stability - (when splice-ops - (dolist (op (sort splice-ops #'> :key #'first)) - (destructuring-bind (ov-start ov-end replacement repl-attr) op - (setf (values string attributes) - (splice-string string - attributes - ov-start - ov-end - replacement - repl-attr)))))) - ;; process all overlays for attributes (virtual text handled separately below). - (loop :for overlay :in overlays - :do (cond - ((typep overlay 'line-endings-overlay) - (when (same-line-p (overlay-end overlay) point) - (setf line-end-overlay overlay))) - ((typep overlay 'line-overlay) - (let ((attribute (overlay-attribute overlay))) - (setf attributes - (overlay-attributes attributes - 0 - (length string) - attribute)) - (setf extend-to-end-attribute attribute))) - ((typep overlay 'cursor-overlay) - (let* ((ov-start (overlay-start-charpos overlay point)) - (ov-end (1+ ov-start)) - (ov-attr (overlay-attribute overlay))) - (unless (cursor-overlay-fake-p overlay) - (set-cursor-attribute ov-attr)) - (if (<= (length string) ov-start) - (setf end-of-line-cursor-attribute ov-attr) + "build a logical-line for the visual line starting at POINT, joining any following buffer lines +whose preceding newline is hidden by an :invisible overlay. a single displayed line may contain +several folds that each hide arbitrary character ranges across multiple buffer lines." + (let ((invisible-overlays + (remove-if-not (lambda (ov) (overlay-get ov :invisible)) overlays))) + (with-point ((vstart point) + (vend point)) + (line-start vstart) + (line-end vend) + ;; extend VEND across every newline hidden by an invisible overlay so the + ;; visual line reaches the next *visible* newline (or the buffer end). + (loop :until (last-line-p vend) + :while (invisible-overlay-covering vend invisible-overlays) + :do (line-offset vend 1) + (line-end vend)) + (let ((overlays (remove-if-not + (lambda (ov) + (and (point<= (overlay-start ov) vend) + (point<= vstart (overlay-end ov)))) + overlays))) + (flet ((overlay-start-charpos (overlay) + ;; column where the overlay starts on this visual line, clamped to + ;; 0 when it begins before VSTART. + (let ((s (overlay-start overlay))) + (if (point<= vstart s) + (count-characters vstart s) + 0))) + (overlay-end-charpos (overlay) + ;; column where the overlay ends, or NIL when it extends past VEND. + (let ((e (overlay-end overlay))) + (when (point<= e vend) + (count-characters vstart e)))) + (start-in-line-p (overlay) + ;; true when the overlay's start falls within this visual line. + (point<= vstart (overlay-start overlay))) + (end-in-line-p (overlay) + ;; true when the overlay's end falls within this visual line. + (point<= (overlay-end overlay) vend))) + (let* ((end-of-line-cursor-attribute nil) + (extend-to-end-attribute nil) + (line-end-overlay nil) + (virtual-items) + (splice-ops) + (left-content + (compute-left-display-area-content active-modes + (point-buffer point) + point)) + (tab-width (variable-value 'tab-width :default point))) + (multiple-value-bind (string attributes) + (collect-visual-line-string-and-attributes vstart vend) + ;; collect string-splice operations from :invisible/:display overlays. + (loop :for overlay :in overlays + :for invisible := (overlay-get overlay :invisible) + :for display := (overlay-get overlay :display) + :do (when (or invisible display) + (let* ((ov-start (overlay-start-charpos overlay)) + (ov-end (or (overlay-end-charpos overlay) + (length string))) + (replacement + (cond + (display + (let ((d (alexandria:ensure-list display))) + (if (stringp (first d)) (first d) ""))) + ((eq invisible :ellipsis) "...") + (t ""))) + (repl-attr + (when (listp display) (second display)))) + (when (< ov-start ov-end) + (push (list ov-start ov-end replacement repl-attr) splice-ops))))) + ;; apply splices right-to-left for position stability + (when splice-ops + (dolist (op (sort (copy-list splice-ops) #'> :key #'first)) + (destructuring-bind (ov-start ov-end replacement repl-attr) op + (setf (values string attributes) + (splice-string string + attributes + ov-start + ov-end + replacement + repl-attr))))) + ;; process all overlays for attributes (virtual text handled separately below). + (loop :for overlay :in overlays + :do (cond + ((typep overlay 'line-endings-overlay) + (when (end-in-line-p overlay) + (setf line-end-overlay overlay))) + ((typep overlay 'line-overlay) + (let ((attribute (overlay-attribute overlay))) (setf attributes - (overlay-attributes attributes ov-start ov-end ov-attr))))) - (t - (let ((ov-start (overlay-start-charpos overlay point)) - (ov-end (overlay-end-charpos overlay point)) - (ov-attr (overlay-attribute overlay)) - (invisible (overlay-get overlay :invisible)) - (display (overlay-get overlay :display))) - ;; plain attribute (only when not replaced by invisible/display) - (when (and ov-attr (not invisible) (not display)) - (unless ov-end - (setf extend-to-end-attribute ov-attr)) - (setf attributes - (overlay-attributes attributes - ov-start - (or ov-end (length string)) - ov-attr))))))) - ;; virtual text from :before-string/:after-string overlays. emit each overlay's - ;; :before then :after, visiting overlays in (end, start) order: at any shared - ;; charpos an overlay closing there (smaller end) is emitted before one opening - ;; there, so trailing :after-strings precede leading :before-strings, but a - ;; zero-length overlay's own pair stays adjacent. a final stable sort by charpos - ;; groups them without affecting this order. - (loop :for overlay :in (stable-sort - (loop :for overlay :in overlays - :when (or (overlay-get overlay :before-string) - (overlay-get overlay :after-string)) - :collect overlay) - (lambda (a b) - (let ((a-end (or (overlay-end-charpos a point) (length string))) - (b-end (or (overlay-end-charpos b point) (length string)))) - (if (= a-end b-end) - (< (overlay-start-charpos a point) - (overlay-start-charpos b point)) - (< a-end b-end))))) - :for before-str := (overlay-get overlay :before-string) - :for after-str := (overlay-get overlay :after-string) - :do (when (and before-str (same-line-p (overlay-start overlay) point)) - (let ((bs (alexandria:ensure-list before-str))) - (push (make-virtual-item :charpos (overlay-start-charpos overlay point) - :string (first bs) - :attribute (second bs)) - virtual-items))) - (when (and after-str (same-line-p (overlay-end overlay) point)) - (let ((as (alexandria:ensure-list after-str))) - (push (make-virtual-item :charpos (or (overlay-end-charpos overlay point) - (length string)) - :string (first as) - :attribute (second as)) - virtual-items)))) - (setf virtual-items - (stable-sort (nreverse virtual-items) #'< :key #'virtual-item-charpos)) - (setf (values string attributes) (expand-tab string attributes tab-width)) - (let ((charpos (point-charpos point))) - (when (< 0 charpos) - (psetf string (subseq string charpos) - attributes (lem/buffer/line:subseq-elements - attributes charpos (length string))) - ;; adjust virtual-item positions for the charpos clip (order preserved) + (overlay-attributes attributes + 0 + (length string) + attribute)) + (setf extend-to-end-attribute attribute))) + ((typep overlay 'cursor-overlay) + ;; remap the cursor into the spliced string so it lands on the right + ;; column past any folds on this visual line. + (let* ((ov-start (adjust-charpos-for-splices + (overlay-start-charpos overlay) splice-ops)) + (ov-end (1+ ov-start)) + (ov-attr (overlay-attribute overlay))) + (unless (cursor-overlay-fake-p overlay) + (set-cursor-attribute ov-attr)) + (if (<= (length string) ov-start) + (setf end-of-line-cursor-attribute ov-attr) + (setf attributes + (overlay-attributes attributes ov-start ov-end ov-attr))))) + (t + (let ((ov-start (adjust-charpos-for-splices + (overlay-start-charpos overlay) splice-ops)) + (ov-end (let ((e (overlay-end-charpos overlay))) + (when e + (adjust-charpos-for-splices e splice-ops)))) + (ov-attr (overlay-attribute overlay)) + (invisible (overlay-get overlay :invisible)) + (display (overlay-get overlay :display))) + ;; plain attribute (only when not replaced by invisible/display) + (when (and ov-attr (not invisible) (not display)) + (unless ov-end + (setf extend-to-end-attribute ov-attr)) + (setf attributes + (overlay-attributes attributes + ov-start + (or ov-end (length string)) + ov-attr))))))) + ;; virtual text from :before-string/:after-string overlays. emit each overlay's + ;; :before then :after, visiting overlays in (end, start) order: at any shared + ;; charpos an overlay closing there (smaller end) is emitted before one opening + ;; there, so trailing :after-strings precede leading :before-strings, but a + ;; zero-length overlay's own pair stays adjacent. a final stable sort by charpos + ;; groups them without affecting this order. + (loop :for overlay :in (stable-sort + (loop :for overlay :in overlays + :when (or (overlay-get overlay :before-string) + (overlay-get overlay :after-string)) + :collect overlay) + (lambda (a b) + (let ((a-end (or (overlay-end-charpos a) (length string))) + (b-end (or (overlay-end-charpos b) (length string)))) + (if (= a-end b-end) + (< (overlay-start-charpos a) + (overlay-start-charpos b)) + (< a-end b-end))))) + :for before-str := (overlay-get overlay :before-string) + :for after-str := (overlay-get overlay :after-string) + :do (when (and before-str (start-in-line-p overlay)) + (let ((bs (alexandria:ensure-list before-str))) + (push (make-virtual-item :charpos (overlay-start-charpos overlay) + :string (first bs) + :attribute (second bs)) + virtual-items))) + (when (and after-str (end-in-line-p overlay)) + (let ((as (alexandria:ensure-list after-str))) + (push (make-virtual-item :charpos (or (overlay-end-charpos overlay) + (length string)) + :string (first as) + :attribute (second as)) + virtual-items)))) + ;; markers were positioned in raw coordinates; remap them into the + ;; spliced string so several folds on one visual line stay anchored. + (dolist (vi virtual-items) + (setf (virtual-item-charpos vi) + (adjust-charpos-for-splices (virtual-item-charpos vi) splice-ops))) (setf virtual-items - (loop :for vi :in virtual-items - :when (>= (virtual-item-charpos vi) charpos) - :collect (make-virtual-item - :charpos (- (virtual-item-charpos vi) charpos) - :string (virtual-item-string vi) - :attribute (virtual-item-attribute vi)))))) - (make-logical-line - :string string - :attributes attributes - :virtual-items virtual-items - :left-content left-content - :extend-to-end extend-to-end-attribute - :end-of-line-cursor-attribute end-of-line-cursor-attribute - :line-end-overlay line-end-overlay)))))) + (stable-sort (nreverse virtual-items) #'< :key #'virtual-item-charpos)) + (setf (values string attributes) (expand-tab string attributes tab-width)) + (let ((charpos (point-charpos point))) + (when (< 0 charpos) + (psetf string (subseq string charpos) + attributes (lem/buffer/line:subseq-elements + attributes charpos (length string))) + ;; adjust virtual-item positions for the charpos clip (order preserved) + (setf virtual-items + (loop :for vi :in virtual-items + :when (>= (virtual-item-charpos vi) charpos) + :collect (make-virtual-item + :charpos (- (virtual-item-charpos vi) charpos) + :string (virtual-item-string vi) + :attribute (virtual-item-attribute vi)))))) + (make-logical-line + :string string + :attributes attributes + :virtual-items virtual-items + :left-content left-content + :extend-to-end extend-to-end-attribute + :end-of-line-cursor-attribute end-of-line-cursor-attribute + :line-end-overlay line-end-overlay)))))))) (defstruct string-with-attribute-item string @@ -462,8 +551,11 @@ VIRTUAL-ITEMS arrive in draw order (from `create-logical-line')." (loop :for logical-line := (create-logical-line point overlays active-modes) :do (when logical-line (funcall function logical-line)) - (unless (line-offset point 1) - (return)))))) + (loop + (unless (line-offset point 1) + (return-from call-do-logical-line)) + (unless (line-continuation-p point) + (return))))))) (defmacro do-logical-line ((logical-line window) &body body) `(call-do-logical-line ,window (lambda (,logical-line) ,@body))) diff --git a/src/ext/language-mode.lisp b/src/ext/language-mode.lisp index f04652f3a..0cca24512 100644 --- a/src/ext/language-mode.lisp +++ b/src/ext/language-mode.lisp @@ -139,9 +139,14 @@ (defun fold-region (start end &optional (fold-marker "...")) "hide the lines of the region [START, END), leaving START's line visible with a fold marker. returns the fold overlay." - (with-point ((s start)) + (with-point ((s start) + (e end)) (line-end s) - (let ((overlay (make-overlay s end 'fold-attribute))) + ;; dont hide the newline that terminates the folded region's last line, or the line after + ;; the fold gets merged onto the header's visual line. + (when (start-line-p e) + (character-offset e -1)) + (let ((overlay (make-overlay s e 'fold-attribute))) (overlay-put overlay :invisible t) (overlay-put overlay :fold t) (overlay-put overlay :before-string (list fold-marker 'fold-attribute)) diff --git a/src/internal-packages.lisp b/src/internal-packages.lisp index 92f4329b6..e28fc61ad 100644 --- a/src/internal-packages.lisp +++ b/src/internal-packages.lisp @@ -662,7 +662,8 @@ ;; display/logical-line.lisp (:export :make-region-overlays-using-global-mode - :line-fully-invisible-p) + :line-continuation-p + :invisible-overlay-covering) ;; interface.lisp (:export :with-implementation From 548b77e09a71e9485ce1df0c2b448ac7f466bbea Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Sat, 27 Jun 2026 12:40:53 +0300 Subject: [PATCH 05/26] add test --- src/attribute.lisp | 3 +++ src/display/logical-line.lisp | 16 ++++++++++++ src/ext/language-mode.lisp | 19 -------------- src/internal-packages.lisp | 4 ++- tests/visual-line.lisp | 49 +++++++++++++++++++++++++++++++++++ 5 files changed, 71 insertions(+), 20 deletions(-) create mode 100644 tests/visual-line.lisp diff --git a/src/attribute.lisp b/src/attribute.lisp index fd2197bf0..66241f38c 100644 --- a/src/attribute.lisp +++ b/src/attribute.lisp @@ -192,6 +192,9 @@ (:light :foreground nil :background "#eedc82") (:dark :foreground nil :background "blue")) +(define-attribute fold-attribute + (t :foreground :base04)) + (define-attribute modeline (t :bold t :background "#404040" :foreground "white")) diff --git a/src/display/logical-line.lisp b/src/display/logical-line.lisp index 5b5f94d04..e51777f65 100644 --- a/src/display/logical-line.lisp +++ b/src/display/logical-line.lisp @@ -133,6 +133,22 @@ visual line each splice text out." (point< point (overlay-end overlay)) overlay))) +(defun fold-region (start end &optional (fold-marker "...")) + "hide the lines of the region [START, END), leaving START's line visible with a fold marker. +returns the fold overlay." + (with-point ((s start) + (e end)) + (line-end s) + ;; dont hide the newline that terminates the folded region's last line, or the line after + ;; the fold gets merged onto the header's visual line. + (when (start-line-p e) + (character-offset e -1)) + (let ((overlay (make-overlay s e 'fold-attribute))) + (overlay-put overlay :invisible t) + (overlay-put overlay :fold t) + (overlay-put overlay :before-string (list fold-marker 'fold-attribute)) + overlay))) + (defun line-continuation-p (point) "whether POINT's line continues a previous visual line. meaning the newline preceding it is hidden by an :invisible overlay, so the line is not a visual line of its own. diff --git a/src/ext/language-mode.lisp b/src/ext/language-mode.lisp index 0cca24512..d417adda9 100644 --- a/src/ext/language-mode.lisp +++ b/src/ext/language-mode.lisp @@ -66,9 +66,6 @@ (define-editor-variable root-uri-patterns '()) (define-editor-variable detective-search nil) -(define-attribute fold-attribute - (t :foreground :base04)) - (defun prompt-for-symbol (prompt history-name) (prompt-for-string prompt :history-symbol history-name)) @@ -136,22 +133,6 @@ (when (point< start end) (values start end)))))) -(defun fold-region (start end &optional (fold-marker "...")) - "hide the lines of the region [START, END), leaving START's line visible with a fold marker. -returns the fold overlay." - (with-point ((s start) - (e end)) - (line-end s) - ;; dont hide the newline that terminates the folded region's last line, or the line after - ;; the fold gets merged onto the header's visual line. - (when (start-line-p e) - (character-offset e -1)) - (let ((overlay (make-overlay s e 'fold-attribute))) - (overlay-put overlay :invisible t) - (overlay-put overlay :fold t) - (overlay-put overlay :before-string (list fold-marker 'fold-attribute)) - overlay))) - (defun fold-overlay-at (point) "the fold overlay whose header line is POINT's line, or NIL." (find-if diff --git a/src/internal-packages.lisp b/src/internal-packages.lisp index e28fc61ad..29e80c378 100644 --- a/src/internal-packages.lisp +++ b/src/internal-packages.lisp @@ -134,6 +134,7 @@ :define-attribute :cursor :region + :fold-attribute :modeline :modeline-inactive :truncate-attribute @@ -663,7 +664,8 @@ (:export :make-region-overlays-using-global-mode :line-continuation-p - :invisible-overlay-covering) + :invisible-overlay-covering + :fold-region) ;; interface.lisp (:export :with-implementation diff --git a/tests/visual-line.lisp b/tests/visual-line.lisp new file mode 100644 index 000000000..88a6a210a --- /dev/null +++ b/tests/visual-line.lisp @@ -0,0 +1,49 @@ +(defpackage :lem-tests/visual-line + (:use :cl :rove :lem) + (:import-from + :lem-tests/utilities + :with-testing-buffer + :make-text-buffer + :lines)) + +(in-package :lem-tests/visual-line) + +(defun point-at-line (buffer line-number) + "temporary point at the start of LINE-NUMBER (0-based) of BUFFER." + (let ((point (copy-point (buffer-start-point buffer) :temporary))) + (line-offset point line-number) + (line-start point) + point)) + +(deftest visual-line-navigation-across-folds + (lem-fake-interface:with-fake-interface () + (with-testing-buffer (buffer (make-text-buffer + (lines "AAAA" "BBBB" "CCCC" "DDDD" "EEEE" "FFFF"))) + (labels ((fold-lines (first last) + "fold buffer lines FIRST..LAST (inclusive) into a single visual line." + (fold-region (point-at-line buffer first) + (point-at-line buffer (1+ last)))) + (expect-visual-line (containing-line first-line last-line) + (testing (format + nil + "buffer line ~D belongs to the visual line spanning lines ~D..~D" + containing-line + first-line + last-line) + (with-point ((p1 (point-at-line buffer containing-line)) + (p2 (point-at-line buffer containing-line))) + (line-offset p1 0 2) + (line-offset p2 0 2) + (ok (= (position-at-point (visual-line-beginning p1)) + (position-at-point (point-at-line buffer first-line)))) + (ok (= (position-at-point (visual-line-end p2)) + (position-at-point (line-end (point-at-line buffer last-line))))))))) + (fold-lines 0 1) + (fold-lines 3 4) + ;; folded lines collapse with others, unfolded lines stand alone. + (expect-visual-line 0 0 1) + (expect-visual-line 1 0 1) + (expect-visual-line 2 2 2) + (expect-visual-line 3 3 4) + (expect-visual-line 4 3 4) + (expect-visual-line 5 5 5))))) \ No newline at end of file From 3b67950942c57c4269928b1413f131a8f05a06c7 Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Tue, 30 Jun 2026 22:48:21 +0300 Subject: [PATCH 06/26] make overlay enter/exit behavior customizable --- src/commands/move.lisp | 102 +++++++++++++++++-------- src/display/logical-line.lisp | 72 +++++++++++++++--- src/ext/language-mode.lisp | 7 +- src/internal-packages.lisp | 6 +- tests/visual-line.lisp | 138 +++++++++++++++++++++++++++++++++- 5 files changed, 278 insertions(+), 47 deletions(-) diff --git a/src/commands/move.lisp b/src/commands/move.lisp index 385d99079..da7d448a4 100644 --- a/src/commands/move.lisp +++ b/src/commands/move.lisp @@ -49,40 +49,76 @@ (defvar *cursor-positions-before-command* (make-hash-table :test 'eq) - "maps each cursor point to its absolute buffer position before the running -command, so `snap-cursors-out-of-invisible' can tell which direction it moved.") + "maps each cursor point to its absolute buffer position before the running command, so +`run-overlay-cursor-motion-hooks' can tell which direction it moved.") -(add-hook *pre-command-hook* 'record-cursor-positions) -(add-hook *post-command-hook* 'snap-cursors-out-of-invisible) +(defvar *cursor-overlays-before-command* + (make-hash-table :test 'eq) + "maps each cursor point to the cursor-hook overlays it occupied before the running command, +so `run-overlay-cursor-motion-hooks' can tell which overlays it entered and which it left.") + +(add-hook *pre-command-hook* 'snapshot-cursor-state) +(add-hook *post-command-hook* 'run-overlay-cursor-motion-hooks) -(defun record-cursor-positions () - "snapshot every cursor's position so a later snap can pick the visible edge on the far side -of a fold the cursor moves into." +(defun snapshot-cursor-state () + "snapshot every cursor's position and the cursor-hook overlays it occupies, so that after the +command `run-overlay-cursor-motion-hooks' can tell which overlays each cursor entered and left, +and in which direction." (clrhash *cursor-positions-before-command*) + (clrhash *cursor-overlays-before-command*) (dolist (point (buffer-cursors (current-buffer))) (setf (gethash point *cursor-positions-before-command*) - (position-at-point point)))) + (position-at-point point)) + (setf (gethash point *cursor-overlays-before-command*) + (overlays-with-cursor-hooks-covering point)))) + +(defun run-overlay-cursor-enter-functions (point direction) + "run the :cursor-enter-functions of every cursor-hook overlay POINT has entered since the +command, each called as (FUNCTION point overlay direction). repeats while a handler +repositions POINT so that overlays it is then pushed into also fire." + (let ((seen (copy-list (gethash point *cursor-overlays-before-command*))) + (visited (list (position-at-point point)))) + (loop + (let ((entered (set-difference (overlays-with-cursor-hooks-covering point) seen))) + (when (null entered) + (return)) + (let ((before-pass (position-at-point point))) + (dolist (overlay entered) + (push overlay seen) + (dolist (function (overlay-get overlay :cursor-enter-functions)) + (funcall function point overlay direction))) + (let ((after-pass (position-at-point point))) + ;; stop once a pass leaves POINT put, or revisits a position. the latter guards + ;; against two overlays snapping a cursor back and forth forever. + ;; this is tricky. this is definitely not the best way to do things but it works for now. + ;; the nature of overlay hooks itself is tricky anyway. + (when (or (= before-pass after-pass) + (member after-pass visited)) + (return)) + (push after-pass visited))))))) + +(defun run-overlay-cursor-leave-functions (point direction) + "run the :cursor-leave-functions of every cursor-hook overlay POINT was in before the +command, each called as (FUNCTION point overlay direction)." + (let ((left (set-difference (gethash point *cursor-overlays-before-command*) + (overlays-with-cursor-hooks-covering point)))) + (dolist (overlay left) + (dolist (function (overlay-get overlay :cursor-leave-functions)) + (funcall function point overlay direction))))) -(defun snap-cursors-out-of-invisible () - "a fold collapses its range, so no cursor may rest within it or at its start. move any such -cursor to the nearest visible position in its direction of travel, so the cursor never appears -stuck on hidden text." +(defun cursor-move-direction-overlay (point) + "the direction POINT moved during the command, :forward or :backward (:forward when its prior +position is unknown or unchanged)." + (let ((before (gethash point *cursor-positions-before-command*))) + (if (and before (< (position-at-point point) before)) + :backward + :forward))) + +(defun run-overlay-cursor-motion-hooks () (dolist (point (buffer-cursors (current-buffer))) - (let ((overlay (invisible-overlay-covering point))) - (when overlay - (let ((forwardp (let ((before (gethash point *cursor-positions-before-command*))) - (or (null before) - (<= before (position-at-point (overlay-start overlay))))))) - (loop :for ov := (invisible-overlay-covering point) - :while ov - :do (if forwardp - (move-point point (overlay-end ov)) - (progn - (move-point point (overlay-start ov)) - ;; dont continue looping if we are at the beginning of the buffer. - ;; it could cause an endless loop. - (unless (character-offset point -1) - (return)))))))))) + (let ((direction (cursor-move-direction-overlay point))) + (run-overlay-cursor-enter-functions point direction) + (run-overlay-cursor-leave-functions point direction)))) (defun visual-line-end (point) "move POINT to the end of its visual line. returns POINT." @@ -101,10 +137,16 @@ stuck on hidden text." (line-start point)) point) +(defun line-continuation-to-skip (point) + (let ((overlay (line-continuation-p point))) + (and overlay + (not (overlay-get overlay :cursor-leave-functions)) + overlay))) + (defun skip-invisible-lines (point direction) "move POINT past any fully invisible lines in DIRECTION (1 or -1). returns POINT on success, or NIL if a buffer boundary is reached." - (loop :while (line-continuation-p point) + (loop :while (line-continuation-to-skip point) :do (unless (line-offset point direction) (return-from skip-invisible-lines nil))) point) @@ -116,7 +158,7 @@ returns POINT on success, or NIL if a buffer boundary is reached." (loop :repeat steps :do (unless (move-to-next-virtual-line point dir) (return-from move-to-next-visible-virtual-line nil)) - (when (line-continuation-p point) + (when (line-continuation-to-skip point) (unless (skip-invisible-lines point dir) (return-from move-to-next-visible-virtual-line nil)))) point)) @@ -128,7 +170,7 @@ returns POINT on success, or NIL if a buffer boundary is reached." (loop :repeat steps :do (unless (line-offset point dir) (return-from visible-line-offset nil)) - (when (line-continuation-p point) + (when (line-continuation-to-skip point) (unless (skip-invisible-lines point dir) (return-from visible-line-offset nil)))) point)) diff --git a/src/display/logical-line.lisp b/src/display/logical-line.lisp index e51777f65..b6f4fbffc 100644 --- a/src/display/logical-line.lisp +++ b/src/display/logical-line.lisp @@ -133,20 +133,71 @@ visual line each splice text out." (point< point (overlay-end overlay)) overlay))) -(defun fold-region (start end &optional (fold-marker "...")) +(defun move-point-out-of-overlay (point overlay direction) + "move POINT to the nearest edge of OVERLAY in DIRECTION (:forward or :backward), so it does not +rest inside. usable directly as a :cursor-enter-functions handler; `fold-region' installs it by +default so a cursor never appears stuck on the hidden text of a fold." + (if (eq direction :backward) + (progn + (move-point point (overlay-start overlay)) + ;; step onto the last visible position before the overlay, unless that is the buffer + ;; start, where there is nowhere further to go. + (character-offset point -1)) + (move-point point (overlay-end overlay)))) + +(defun reveal-overlay-on-cursor-enter (point overlay direction) + (overlay-put overlay :invisible nil) + (overlay-put overlay :show-virtual-text nil)) + +(defun hide-overlay-on-cursor-leave (point overlay direction) + (overlay-put overlay :invisible t) + (overlay-put overlay :show-virtual-text t)) + +(defun overlay-show-virtual-text-p (overlay) + "whether OVERLAY's :before-string/:after-string should render. defaults to T." + (let ((value (getf (overlay-plist overlay) :show-virtual-text :unset))) + (if (eq value :unset) t value))) + +(defun overlay-has-cursor-hooks-p (overlay) + (or (overlay-get overlay :cursor-enter-functions) + (overlay-get overlay :cursor-leave-functions))) + +(defun overlays-with-cursor-hooks-covering (point) + "the overlays covering POINT that take part in cursor enter/leave tracking." + (loop :for overlay :in (buffer-overlays (point-buffer point)) + :when (and (overlay-has-cursor-hooks-p overlay) + (point<= (overlay-start overlay) point) + (point< point (overlay-end overlay))) + :collect overlay)) + +;; but reveal behavior isnt relevant for line folding +(defun place-region-placeholder-overlay (start end &key (placeholder "...") (cursor-behavior :move-out) (is-line-fold t)) "hide the lines of the region [START, END), leaving START's line visible with a fold marker. -returns the fold overlay." +returns the fold overlay. CURSOR-BEHAVIOR decides how the cursor is kept off the hidden text: +- :move-out :: move the cursor to the nearest visible edge (see `move-point-out-of-overlay'). +- :reveal :: open the fold while the cursor is inside it and close it again on leave (see + `reveal-overlay-on-cursor-enter' / `hide-overlay-on-cursor-leave'). +- nil :: install nothing; the cursor may rest on the hidden text. +callers can also set the overlay's :cursor-enter-functions / :cursor-leave-functions directly." (with-point ((s start) (e end)) - (line-end s) - ;; dont hide the newline that terminates the folded region's last line, or the line after - ;; the fold gets merged onto the header's visual line. - (when (start-line-p e) - (character-offset e -1)) + (when is-line-fold + (line-end s) + ;; dont hide the newline that terminates the folded region's last line, or the line after + ;; the fold gets merged onto the header's visual line. + (when (start-line-p e) + (character-offset e -1))) (let ((overlay (make-overlay s e 'fold-attribute))) (overlay-put overlay :invisible t) (overlay-put overlay :fold t) - (overlay-put overlay :before-string (list fold-marker 'fold-attribute)) + (overlay-put overlay :before-string (list placeholder 'fold-attribute)) + (ecase cursor-behavior + (:move-out + (overlay-put overlay :cursor-enter-functions (list 'move-point-out-of-overlay))) + (:reveal + (overlay-put overlay :cursor-enter-functions (list 'reveal-overlay-on-cursor-enter)) + (overlay-put overlay :cursor-leave-functions (list 'hide-overlay-on-cursor-leave))) + ((nil))) overlay))) (defun line-continuation-p (point) @@ -313,8 +364,9 @@ several folds that each hide arbitrary character ranges across multiple buffer l ;; groups them without affecting this order. (loop :for overlay :in (stable-sort (loop :for overlay :in overlays - :when (or (overlay-get overlay :before-string) - (overlay-get overlay :after-string)) + :when (and (overlay-show-virtual-text-p overlay) + (or (overlay-get overlay :before-string) + (overlay-get overlay :after-string))) :collect overlay) (lambda (a b) (let ((a-end (or (overlay-end-charpos a) (length string))) diff --git a/src/ext/language-mode.lisp b/src/ext/language-mode.lisp index d417adda9..070628741 100644 --- a/src/ext/language-mode.lisp +++ b/src/ext/language-mode.lisp @@ -146,9 +146,10 @@ (let ((fn (variable-value 'fold-region-function :default point))) (multiple-value-bind (start end) (funcall fn point) (when (and start end (not (same-line-p start end))) - (fold-region start end) - (move-point point start) - t)))) + (let ((overlay (place-region-placeholder-overlay start end :is-line-fold t))) + (move-point point start) + (overlay-put overlay :fold t) + overlay))))) (defun fold-toggle-at-point (&optional (point (current-point))) "toggle the fold at POINT. returns T when a fold was added or removed, and NIL when there was diff --git a/src/internal-packages.lisp b/src/internal-packages.lisp index 29e80c378..8a6e8b04e 100644 --- a/src/internal-packages.lisp +++ b/src/internal-packages.lisp @@ -665,7 +665,11 @@ :make-region-overlays-using-global-mode :line-continuation-p :invisible-overlay-covering - :fold-region) + :move-point-out-of-overlay + :reveal-overlay-on-cursor-enter + :hide-overlay-on-cursor-leave + :overlays-with-cursor-hooks-covering + :place-region-placeholder-overlay) ;; interface.lisp (:export :with-implementation diff --git a/tests/visual-line.lisp b/tests/visual-line.lisp index 88a6a210a..45b576e87 100644 --- a/tests/visual-line.lisp +++ b/tests/visual-line.lisp @@ -15,14 +15,27 @@ (line-start point) point)) +(defun forward-char-command () + (lem-core::save-continue-flags + (lem-core:call-command (make-instance 'lem:forward-char) nil))) + +(defun backward-char-command () + (lem-core::save-continue-flags + (lem-core:call-command (make-instance 'lem:backward-char) nil))) + +(defun next-line-command () + (lem-core::save-continue-flags + (lem-core:call-command (make-instance 'lem:next-line) nil))) + (deftest visual-line-navigation-across-folds (lem-fake-interface:with-fake-interface () (with-testing-buffer (buffer (make-text-buffer (lines "AAAA" "BBBB" "CCCC" "DDDD" "EEEE" "FFFF"))) (labels ((fold-lines (first last) "fold buffer lines FIRST..LAST (inclusive) into a single visual line." - (fold-region (point-at-line buffer first) - (point-at-line buffer (1+ last)))) + (place-region-placeholder-overlay + (point-at-line buffer first) + (point-at-line buffer (1+ last)))) (expect-visual-line (containing-line first-line last-line) (testing (format nil @@ -46,4 +59,123 @@ (expect-visual-line 2 2 2) (expect-visual-line 3 3 4) (expect-visual-line 4 3 4) - (expect-visual-line 5 5 5))))) \ No newline at end of file + (expect-visual-line 5 5 5))))) + +(defun hide-range (buffer start-charpos end-charpos &key (cursor-behavior nil)) + (with-point ((start (buffer-start-point buffer)) + (end (buffer-start-point buffer))) + (character-offset start start-charpos) + (character-offset end end-charpos) + (place-region-placeholder-overlay + start + end + :is-line-fold nil + :cursor-behavior cursor-behavior))) + +;; move-point-out-of-overlay (the :move-out handler) moves the cursor out of an invisible +;; overlay's span +(deftest cursor-moves-out-of-invisible-overlay + (lem-fake-interface:with-fake-interface () + (with-testing-buffer (buffer (make-text-buffer (lines "ABCDEFGH"))) + (lem-core::set-window-buffer buffer (current-window)) + ;; hides "EFG" + (let ((overlay (hide-range buffer 4 7 :cursor-behavior :move-out)) + (point (buffer-point buffer))) + (move-point point (buffer-start-point buffer)) + ;; ABC|DEFGH, just before the range + (character-offset point 3) + ;; ABCD|EFGH, enters the range, gets moved out + (forward-char-command) + (ok (null (invisible-overlay-covering point)) "not left on hidden text") + (ok (point= point (overlay-end overlay)) "pushed forward out of the span") + ;; entering from the far side throws the cursor out the other way + (backward-char-command) + (ok (null (invisible-overlay-covering point)) "not left on hidden text") + (ok (point< point (overlay-start overlay)) "pushed backward out of the span"))))) + +;; reveal-overlay-on-cursor-enter / hide-overlay-on-cursor-leave toggle :invisible as the cursor +;; enters and leaves. +(deftest cursor-reveals-and-hides-invisible-overlay + (lem-fake-interface:with-fake-interface () + (with-testing-buffer (buffer (make-text-buffer (lines "ABCDEFGH"))) + (lem-core::set-window-buffer buffer (current-window)) + (let ((overlay (hide-range buffer 4 7 :cursor-behavior :reveal)) + (point (buffer-point buffer))) + (ok (eq t (overlay-get overlay :invisible)) "starts hidden") + ;; step from just before the hidden range into it; entering must reveal it. + (move-point point (buffer-start-point buffer)) + ;; ABC|DEFGH, just before the range + (character-offset point 3) + ;; ABCD|EFGH, cursor enters the range, text gets revealed + (forward-char-command) + (ok (null (overlay-get overlay :invisible)) "revealed on enter") + ;; keep walking until the cursor passes the far edge. leaving must hide it again. + (loop :until (point<= (overlay-end overlay) point) + :do (forward-char-command)) + (ok (eq t (overlay-get overlay :invisible)) "hidden again on leave"))))) + +;; move-point-out-of-overlay also moves the cursor out of a :move-out fold whose hidden span +;; crosses a newline (an arbitrary line-collapsing fold), so forward-char/backward-char never get +;; stuck on the hidden text when stepping in from either side. +(deftest cursor-moves-out-of-newline-crossing-fold + (lem-fake-interface:with-fake-interface () + (with-testing-buffer (buffer (make-text-buffer + (lines "abc DEF ghi" "JKL mno PQR" "stu VWX yz"))) + (lem-core::set-window-buffer buffer (current-window)) + ;; hide "DEF ghi" + newline + "JKL " + (with-point ((start (point-at-line buffer 0)) + (end (point-at-line buffer 1))) + (character-offset start 4) + (character-offset end 4) + (let ((overlay (place-region-placeholder-overlay + start + end + :is-line-fold nil + :cursor-behavior :move-out)) + (point (buffer-point buffer))) + ;; "abc |DEF..." just before the span. entering it pushes out the forward edge. + (move-point point (point-at-line buffer 0)) + (character-offset point 3) + (forward-char-command) + (ok (null (invisible-overlay-covering point)) "not left on hidden text") + (ok (point= point (overlay-end overlay)) "pushed forward out of the span") + ;; entering from the far side throws the cursor out the other way + (backward-char-command) + (ok (null (invisible-overlay-covering point)) "not left on hidden text") + (ok (point< point (overlay-start overlay)) "pushed backward out of the span")))))) + +;; vertical motion steps clear of a :move-out fold (the default, e.g. a folded defun): next-line +;; from the fold header lands on the first visible line past the hidden lines, not inside them. +(deftest next-line-skips-move-out-fold + (lem-fake-interface:with-fake-interface () + (with-testing-buffer (buffer (make-text-buffer + (lines "AAAA" "BBBB" "CCCC" "DDDD" "EEEE" "FFFF"))) + (lem-core::set-window-buffer buffer (current-window)) + (place-region-placeholder-overlay (point-at-line buffer 1) + (point-at-line buffer 4)) + (let ((point (buffer-point buffer))) + (move-point point (point-at-line buffer 1)) + (next-line-command) + (ok (null (invisible-overlay-covering point)) "not left on a hidden line") + (ok (= (position-at-point point) + (position-at-point (point-at-line buffer 4))) + "next-line landed on the first visible line past the fold"))))) + +;; vertical motion enters a :reveal fold: next-line from the header lands on the first hidden line +;; and the post-command cursor-enter hook reveals it. +(deftest next-line-enters-and-reveals-reveal-fold + (lem-fake-interface:with-fake-interface () + (with-testing-buffer (buffer (make-text-buffer + (lines "AAAA" "BBBB" "CCCC" "DDDD" "EEEE" "FFFF"))) + (lem-core::set-window-buffer buffer (current-window)) + (let ((overlay (place-region-placeholder-overlay (point-at-line buffer 1) + (point-at-line buffer 4) + :cursor-behavior :reveal)) + (point (buffer-point buffer))) + (ok (eq t (overlay-get overlay :invisible)) "starts hidden") + (move-point point (point-at-line buffer 1)) + (next-line-command) + (ok (null (overlay-get overlay :invisible)) "revealed after entering on next-line") + (ok (= (position-at-point point) + (position-at-point (point-at-line buffer 2))) + "cursor landed inside the fold on the first hidden line"))))) \ No newline at end of file From 46c45736a453aa26f1e2b5fe67dd8ba5e854fd2a Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Wed, 1 Jul 2026 23:21:57 +0300 Subject: [PATCH 07/26] fold only when on first line of defun --- src/ext/language-mode.lisp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ext/language-mode.lisp b/src/ext/language-mode.lisp index 070628741..af6045534 100644 --- a/src/ext/language-mode.lisp +++ b/src/ext/language-mode.lisp @@ -145,7 +145,9 @@ "fold the defun at POINT. Returns T when something was folded." (let ((fn (variable-value 'fold-region-function :default point))) (multiple-value-bind (start end) (funcall fn point) - (when (and start end (not (same-line-p start end))) + (when (and start end + (not (same-line-p start end)) + (same-line-p start point)) (let ((overlay (place-region-placeholder-overlay start end :is-line-fold t))) (move-point point start) (overlay-put overlay :fold t) From 0a7868495da9e0216399f741093861d4da81585a Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Fri, 3 Jul 2026 17:06:55 +0300 Subject: [PATCH 08/26] fix scrolling issue when moving the cursor down there was an issue where the screen randomly jumps a few lines down --- src/window/virtual-line.lisp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/window/virtual-line.lisp b/src/window/virtual-line.lisp index 3d314759b..d71d36bf5 100644 --- a/src/window/virtual-line.lisp +++ b/src/window/virtual-line.lisp @@ -95,9 +95,30 @@ next line because it is at the end of width." #'inc))) offset))) +(defun count-hidden-lines (start-point end-point) + "number of hidden buffer lines between START-POINT and END-POINT." + (when (point< end-point start-point) + (rotatef start-point end-point)) + (with-point ((p start-point) + (goal end-point)) + (line-start p) + (line-start goal) + (loop :with count := 0 + :until (same-line-p p goal) + :do (unless (line-offset p 1) + (return count)) + (when (line-continuation-p p) + (incf count)) + :finally (return count)))) + (defun window-cursor-y-not-wrapping (window) - (count-lines (window-buffer-point window) - (window-view-point window))) + "number of screen rows between the view point and the cursor. +excludes lines hidden by overlays with :invisible property because those lines dont occupy any +vertical space." + (let ((view-point (window-view-point window)) + (buffer-point (window-buffer-point window))) + (- (count-lines buffer-point view-point) + (count-hidden-lines view-point buffer-point)))) (defun window-cursor-y (window) (if (point< (window-buffer-point window) From 039b7ddee685e7a0f59db8c0e9309054182657d1 Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Sat, 4 Jul 2026 22:38:19 +0300 Subject: [PATCH 09/26] support for displaying images in webview --- .../server/frontend/dist/assets/index.css | 2 +- .../server/frontend/dist/assets/index.js | 2 +- frontends/server/frontend/editor.js | 101 +++++++++++++++++- frontends/server/main.lisp | 83 +++++++++++++- src/display/physical-line.lisp | 12 +-- src/internal-packages.lisp | 1 + 6 files changed, 181 insertions(+), 20 deletions(-) diff --git a/frontends/server/frontend/dist/assets/index.css b/frontends/server/frontend/dist/assets/index.css index 3dfcc503b..d88bed8c2 100644 --- a/frontends/server/frontend/dist/assets/index.css +++ b/frontends/server/frontend/dist/assets/index.css @@ -1 +1 @@ -@keyframes lem-cursor-blink{0%,to{opacity:1}50%{opacity:0}}.lem-cursor{position:absolute;pointer-events:none;z-index:300;animation:lem-cursor-blink 1s step-end infinite;overflow:hidden;white-space:pre;line-height:1;box-sizing:border-box}.lem-editor__floating-window--bordered{padding:10px;border:none;border-radius:8px;box-shadow:4px 4px 16px #000,0 0 0 1px #80808033;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}.lem-editor__mode-line{border:none;border-radius:4px;box-shadow:0 0 0 1px #80808080}.lem-editor__vertical-border{width:10px;background:linear-gradient(to right,transparent 4px,rgba(128,128,128,.5) 4px 6px,transparent 5px)}.lem-editor__horizontal-border{height:10px;background:linear-gradient(to bottom,transparent 0px,rgba(128,128,128,.5) 3px 5px,transparent 2px)} +@keyframes lem-cursor-blink{0%,to{opacity:1}50%{opacity:0}}.lem-cursor{pointer-events:none;z-index:300;white-space:pre;box-sizing:border-box;line-height:1;animation:1s step-end infinite lem-cursor-blink;position:absolute;overflow:hidden}.lem-editor__floating-window--bordered{-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);border:none;border-radius:8px;padding:10px;box-shadow:4px 4px 16px #000,0 0 0 1px #80808033}.lem-editor__mode-line{border:none;border-radius:4px;box-shadow:0 0 0 1px #80808080}.lem-editor__vertical-border{background:linear-gradient(90deg,#0000 4px,#80808080 4px 6px,#0000 5px);width:10px}.lem-editor__horizontal-border{background:linear-gradient(#0000 0,#80808080 3px 5px,#0000 2px);height:10px} diff --git a/frontends/server/frontend/dist/assets/index.js b/frontends/server/frontend/dist/assets/index.js index a036fd0e9..1cc125100 100644 --- a/frontends/server/frontend/dist/assets/index.js +++ b/frontends/server/frontend/dist/assets/index.js @@ -1 +1 @@ -(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const n of document.querySelectorAll('link[rel="modulepreload"]'))i(n);new MutationObserver(n=>{for(const s of n)if(s.type==="childList")for(const r of s.addedNodes)r.tagName==="LINK"&&r.rel==="modulepreload"&&i(r)}).observe(document,{childList:!0,subtree:!0});function t(n){const s={};return n.integrity&&(s.integrity=n.integrity),n.referrerPolicy&&(s.referrerPolicy=n.referrerPolicy),n.crossOrigin==="use-credentials"?s.credentials="include":n.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function i(n){if(n.ep)return;n.ep=!0;const s=t(n);fetch(n.href,s)}})();var dist={},client={},models={},hasRequiredModels;function requireModels(){return hasRequiredModels||(hasRequiredModels=1,function(o){var e=models&&models.__extends||function(){var u=function(h,a){return u=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(c,p){c.__proto__=p}||function(c,p){for(var w in p)Object.prototype.hasOwnProperty.call(p,w)&&(c[w]=p[w])},u(h,a)};return function(h,a){if(typeof a!="function"&&a!==null)throw new TypeError("Class extends value "+String(a)+" is not a constructor or null");u(h,a);function c(){this.constructor=h}h.prototype=a===null?Object.create(a):(c.prototype=a.prototype,new c)}}();Object.defineProperty(o,"__esModule",{value:!0}),o.createJSONRPCNotification=o.createJSONRPCRequest=o.createJSONRPCSuccessResponse=o.createJSONRPCErrorResponse=o.JSONRPCErrorCode=o.JSONRPCErrorException=o.isJSONRPCResponses=o.isJSONRPCResponse=o.isJSONRPCRequests=o.isJSONRPCRequest=o.isJSONRPCID=o.JSONRPC=void 0,o.JSONRPC="2.0";var t=function(u){return typeof u=="string"||typeof u=="number"||u===null};o.isJSONRPCID=t;var i=function(u){return u.jsonrpc===o.JSONRPC&&u.method!==void 0&&u.result===void 0&&u.error===void 0};o.isJSONRPCRequest=i;var n=function(u){return Array.isArray(u)&&u.every(o.isJSONRPCRequest)};o.isJSONRPCRequests=n;var s=function(u){return u.jsonrpc===o.JSONRPC&&u.id!==void 0&&(u.result!==void 0||u.error!==void 0)};o.isJSONRPCResponse=s;var r=function(u){return Array.isArray(u)&&u.every(o.isJSONRPCResponse)};o.isJSONRPCResponses=r;var l=function(u,h,a){var c={code:u,message:h};return a!=null&&(c.data=a),c},f=function(u){e(h,u);function h(a,c,p){var w=u.call(this,a)||this;return Object.setPrototypeOf(w,h.prototype),w.code=c,w.data=p,w}return h.prototype.toObject=function(){return l(this.code,this.message,this.data)},h}(Error);o.JSONRPCErrorException=f,function(u){u[u.ParseError=-32700]="ParseError",u[u.InvalidRequest=-32600]="InvalidRequest",u[u.MethodNotFound=-32601]="MethodNotFound",u[u.InvalidParams=-32602]="InvalidParams",u[u.InternalError=-32603]="InternalError"}(o.JSONRPCErrorCode||(o.JSONRPCErrorCode={}));var d=function(u,h,a,c){return{jsonrpc:o.JSONRPC,id:u,error:l(h,a,c)}};o.createJSONRPCErrorResponse=d;var y=function(u,h){return{jsonrpc:o.JSONRPC,id:u,result:h??null}};o.createJSONRPCSuccessResponse=y;var v=function(u,h,a){return{jsonrpc:o.JSONRPC,id:u,method:h,params:a}};o.createJSONRPCRequest=v;var N=function(u,h){return{jsonrpc:o.JSONRPC,method:u,params:h}};o.createJSONRPCNotification=N}(models)),models}var internal={},hasRequiredInternal;function requireInternal(){return hasRequiredInternal||(hasRequiredInternal=1,Object.defineProperty(internal,"__esModule",{value:!0}),internal.DefaultErrorCode=void 0,internal.DefaultErrorCode=0),internal}var hasRequiredClient;function requireClient(){if(hasRequiredClient)return client;hasRequiredClient=1;var o=client&&client.__awaiter||function(r,l,f,d){function y(v){return v instanceof f?v:new f(function(N){N(v)})}return new(f||(f=Promise))(function(v,N){function u(c){try{a(d.next(c))}catch(p){N(p)}}function h(c){try{a(d.throw(c))}catch(p){N(p)}}function a(c){c.done?v(c.value):y(c.value).then(u,h)}a((d=d.apply(r,l||[])).next())})},e=client&&client.__generator||function(r,l){var f={label:0,sent:function(){if(v[0]&1)throw v[1];return v[1]},trys:[],ops:[]},d,y,v,N;return N={next:u(0),throw:u(1),return:u(2)},typeof Symbol=="function"&&(N[Symbol.iterator]=function(){return this}),N;function u(a){return function(c){return h([a,c])}}function h(a){if(d)throw new TypeError("Generator is already executing.");for(;N&&(N=0,a[0]&&(f=0)),f;)try{if(d=1,y&&(v=a[0]&2?y.return:a[0]?y.throw||((v=y.return)&&v.call(y),0):y.next)&&!(v=v.call(y,a[1])).done)return v;switch(y=0,v&&(a=[a[0]&2,v.value]),a[0]){case 0:case 1:v=a;break;case 4:return f.label++,{value:a[1],done:!1};case 5:f.label++,y=a[1],a=[0];continue;case 7:a=f.ops.pop(),f.trys.pop();continue;default:if(v=f.trys,!(v=v.length>0&&v[v.length-1])&&(a[0]===6||a[0]===2)){f=0;continue}if(a[0]===3&&(!v||a[1]>v[0]&&a[1]0&&m[m.length-1])&&(A[0]===6||A[0]===2)){p=0;continue}if(A[0]===3&&(!m||A[1]>m[0]&&A[1]0&&d[d.length-1])&&(u[0]===6||u[0]===2)){r=0;continue}if(u[0]===3&&(!d||u[1]>d[0]&&u[1]{const[t,i,n]=e;this.requestInternal(t,i,n)}),this.messageQueue=[]}request(e,t,i){this.webSocket.readyState===WebSocket.OPEN?this.requestInternal(e,t,i):this.messageQueue.push([e,t,i])}notify(e,t){switch(this.webSocket.readyState){case WebSocket.OPEN:this.serverAndClient.notify(e,t);break}}connect(e){this.closed||(console.log("connect",this.url),this.webSocket=new WebSocket(this.url),this.serverAndClient||(this.serverAndClient=new distExports.JSONRPCServerAndClient(new distExports.JSONRPCServer,new distExports.JSONRPCClient(t=>{try{return this.webSocket.send(JSON.stringify(t)),Promise.resolve()}catch(i){return Promise.reject(i)}}))),this.webSocket.onmessage=t=>{this.serverAndClient.receiveAndSend(JSON.parse(t.data.toString()))},this.webSocket.onopen=()=>{console.log("WebSocket connection established"),this.connectionEstablished=!0,this.onConnected&&this.onConnected(),this.requestMessageQueue()},this.webSocket.onclose=t=>{console.error("WebScoket closed",t),this.serverAndClient.rejectAllPendingRequests(`Connection is closed (${t.reason}).`),this.connectionEstablished&&this.onClosed(),this.timerId=setTimeout(()=>{this.connect()},3e3)},this.webSocket.onerror=t=>{console.error("WebSocket error:",t),this.webSocket.close()})}}const modifierKeys=["Shift","Control","Alt","Meta","CapsLock"],convertKeyTable={Enter:"Return",ArrowRight:"Right",ArrowLeft:"Left",ArrowUp:"Up",ArrowDown:"Down","¡":"1","™":"2","£":"3","¢":"4","∞":"5","§":"6","¶":"7","•":"8",ª:"9",º:"0","–":"-","≠":"=","“":"[","‘":"]","«":"\\","…":";",æ:"'","≤":",","≥":".","÷":"/","⁄":"!","€":"@","‹":"#","›":"$",fi:"%",fl:"^","‡":"&","°":"*","·":"(","‚":")","—":"_","±":"+","”":"{","’":"}","»":"|",Ú:":",Æ:'"',"¯":"<","˘":">","¿":"?",œ:"q","∑":"w","´":"e","®":"r","†":"t","¥":"y","¨":"u","ˆ":"i",ø:"o",π:"p",å:"a",ß:"s","∂":"d",ƒ:"f","©":"g","˙":"h","∆":"j","˚":"k","¬":"l",Ω:"z","≈":"x",ç:"c","√":"v","∫":"b","˜":"n",µ:"m",Œ:"Q","„":"W","´":"E","‰":"R","ˇ":"T",Á:"Y","¨":"U","ˆ":"I",Ø:"O","∏":"P",Å:"A",Í:"S",Î:"D",Ï:"F","˝":"G",Ó:"H",Ô:"J","":"K",Ò:"L","¸":"Z","˛":"X",Ç:"C","◊":"V",ı:"B","˜":"N",Â:"M"};function getKey(o){return o.altKey?convertKeyTable[o.key]||(o.code.startsWith("Key")?o.code[3].toLowerCase():null)||o.key:convertKeyTable[o.key]||o.key}function convertKeyEvent(o){return modifierKeys.indexOf(o.key)!==-1?null:{key:getKey(o),ctrl:o.ctrlKey,meta:o.altKey,super:o.metaKey,shift:o.shiftKey}}var defs=[[0,31,"N"],[32,126,"Na"],[127,160,"N"],[161,161,"A"],[162,163,"Na"],[164,164,"A"],[165,166,"Na"],[167,168,"A"],[169,169,"N"],[170,170,"A"],[171,171,"N"],[172,172,"Na"],[173,174,"A"],[175,175,"Na"],[176,180,"A"],[181,181,"N"],[182,186,"A"],[187,187,"N"],[188,191,"A"],[192,197,"N"],[198,198,"A"],[199,207,"N"],[208,208,"A"],[209,214,"N"],[215,216,"A"],[217,221,"N"],[222,225,"A"],[226,229,"N"],[230,230,"A"],[231,231,"N"],[232,234,"A"],[235,235,"N"],[236,237,"A"],[238,239,"N"],[240,240,"A"],[241,241,"N"],[242,243,"A"],[244,246,"N"],[247,250,"A"],[251,251,"N"],[252,252,"A"],[253,253,"N"],[254,254,"A"],[255,256,"N"],[257,257,"A"],[258,272,"N"],[273,273,"A"],[274,274,"N"],[275,275,"A"],[276,282,"N"],[283,283,"A"],[284,293,"N"],[294,295,"A"],[296,298,"N"],[299,299,"A"],[300,304,"N"],[305,307,"A"],[308,311,"N"],[312,312,"A"],[313,318,"N"],[319,322,"A"],[323,323,"N"],[324,324,"A"],[325,327,"N"],[328,331,"A"],[332,332,"N"],[333,333,"A"],[334,337,"N"],[338,339,"A"],[340,357,"N"],[358,359,"A"],[360,362,"N"],[363,363,"A"],[364,461,"N"],[462,462,"A"],[463,463,"N"],[464,464,"A"],[465,465,"N"],[466,466,"A"],[467,467,"N"],[468,468,"A"],[469,469,"N"],[470,470,"A"],[471,471,"N"],[472,472,"A"],[473,473,"N"],[474,474,"A"],[475,475,"N"],[476,476,"A"],[477,592,"N"],[593,593,"A"],[594,608,"N"],[609,609,"A"],[610,707,"N"],[708,708,"A"],[709,710,"N"],[711,711,"A"],[712,712,"N"],[713,715,"A"],[716,716,"N"],[717,717,"A"],[718,719,"N"],[720,720,"A"],[721,727,"N"],[728,731,"A"],[732,732,"N"],[733,733,"A"],[734,734,"N"],[735,735,"A"],[736,767,"N"],[768,879,"A"],[880,912,"N"],[913,929,"A"],[930,930,"N"],[931,937,"A"],[938,944,"N"],[945,961,"A"],[962,962,"N"],[963,969,"A"],[970,1024,"N"],[1025,1025,"A"],[1026,1039,"N"],[1040,1103,"A"],[1104,1104,"N"],[1105,1105,"A"],[1106,4351,"N"],[4352,4447,"W"],[4448,8207,"N"],[8208,8208,"A"],[8209,8210,"N"],[8211,8214,"A"],[8215,8215,"N"],[8216,8217,"A"],[8218,8219,"N"],[8220,8221,"A"],[8222,8223,"N"],[8224,8226,"A"],[8227,8227,"N"],[8228,8231,"A"],[8232,8239,"N"],[8240,8240,"A"],[8241,8241,"N"],[8242,8243,"A"],[8244,8244,"N"],[8245,8245,"A"],[8246,8250,"N"],[8251,8251,"A"],[8252,8253,"N"],[8254,8254,"A"],[8255,8307,"N"],[8308,8308,"A"],[8309,8318,"N"],[8319,8319,"A"],[8320,8320,"N"],[8321,8324,"A"],[8325,8360,"N"],[8361,8361,"H"],[8362,8363,"N"],[8364,8364,"A"],[8365,8450,"N"],[8451,8451,"A"],[8452,8452,"N"],[8453,8453,"A"],[8454,8456,"N"],[8457,8457,"A"],[8458,8466,"N"],[8467,8467,"A"],[8468,8469,"N"],[8470,8470,"A"],[8471,8480,"N"],[8481,8482,"A"],[8483,8485,"N"],[8486,8486,"A"],[8487,8490,"N"],[8491,8491,"A"],[8492,8530,"N"],[8531,8532,"A"],[8533,8538,"N"],[8539,8542,"A"],[8543,8543,"N"],[8544,8555,"A"],[8556,8559,"N"],[8560,8569,"A"],[8570,8584,"N"],[8585,8585,"A"],[8586,8591,"N"],[8592,8601,"A"],[8602,8631,"N"],[8632,8633,"A"],[8634,8657,"N"],[8658,8658,"A"],[8659,8659,"N"],[8660,8660,"A"],[8661,8678,"N"],[8679,8679,"A"],[8680,8703,"N"],[8704,8704,"A"],[8705,8705,"N"],[8706,8707,"A"],[8708,8710,"N"],[8711,8712,"A"],[8713,8714,"N"],[8715,8715,"A"],[8716,8718,"N"],[8719,8719,"A"],[8720,8720,"N"],[8721,8721,"A"],[8722,8724,"N"],[8725,8725,"A"],[8726,8729,"N"],[8730,8730,"A"],[8731,8732,"N"],[8733,8736,"A"],[8737,8738,"N"],[8739,8739,"A"],[8740,8740,"N"],[8741,8741,"A"],[8742,8742,"N"],[8743,8748,"A"],[8749,8749,"N"],[8750,8750,"A"],[8751,8755,"N"],[8756,8759,"A"],[8760,8763,"N"],[8764,8765,"A"],[8766,8775,"N"],[8776,8776,"A"],[8777,8779,"N"],[8780,8780,"A"],[8781,8785,"N"],[8786,8786,"A"],[8787,8799,"N"],[8800,8801,"A"],[8802,8803,"N"],[8804,8807,"A"],[8808,8809,"N"],[8810,8811,"A"],[8812,8813,"N"],[8814,8815,"A"],[8816,8833,"N"],[8834,8835,"A"],[8836,8837,"N"],[8838,8839,"A"],[8840,8852,"N"],[8853,8853,"A"],[8854,8856,"N"],[8857,8857,"A"],[8858,8868,"N"],[8869,8869,"A"],[8870,8894,"N"],[8895,8895,"A"],[8896,8977,"N"],[8978,8978,"A"],[8979,8985,"N"],[8986,8987,"W"],[8988,9e3,"N"],[9001,9002,"W"],[9003,9192,"N"],[9193,9196,"W"],[9197,9199,"N"],[9200,9200,"W"],[9201,9202,"N"],[9203,9203,"W"],[9204,9311,"N"],[9312,9449,"A"],[9450,9450,"N"],[9451,9547,"A"],[9548,9551,"N"],[9552,9587,"A"],[9588,9599,"N"],[9600,9615,"A"],[9616,9617,"N"],[9618,9621,"A"],[9622,9631,"N"],[9632,9633,"A"],[9634,9634,"N"],[9635,9641,"A"],[9642,9649,"N"],[9650,9651,"A"],[9652,9653,"N"],[9654,9655,"A"],[9656,9659,"N"],[9660,9661,"A"],[9662,9663,"N"],[9664,9665,"A"],[9666,9669,"N"],[9670,9672,"A"],[9673,9674,"N"],[9675,9675,"A"],[9676,9677,"N"],[9678,9681,"A"],[9682,9697,"N"],[9698,9701,"A"],[9702,9710,"N"],[9711,9711,"A"],[9712,9724,"N"],[9725,9726,"W"],[9727,9732,"N"],[9733,9734,"A"],[9735,9736,"N"],[9737,9737,"A"],[9738,9741,"N"],[9742,9743,"A"],[9744,9747,"N"],[9748,9749,"W"],[9750,9755,"N"],[9756,9756,"A"],[9757,9757,"N"],[9758,9758,"A"],[9759,9791,"N"],[9792,9792,"A"],[9793,9793,"N"],[9794,9794,"A"],[9795,9799,"N"],[9800,9811,"W"],[9812,9823,"N"],[9824,9825,"A"],[9826,9826,"N"],[9827,9829,"A"],[9830,9830,"N"],[9831,9834,"A"],[9835,9835,"N"],[9836,9837,"A"],[9838,9838,"N"],[9839,9839,"A"],[9840,9854,"N"],[9855,9855,"W"],[9856,9874,"N"],[9875,9875,"W"],[9876,9885,"N"],[9886,9887,"A"],[9888,9888,"N"],[9889,9889,"W"],[9890,9897,"N"],[9898,9899,"W"],[9900,9916,"N"],[9917,9918,"W"],[9919,9919,"A"],[9920,9923,"N"],[9924,9925,"W"],[9926,9933,"A"],[9934,9934,"W"],[9935,9939,"A"],[9940,9940,"W"],[9941,9953,"A"],[9954,9954,"N"],[9955,9955,"A"],[9956,9959,"N"],[9960,9961,"A"],[9962,9962,"W"],[9963,9969,"A"],[9970,9971,"W"],[9972,9972,"A"],[9973,9973,"W"],[9974,9977,"A"],[9978,9978,"W"],[9979,9980,"A"],[9981,9981,"W"],[9982,9983,"A"],[9984,9988,"N"],[9989,9989,"W"],[9990,9993,"N"],[9994,9995,"W"],[9996,10023,"N"],[10024,10024,"W"],[10025,10044,"N"],[10045,10045,"A"],[10046,10059,"N"],[10060,10060,"W"],[10061,10061,"N"],[10062,10062,"W"],[10063,10066,"N"],[10067,10069,"W"],[10070,10070,"N"],[10071,10071,"W"],[10072,10101,"N"],[10102,10111,"A"],[10112,10132,"N"],[10133,10135,"W"],[10136,10159,"N"],[10160,10160,"W"],[10161,10174,"N"],[10175,10175,"W"],[10176,10213,"N"],[10214,10221,"Na"],[10222,10628,"N"],[10629,10630,"Na"],[10631,11034,"N"],[11035,11036,"W"],[11037,11087,"N"],[11088,11088,"W"],[11089,11092,"N"],[11093,11093,"W"],[11094,11097,"A"],[11098,11903,"N"],[11904,11929,"W"],[11930,11930,"N"],[11931,12019,"W"],[12020,12031,"N"],[12032,12245,"W"],[12246,12271,"N"],[12272,12287,"W"],[12288,12288,"F"],[12289,12350,"W"],[12351,12352,"N"],[12353,12438,"W"],[12439,12440,"N"],[12441,12543,"W"],[12544,12548,"N"],[12549,12591,"W"],[12592,12592,"N"],[12593,12686,"W"],[12687,12687,"N"],[12688,12771,"W"],[12772,12782,"N"],[12783,12830,"W"],[12831,12831,"N"],[12832,12871,"W"],[12872,12879,"A"],[12880,19903,"W"],[19904,19967,"N"],[19968,42124,"W"],[42125,42127,"N"],[42128,42182,"W"],[42183,43359,"N"],[43360,43388,"W"],[43389,44031,"N"],[44032,55203,"W"],[55204,57343,"N"],[57344,63743,"A"],[63744,64255,"W"],[64256,65023,"N"],[65024,65039,"A"],[65040,65049,"W"],[65050,65071,"N"],[65072,65106,"W"],[65107,65107,"N"],[65108,65126,"W"],[65127,65127,"N"],[65128,65131,"W"],[65132,65280,"N"],[65281,65376,"F"],[65377,65470,"H"],[65471,65473,"N"],[65474,65479,"H"],[65480,65481,"N"],[65482,65487,"H"],[65488,65489,"N"],[65490,65495,"H"],[65496,65497,"N"],[65498,65500,"H"],[65501,65503,"N"],[65504,65510,"F"],[65511,65511,"N"],[65512,65518,"H"],[65519,65532,"N"],[65533,65533,"A"],[65534,94175,"N"],[94176,94180,"W"],[94181,94191,"N"],[94192,94193,"W"],[94194,94207,"N"],[94208,100343,"W"],[100344,100351,"N"],[100352,101589,"W"],[101590,101631,"N"],[101632,101640,"W"],[101641,110575,"N"],[110576,110579,"W"],[110580,110580,"N"],[110581,110587,"W"],[110588,110588,"N"],[110589,110590,"W"],[110591,110591,"N"],[110592,110882,"W"],[110883,110897,"N"],[110898,110898,"W"],[110899,110927,"N"],[110928,110930,"W"],[110931,110932,"N"],[110933,110933,"W"],[110934,110947,"N"],[110948,110951,"W"],[110952,110959,"N"],[110960,111355,"W"],[111356,126979,"N"],[126980,126980,"W"],[126981,127182,"N"],[127183,127183,"W"],[127184,127231,"N"],[127232,127242,"A"],[127243,127247,"N"],[127248,127277,"A"],[127278,127279,"N"],[127280,127337,"A"],[127338,127343,"N"],[127344,127373,"A"],[127374,127374,"W"],[127375,127376,"A"],[127377,127386,"W"],[127387,127404,"A"],[127405,127487,"N"],[127488,127490,"W"],[127491,127503,"N"],[127504,127547,"W"],[127548,127551,"N"],[127552,127560,"W"],[127561,127567,"N"],[127568,127569,"W"],[127570,127583,"N"],[127584,127589,"W"],[127590,127743,"N"],[127744,127776,"W"],[127777,127788,"N"],[127789,127797,"W"],[127798,127798,"N"],[127799,127868,"W"],[127869,127869,"N"],[127870,127891,"W"],[127892,127903,"N"],[127904,127946,"W"],[127947,127950,"N"],[127951,127955,"W"],[127956,127967,"N"],[127968,127984,"W"],[127985,127987,"N"],[127988,127988,"W"],[127989,127991,"N"],[127992,128062,"W"],[128063,128063,"N"],[128064,128064,"W"],[128065,128065,"N"],[128066,128252,"W"],[128253,128254,"N"],[128255,128317,"W"],[128318,128330,"N"],[128331,128334,"W"],[128335,128335,"N"],[128336,128359,"W"],[128360,128377,"N"],[128378,128378,"W"],[128379,128404,"N"],[128405,128406,"W"],[128407,128419,"N"],[128420,128420,"W"],[128421,128506,"N"],[128507,128591,"W"],[128592,128639,"N"],[128640,128709,"W"],[128710,128715,"N"],[128716,128716,"W"],[128717,128719,"N"],[128720,128722,"W"],[128723,128724,"N"],[128725,128727,"W"],[128728,128731,"N"],[128732,128735,"W"],[128736,128746,"N"],[128747,128748,"W"],[128749,128755,"N"],[128756,128764,"W"],[128765,128991,"N"],[128992,129003,"W"],[129004,129007,"N"],[129008,129008,"W"],[129009,129291,"N"],[129292,129338,"W"],[129339,129339,"N"],[129340,129349,"W"],[129350,129350,"N"],[129351,129535,"W"],[129536,129647,"N"],[129648,129660,"W"],[129661,129663,"N"],[129664,129672,"W"],[129673,129679,"N"],[129680,129725,"W"],[129726,129726,"N"],[129727,129733,"W"],[129734,129741,"N"],[129742,129755,"W"],[129756,129759,"N"],[129760,129768,"W"],[129769,129775,"N"],[129776,129784,"W"],[129785,131071,"N"],[131072,196605,"W"],[196606,196607,"N"],[196608,262141,"W"],[262142,917759,"N"],[917760,917999,"A"],[918e3,983039,"N"],[983040,1048573,"A"],[1048574,1048575,"N"],[1048576,1114109,"A"],[1114110,1114111,"N"]];function getEAWOfCodePoint(o){let e=0,t=defs.length-1;for(;e!==t;){const i=e+(t-e>>1),[n,s,r]=defs[i];if(os)e=i+1;else return r}return defs[e][2]}function getEAW(o,e=0){const t=o.codePointAt(e);if(t!==void 0)return getEAWOfCodePoint(t)}const textOffsetY=5,isSafari=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);function isWideChar(o){switch(getEAW(o)){case"F":case"W":return!0;case"A":default:return!1}}function isMacOS(){return window.navigator.userAgent.indexOf("Mac OS X")!==-1}function computeFontSize(o){const t=document.createElement("canvas").getContext("2d");t.font=o;const i=t.measureText("W");return[Math.floor(i.width),Math.round(i.fontBoundingBoxAscent+textOffsetY+(i.emHeightDescent||0))]}function drawBlock({ctx:o,x:e,y:t,width:i,height:n,style:s}){o.fillStyle=s,o.fillRect(e,t,i,n)}function drawText({ctx:o,x:e,y:t,text:i,font:n,style:s,option:r}){t+=Math.round(textOffsetY),o.fillStyle=s,o.font=n,o.textBaseline="top";for(const l of i)isWideChar(l)?(o.fillText(l,e,t,r.fontWidth*2),e+=r.fontWidth*2):(o.fillText(l,e,t,r.fontWidth),e+=r.fontWidth)}function drawHorizontalLine({ctx:o,x:e,y:t,width:i,style:n,lineWidth:s=1}){o.strokeStyle=n,o.lineWidth=s,o.setLineDash=[],o.beginPath(),o.moveTo(e,t),o.lineTo(e+i,t),o.stroke()}class Option{constructor({fontName:e,fontSize:t}){this.setFont(e,t),this.foreground="#cccccc",this.background="#2d2d2d"}setFont(e,t){const i=t+"px "+e,[n,s]=computeFontSize(i);this.fontName=e,this.fontSize=t,this.fontWidth=n,this.fontHeight=s,this.font=i}}function getLemEditorElement(){return document.getElementById("lem-editor")}function normalizeWheelDelta(o,e,t,i){switch(t){case 0:return{dx:o/i,dy:e/i};case 2:return{dx:o*20,dy:e*20};default:return{dx:o,dy:e}}}function extractWholeLines(o,e){const t=Math.trunc(o),i=Math.trunc(e);return{scrollX:t,scrollY:i,remainderX:o-t,remainderY:e-i}}function cursorPosition(o,e){const[t,i]=e.getDisplayRectangle(),n=o.clientX-t,s=o.clientY-i;return{pixelX:n,pixelY:s,x:Math.floor(n/e.option.fontWidth),y:Math.floor(s/e.option.fontHeight)}}function makeWheelHandler(o){let e={x:0,y:0},t=!1,i={pixelX:0,pixelY:0,x:0,y:0};return n=>{n.preventDefault(),i=cursorPosition(n,o);const{dx:s,dy:r}=normalizeWheelDelta(n.deltaX,n.deltaY,n.deltaMode,o.option.fontHeight);e={x:e.x+s,y:e.y+r},t||(t=!0,requestAnimationFrame(()=>{t=!1;const{scrollX:l,scrollY:f,remainderX:d,remainderY:y}=extractWholeLines(e.x,e.y);e={x:d,y},(l!==0||f!==0)&&o.jsonrpc.notify("input",{kind:"wheel",value:{...i,wheelX:-l,wheelY:-f}})}))}}function addMouseEventListeners({dom:o,editor:e,isDraggable:t,draggableStyle:i}){o.addEventListener("contextmenu",r=>{r.preventDefault()});const n=(r,l)=>{r.preventDefault();const[f,d]=e.getDisplayRectangle(),y=r.clientX-f,v=r.clientY-d,N=Math.floor(y/e.option.fontWidth),u=Math.floor(v/e.option.fontHeight);e.jsonrpc.notify("input",{kind:l,value:{x:N,y:u,pixelX:y,pixelY:v,button:r.button,clicks:r.detail}})};o.addEventListener("mousedown",r=>{t&&(document.body.style.cursor=i),e.focusHiddenInput(),n(r,"mousedown")}),o.addEventListener("mouseup",r=>{t&&(document.body.style.cursor="default"),n(r,"mouseup")});let s=0;o.addEventListener("mousemove",r=>{r.preventDefault();const l=Date.now();if(l-s>50){s=l;const[f,d]=e.getDisplayRectangle(),y=r.clientX-f,v=r.clientY-d,N=Math.floor(y/e.option.fontWidth),u=Math.floor(v/e.option.fontHeight);e.jsonrpc.notify("input",{kind:"mousemove",value:{x:N,y:u,pixelX:y,pixelY:v,button:r.buttons===0?null:r.buttons-1}})}}),t&&(o.addEventListener("mouseover",()=>{document.body.style.cursor=i}),o.addEventListener("mouseout",r=>{r.buttons!==1&&(document.body.style.cursor="default")})),o.addEventListener("wheel",makeWheelHandler(e))}const zIndexTable={"floating-window":200,modeline:100,"vertical-border":100,"horizontal-border":101};function zindex(o){return zIndexTable[o]||0}const borderOffsetX=5,borderOffsetY=10;class BaseSurface{constructor({editor:o}){this.editor=o,this.mainDOM=null,this.wrapper=null}delete(){this.wrapper?getLemEditorElement().removeChild(this.wrapper):getLemEditorElement().removeChild(this.mainDOM)}setupDOM({dom:o,isFloating:e,border:t,cssClassName:i}){this.mainDOM=o,e&&t?(this.wrapper=document.createElement("div"),i&&(this.wrapper.className=i),this.wrapper.style.position="absolute",this.wrapper.style.backgroundColor=this.editor.option.background,this.wrapper.style.zIndex=zindex("floating-window"),this.wrapper.appendChild(o),getLemEditorElement().appendChild(this.wrapper)):(i&&(o.className=i),getLemEditorElement().appendChild(o))}move(o,e,t,i){const[n,s]=this.editor.getDisplayRectangle(),r=t!=null?Math.floor(n+t):Math.floor(n+o*this.editor.option.fontWidth),l=i!=null?Math.floor(s+i):Math.floor(s+e*this.editor.option.fontHeight);this.wrapper?(this.wrapper.style.left=r-borderOffsetX+"px",this.wrapper.style.top=l-borderOffsetY+"px",this.mainDOM.style.left=borderOffsetX+"px",this.mainDOM.style.top=borderOffsetY+"px"):(this.mainDOM.style.left=r+"px",this.mainDOM.style.top=l+"px")}_resize(o,e,t,i){const n=window.devicePixelRatio||1,s=t??o*this.editor.option.fontWidth,r=i??e*this.editor.option.fontHeight;this.mainDOM.width=s*n,this.mainDOM.height=r*n,this.mainDOM.style.width=s+"px",this.mainDOM.style.height=r+"px",this.wrapper&&(this.wrapper.style.width=s+borderOffsetX*2+"px",this.wrapper.style.height=r+borderOffsetY*2+"px")}drawBlock(o,e,t,i,n){}drawText(o,e,t,i,n){}touch(){}evalIn(code){return eval(code)}}class CanvasSurface extends BaseSurface{constructor({editor:e,view:t,x:i,y:n,width:s,height:r,styles:l,isFloating:f,border:d,cssClassName:y}){super({editor:e});const v=this.setupCanvas(l);this.setupDOM({dom:v,isFloating:f,border:d,cssClassName:y}),this.move(i,n),this.resize(s,r),this.drawingQueue=[],addMouseEventListeners({dom:v,editor:e})}setupCanvas(e){const t=document.createElement("canvas");if(t.style.position="absolute",e)for(let i in e)t.style[i]=e[i];return t}resize(e,t,i,n){this._resize(e,t,i,n);const s=window.devicePixelRatio||1;this.mainDOM.getContext("2d").scale(s,s)}drawBlock(e,t,i,n,s){const r=this.editor.option;this.drawingQueue.push(function(l){drawBlock({ctx:l,x:e*r.fontWidth,y:t*r.fontHeight,width:i*r.fontWidth,height:n*r.fontHeight,style:s})})}drawText(e,t,i,n,s,r){const l=this.editor.option;this.drawingQueue.push(function(f){if(r=r?`${l.fontSize}px ${r}`:l.font,!s)drawBlock({ctx:f,x:e*l.fontWidth,y:t*l.fontHeight,width:n*l.fontWidth,height:l.fontHeight,style:l.background}),drawText({ctx:f,x:e*l.fontWidth,y:t*l.fontHeight,text:i,style:l.foreground,font:r,option:l});else{let{foreground:d,background:y,bold:v,reverse:N,underline:u,cursor:h}=s;if(d||(d=l.foreground),y||(y=l.background),N){const p=y;y=d,d=p}h&&(y=l.background);const a=e*l.fontWidth,c=t*l.fontHeight;drawBlock({ctx:f,x:a,y:c,width:n*l.fontWidth,height:l.fontHeight,style:y}),drawText({ctx:f,x:a,y:c,text:i,style:d,font:v?"bold "+r:r,option:l}),u&&drawHorizontalLine({ctx:f,x:a,y:c+l.fontHeight-2,width:n*l.fontWidth,style:typeof u=="string"?u:d,lineWidth:2})}})}touch(){const e=this.mainDOM.getContext("2d");for(let t of this.drawingQueue)t(e);this.drawingQueue=[]}activate(){this.mainDOM.dataset.store="active"}deactivate(){this.mainDOM.dataset.store="inactive"}}class HTMLSurface extends BaseSurface{constructor({editor:e,x:t,y:i,width:n,height:s,styles:r,option:l,isFloating:f,border:d,html:y}){super({editor:e});const v=document.createElement("iframe");this.setupDOM({dom:v,isFloating:f,border:d}),v.style.position="absolute",v.style.backgroundColor=l.background,v.setAttribute("sandbox","allow-scripts allow-same-origin"),v.srcdoc=y,v.addEventListener("load",()=>{const N=v.contentWindow;N.invokeLem=(u,h)=>parent.postMessage({type:"invoke-lem",method:u,args:h})}),this.iframe=v,this.move(t,i),this.resize(n,s)}resize(e,t,i,n){this._resize(e,t,i,n)}update(e){const t=this.iframe.contentWindow.scrollY;this.iframe.srcdoc=e,this.iframe.onload=()=>{this.iframe.onload=null,this.iframe.contentWindow.scrollTo(0,t)}}evalIn(e){return this.iframe.contentWindow.eval(e)}}class VerticalBorder{constructor({x:e,y:t,height:i,option:n,editor:s}){this.option=n,this.editor=s,this.line=document.createElement("div"),this.line.className="lem-editor__vertical-border",this.line.style.height=i*n.fontHeight+"px",this.line.style.position="absolute",this.line.style.zIndex=zindex("vertical-border"),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:s,isDraggable:!0,draggableStyle:"col-resize"})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){const[i,n]=this.editor.getDisplayRectangle();this.line.style.left=Math.floor(i+e*this.option.fontWidth-this.option.fontWidth/2)+"px",this.line.style.top=n+t*this.option.fontHeight+"px"}resize(e){this.line.style.height=e*this.option.fontHeight+"px"}}class HorizontalBorder{constructor({x:e,y:t,width:i,option:n,editor:s}){this.option=n,this.editor=s,this.line=document.createElement("div"),this.line.className="lem-editor__horizontal-border",this.line.style.width=i*n.fontWidth+"px",this.line.style.position="absolute",this.line.style.zIndex=zindex("horizontal-border"),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:s,isDraggable:!0,draggableStyle:"row-resize"})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){const[i,n]=this.editor.getDisplayRectangle();this.line.style.left=i+e*this.option.fontWidth+"px",this.line.style.top=Math.floor(n+t*this.option.fontHeight-4)+"px"}resize(e){this.line.style.width=e*this.option.fontWidth+"px"}}const viewStyles={header:()=>{},tile:()=>{},floating:o=>({boxSizing:"border-box",borderColor:o.foreground,backgroundColor:o.background})};function getViewStyle(o,e){return viewStyles[o](e)||{}}class View{constructor({id:e,x:t,y:i,width:n,height:s,pixelX:r,pixelY:l,pixelWidth:f,pixelHeight:d,useModeline:y,kind:v,type:N,content:u,border:h,borderShape:a,option:c,editor:p}){switch(this.option=c,this.id=e,this.x=t,this.y=i,this.width=n,this.height=s,this.pixelX=r,this.pixelY=l,this.pixelWidth=f,this.pixelHeight=d,this.useModeline=y,this.kind=v,this.type=N,this.border=h,this.borderShape=a,this.editor=p,this.bottomBar=null,this.leftsideBar=null,v){case"tile":this.mainSurface=this.makeSurface(N,u),this.leftSideBar=new VerticalBorder({x:t,y:i,height:s+(y?1:0),option:c,editor:p}),y||(this.bottomBar=new HorizontalBorder({x:t,y:i+s-1,width:n,option:c,editor:p}));break;case"header":this.mainSurface=this.makeSurface(N,u);break;case"floating":this.mainSurface=this.makeSurface(N,u),a==="left-border"&&(this.leftSideBar=new VerticalBorder({x:t,y:i,height:s,option:c,editor:p}));break}this.modelineSurface=y?this.makeModelineSurface():null,v==="floating"&&(r!=null||l!=null)&&this.move(t,i,r,l)}delete(){this.mainSurface.delete(),this.modelineSurface&&this.modelineSurface.delete(),this.leftSideBar&&this.leftSideBar.delete(),this.bottomBar&&this.bottomBar.delete()}move(e,t,i,n){if(this.x=e,this.y=t,this.pixelX=i,this.pixelY=n,this.mainSurface.move(e,t,i,n),this.modelineSurface){const s=n!=null&&this.pixelHeight!=null?n+this.pixelHeight:null;this.modelineSurface.move(e,t+this.height,i,s)}this.leftSideBar&&this.leftSideBar.move(e,t),this.bottomBar&&this.bottomBar.move(e,t+this.height)}resize(e,t,i,n){if(this.width=e,this.height=t,this.pixelWidth=i,this.pixelHeight=n,this.mainSurface.resize(e,t,i,n),this.modelineSurface){const s=this.pixelY!=null&&n!=null?this.pixelY+n:null;this.modelineSurface.move(this.x,this.y+this.height,this.pixelX,s),this.modelineSurface.resize(e,1)}this.leftSideBar&&this.leftSideBar.resize(t+(this.modelineSurface?1:0)),this.bottomBar&&this.bottomBar.resize(e)}clear(){this.mainSurface.drawBlock(0,0,this.width,this.height,this.option.background)}clearEol(e,t){this.mainSurface.drawBlock(e,t,this.width-e,1,this.option.background)}clearEob(e,t){this.mainSurface.drawBlock(e,t,this.width,this.height-t,this.option.background)}print(e,t,i,n,s,r){this.mainSurface.drawText(e,t,i,n,s,r)}printToModeline(e,t,i,n,s){this.modelineSurface&&this.modelineSurface.drawText(e,t,i,n,s)}touch(e){this.mainSurface.touch(),this.modelineSurface&&(this.modelineSurface.touch(),e?this.modelineSurface.activate():this.modelineSurface.deactivate())}makeSurface(e,t){switch(e){case"html":return this.makeHTMLSurface(t);case"editor":return this.makeEditorSurface();default:console.error(`unknown type: ${e}`)}}makeHTMLSurface(e){return new HTMLSurface({editor:this.editor,x:this.x,y:this.y,width:this.width,height:this.height,styles:getViewStyle(this.kind,this.option),option:this.option,isFloating:this.kind==="floating",border:this.border,html:e})}makeEditorSurface(){const e=this.borderShape==="left-border"?0:this.border,t=this.kind==="floating";return new CanvasSurface({option:this.editor.option,x:this.x,y:this.y,width:this.width,height:this.height,styles:getViewStyle(this.kind,this.option),editor:this.editor,border:e,isFloating:t,view:this,cssClassName:t&&e?"lem-editor__floating-window--bordered":null})}makeModelineSurface(){const e=new CanvasSurface({option:this.editor.option,x:this.x,y:this.y+this.height,width:this.width,height:1,editor:this.editor,view:this,styles:{zIndex:zindex("modeline")},cssClassName:"lem-editor__mode-line"});return addMouseEventListeners({dom:e.mainDOM,editor:this.editor,isDraggable:!0,draggableStyle:"row-resize"}),e}changeToHTMLContent(e){this.mainSurface.constructor.name==="HTMLSurface"?this.mainSurface.update(e):(this.mainSurface.delete(),this.mainSurface=this.makeHTMLSurface(e))}changeToEditorContent(){this.mainSurface.delete(),this.mainSurface=this.makeEditorSurface()}evalIn(e){return this.mainSurface.evalIn(e)}}function isPasteKeyEvent(o){return isMacOS()?o.metaKey&&o.key==="v":o.ctrlKey&&o.shiftKey&&o.key==="V"}class Input{constructor(e){const t=e.option;this.editor=e,this.composition=!1,this.ignoreKeydownAfterCompositionend=!1,this.span=document.createElement("span"),this.span.style.color=t.foreground,this.span.style.backgroundColor=t.background,this.span.style.position="absolute",this.span.style.zIndex=1e6,this.span.style.top="0",this.span.style.left="0",this.span.style.font=t.font,this.input=document.createElement("input"),this.input.style.backgroundColor="transparent",this.input.style.color="transparent",this.input.style.width="0",this.input.style.padding="0",this.input.style.margin="0",this.input.style.border="none",this.input.style.position="absolute",this.input.style.zIndex="-10",this.input.style.top="0",this.input.style.left="0",this.input.style.font=t.font,this.input.addEventListener("blur",i=>{this.input.focus()}),this.input.addEventListener("input",i=>{this.composition===!1&&(this.input.value="",this.span.innerHTML="",this.input.style.width="0",isMacOS()||this.editor.emitInputString(i.data))}),this.input.addEventListener("paste",async i=>{i.preventDefault();const n=i.clipboardData||window.Clipboard.data,s=n?.getData("text")??n?.getData("text/plain");if(s&&s.length>0){this.editor.emitInputString(s);return}try{if(navigator.clipboard?.readText){const r=await navigator.clipboard.readText();if(r&&r.length>0){this.editor.emitInputString(r);return}}}catch(r){console.warn("clipboard.readText() failed:",r)}alert("Paste failed (permission/environment restriction")}),this.input.addEventListener("keydown",i=>{if(!isPasteKeyEvent(i)&&!(i.isComposing||this.composition)&&i.key!=="Process"){if(this.ignoreKeydownAfterCompositionend&&(isSafari||isMacOS())){i.preventDefault(),this.ignoreKeydownAfterCompositionend=!1;return}if(!(!isMacOS()&&!i.ctrlKey&&!i.altKey&&i.key.length===1)&&(i.preventDefault(),i.isComposing!==!0&&i.code!==""))return setTimeout(()=>{this.composition||(this.editor.emitInput(i),this.input.value="")},0),!1}}),this.input.addEventListener("compositionstart",i=>{this.composition=!0,this.span.innerHTML=this.input.value,this.input.style.width=this.span.offsetWidth+"px"}),this.input.addEventListener("compositionupdate",i=>{this.span.innerHTML=i.data,this.input.style.width=this.span.offsetWidth+"px"}),this.input.addEventListener("compositionend",i=>{this.composition=!1,this.editor.emitInputString(this.input.value),this.input.value="",this.span.innerHTML=this.input.value,this.input.style.width="0",this.ignoreKeydownAfterCompositionend=!0}),document.body.appendChild(this.input),document.body.appendChild(this.span),this.input.focus()}finalize(){document.body.removeChild(this.input),document.body.removeChild(this.span)}move(e,t){const[i,n]=this.editor.getDisplayRectangle();this.span.style.top=n+t+"px",this.span.style.left=i+e+"px",this.input.style.top=this.span.offsetTop+"px",this.input.style.left=this.span.offsetLeft+"px"}updateForeground(e){this.span.style.color=e}updateBackground(e){this.span.style.backgroundColor=e}}class MessageTable{constructor(){this.map=new Map}register(e,t){for(const i in t){const n=t[i];this.map.set(i,n),e.on(i,n)}}get(e){return this.map.get(e)}}function getDisplayRectangleDefault(){return[0,0,window.innerWidth,window.innerHeight]}class Editor{constructor({getDisplayRectangle:e=getDisplayRectangleDefault,fontName:t,fontSize:i,url:n,onExit:s,onClosed:r}){this.getDisplayRectangle=e,this.option=new Option({fontName:t,fontSize:i}),this.onExit=s,this.input=new Input(this),this.cursors=new Map,this.cursorOverlay=document.createElement("div"),this.cursorOverlay.className="lem-cursor",this.cursorOverlay.style.width=this.option.fontWidth+"px",this.cursorOverlay.style.height=this.option.fontHeight+"px",this.cursorOverlay.style.backgroundColor="#ffffff",this.cursorType="box",this.viewMap=new Map,this.jsonrpc=new JSONRPC(n,{onClosed:()=>{r()}}),this.messageTable=new MessageTable,this.messageTable.register(this.jsonrpc,{"update-foreground":this.updateForeground.bind(this),"update-background":this.updateBackground.bind(this),"make-view":this.makeView.bind(this),"delete-view":this.deleteView.bind(this),"resize-view":this.resize.bind(this),"move-view":this.move.bind(this),"redraw-view-after":this.redrawViewAfter.bind(this),clear:this.clear.bind(this),"clear-eol":this.clearEol.bind(this),"clear-eob":this.clearEob.bind(this),put:this.put.bind(this),"modeline-put":this.modelinePut.bind(this),"update-display":this.updateDisplay.bind(this),"move-cursor":this.moveCursor.bind(this),"change-view":this.changeView.bind(this),"resize-display":this.resizeDisplay.bind(this),bulk:this.bulk.bind(this),exit:this.exitEditor.bind(this),"get-clipboard-text":this.getClipboardText.bind(this),"set-clipboard-text":this.setClipboardText.bind(this),"js-eval":this.jsEval.bind(this),"set-font":this.setFont.bind(this),"get-font":this.getFont.bind(this),"get-display-size":this.getDisplaySize.bind(this),"load-css":this.loadCSS.bind(this),"update-cursor-shape":this.updateCursorShape.bind(this)}),this.login(),this.boundedHandleResize=this.handleResize.bind(this),this.focusHiddenInput=this.focusHiddenInput.bind(this)}init(){window.addEventListener("resize",this.boundedHandleResize),document.getElementsByTagName("html")[0].style["background-color"]="#333",getLemEditorElement().appendChild(this.cursorOverlay)}finalize(){window.removeEventListener("resize",this.boundedHandleResize),this.input.finalize(),this.cursorOverlay.parentNode&&this.cursorOverlay.parentNode.removeChild(this.cursorOverlay)}closeConnection(){this.jsonrpc.close()}emitInput(e){const t=convertKeyEvent(e);if(t){if(t.key==="]"&&t.ctrl&&!t.meta&&!t.super&&!t.shift){this.jsonrpc.notify("input",{kind:"abort"});return}t.key!=="Unidentified"&&this.jsonrpc.notify("input",{kind:"key",value:t})}}emitInputString(e){e?this.jsonrpc.notify("input",{kind:"input-string",value:e}):console.error("unexpected argument",e)}handleResize(e){this.jsonrpc.notify("redraw",{size:this.getDisplaySize()})}focusHiddenInput(){const e=this.input?.input;if(e){try{window.focus()}catch{}requestAnimationFrame(()=>{setTimeout(()=>{e.focus({preventScroll:!0})},0)})}}sendNotification(e,t){this.jsonrpc.notify(e,t)}request(e,t,i){this.jsonrpc.request(e,t,i)}getDisplaySize(){const[e,t,i,n]=this.getDisplayRectangle(),s=Math.floor(i/this.option.fontWidth),r=Math.floor(n/this.option.fontHeight);return{width:s,height:r}}callMessage(e,t){this.messageTable.get(e)(t)}findViewById(e){return this.viewMap.get(e)}login(){this.jsonrpc.request("login",{size:this.getDisplaySize(),foreground:this.option.foreground,background:this.option.background},e=>{if(this.updateForeground(e.foreground),this.updateBackground(e.background),e.views)for(const t of e.views)this.makeView(t);this.jsonrpc.notify("redraw",{size:this.getDisplaySize()})})}updateForeground(e){e!=null&&(this.option.foreground=e,this.input.updateForeground(e))}updateBackground(e){if(e==null)return;this.option.background=e,this.input.updateBackground(e);const t=getLemEditorElement();t.style.backgroundColor=e}makeView({id:e,x:t,y:i,width:n,height:s,pixelX:r,pixelY:l,pixelWidth:f,pixelHeight:d,use_modeline:y,kind:v,type:N,content:u,border:h,border_shape:a}){const c=new View({option:this.option,id:e,x:t,y:i,width:n,height:s,pixelX:r,pixelY:l,pixelWidth:f,pixelHeight:d,useModeline:y,kind:v,type:N,content:u,border:h,borderShape:a,editor:this});this.viewMap.set(e,c)}deleteView({viewInfo:{id:e}}){this.findViewById(e).delete(),this.viewMap.delete(e)}resize({viewInfo:{id:e},width:t,height:i,pixelWidth:n,pixelHeight:s}){const r=this.findViewById(e);r?r.resize(t,i,n,s):console.warn(`resize: view not found for id ${e}`)}move({viewInfo:{id:e},x:t,y:i,pixelX:n,pixelY:s}){const r=this.findViewById(e);r?r.move(t,i,n,s):console.warn(`move: view not found for id ${e}`)}redrawViewAfter({viewInfo:{id:e},isActive:t}){this.findViewById(e).touch(t)}clear({viewInfo:{id:e}}){this.findViewById(e).clear()}clearEol({viewInfo:{id:e},x:t,y:i}){this.findViewById(e).clearEol(t,i)}clearEob({viewInfo:{id:e},x:t,y:i}){this.findViewById(e).clearEob(t,i)}put({viewInfo:{id:e},x:t,y:i,text:n,textWidth:s,attribute:r,font:l}){this.findViewById(e).print(t,i,n,s,r,l)}modelinePut({viewInfo:{id:e},x:t,y:i,text:n,textWidth:s,attribute:r}){this.findViewById(e).printToModeline(t,i,n,s,r)}updateDisplay(){}moveCursor({viewInfo:{id:e},x:t,y:i,color:n,cursorText:s,cursorForeground:r}){const l=this.findViewById(e),[f,d]=this.getDisplayRectangle(),y=l.x*this.option.fontWidth+t*this.option.fontWidth,v=l.y*this.option.fontHeight+i*this.option.fontHeight;this.input.move(y,v);const N=n||this.option.foreground,u=r||this.option.background,h=this.cursorOverlay;switch(this.cursorType){case"bar":h.style.left=f+y+"px",h.style.top=d+v+"px",h.style.width="2px",h.style.height=this.option.fontHeight+"px",h.style.backgroundColor=N,h.textContent="",h.style.color="",h.style.font="",h.style.paddingTop="";break;case"underline":h.style.left=f+y+"px",h.style.top=d+v+this.option.fontHeight-2+"px",h.style.width=this.option.fontWidth+"px",h.style.height="2px",h.style.backgroundColor=N,h.textContent="",h.style.color="",h.style.font="",h.style.paddingTop="";break;case"box":default:h.style.left=f+y+"px",h.style.top=d+v+"px",h.style.width=this.option.fontWidth+"px",h.style.height=this.option.fontHeight+"px",h.style.backgroundColor=N,h.style.font=this.option.font,h.style.paddingTop=textOffsetY+"px",h.textContent=s||"",h.style.color=u;break}h.style.animation="none",h.offsetHeight,h.style.animation=""}updateCursorShape({cursorType:e}){this.cursorType=e||"box"}changeView({viewInfo:{id:e},type:t,content:i}){const n=this.findViewById(e);switch(t){case"html":n.changeToHTMLContent(i);break;case"editor":n.changeToEditorContent();break}}resizeDisplay({width:e,height:t}){const i=getLemEditorElement();i.style.width=Math.floor(e*this.option.fontWidth)+"px",i.style.height=Math.floor(t*this.option.fontHeight)+"px"}bulk(e){for(const{method:t,argument:i}of e)this.callMessage(t,i)}exitEditor(){this.onExit&&this.onExit()}getClipboardText(){navigator.clipboard?.readText().then(e=>{this.jsonrpc.notify("got-clipboard-text",{text:e})})}setClipboardText({text:e}){navigator.clipboard&&navigator.clipboard.writeText(e)}jsEval({viewInfo:{id:e},code:t}){const n=this.findViewById(e).evalIn(t);return n&&n.toString()}setFont({fontName:e,fontSize:t}){this.option.setFont(e||this.option.fontName,t||this.option.fontSize)}getFont(){return{name:this.option.fontName,size:this.option.fontSize}}loadCSS({content:e}){const t=document.createElement("style");t.textContent=e,document.head.appendChild(t)}notifyToServer(e,t){this.jsonrpc.notify("invoke",{method:e,args:t})}}const canvas=document.querySelector("#editor");async function main(){await Promise.all([document.fonts.load("19px file-icons"),document.fonts.load("19px AllTheIcons"),document.fonts.load("19px fontawesome"),document.fonts.load("19px material-design-icons"),document.fonts.load("19px octicons")]),await document.fonts.ready;const o=window.location.protocol==="https:"?"wss":"ws",e=new Editor({canvas,fontName:"Monospace",fontSize:18,url:`${o}://${window.location.hostname}:${window.location.port}`,onExit:null,onClosed:null});window.addEventListener("message",t=>{t.data.type==="invoke-lem"&&e.notifyToServer(t.data.method,t.data.args)}),e.init()}main(); +var __defProp=Object.defineProperty,__commonJSMin=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),__exportAll=(e,t)=>{let n={};for(var r in e)__defProp(n,r,{get:e[r],enumerable:!0});return t||__defProp(n,Symbol.toStringTag,{value:`Module`}),n};(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var require_models=__commonJSMin((e=>{var t=e&&e.__extends||(function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};return function(t,n){if(typeof n!=`function`&&n!==null)throw TypeError(`Class extends value `+String(n)+` is not a constructor or null`);e(t,n);function r(){this.constructor=t}t.prototype=n===null?Object.create(n):(r.prototype=n.prototype,new r)}})();Object.defineProperty(e,"__esModule",{value:!0}),e.createJSONRPCNotification=e.createJSONRPCRequest=e.createJSONRPCSuccessResponse=e.createJSONRPCErrorResponse=e.JSONRPCErrorCode=e.JSONRPCErrorException=e.isJSONRPCResponses=e.isJSONRPCResponse=e.isJSONRPCRequests=e.isJSONRPCRequest=e.isJSONRPCID=e.JSONRPC=void 0,e.JSONRPC=`2.0`,e.isJSONRPCID=function(e){return typeof e==`string`||typeof e==`number`||e===null},e.isJSONRPCRequest=function(t){return t.jsonrpc===e.JSONRPC&&t.method!==void 0&&t.result===void 0&&t.error===void 0},e.isJSONRPCRequests=function(t){return Array.isArray(t)&&t.every(e.isJSONRPCRequest)},e.isJSONRPCResponse=function(t){return t.jsonrpc===e.JSONRPC&&t.id!==void 0&&(t.result!==void 0||t.error!==void 0)},e.isJSONRPCResponses=function(t){return Array.isArray(t)&&t.every(e.isJSONRPCResponse)};var n=function(e,t,n){var r={code:e,message:t};return n!=null&&(r.data=n),r};e.JSONRPCErrorException=function(e){t(r,e);function r(t,n,i){var a=e.call(this,t)||this;return Object.setPrototypeOf(a,r.prototype),a.code=n,a.data=i,a}return r.prototype.toObject=function(){return n(this.code,this.message,this.data)},r}(Error),(function(e){e[e.ParseError=-32700]=`ParseError`,e[e.InvalidRequest=-32600]=`InvalidRequest`,e[e.MethodNotFound=-32601]=`MethodNotFound`,e[e.InvalidParams=-32602]=`InvalidParams`,e[e.InternalError=-32603]=`InternalError`})(e.JSONRPCErrorCode||={}),e.createJSONRPCErrorResponse=function(t,r,i,a){return{jsonrpc:e.JSONRPC,id:t,error:n(r,i,a)}},e.createJSONRPCSuccessResponse=function(t,n){return{jsonrpc:e.JSONRPC,id:t,result:n??null}},e.createJSONRPCRequest=function(t,n,r){return{jsonrpc:e.JSONRPC,id:t,method:n,params:r}},e.createJSONRPCNotification=function(t,n){return{jsonrpc:e.JSONRPC,method:t,params:n}}})),require_internal=__commonJSMin((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.DefaultErrorCode=void 0,e.DefaultErrorCode=0})),require_client=__commonJSMin((e=>{var t=e&&e.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||=Promise)(function(n,a){function o(e){try{c(r.next(e))}catch(e){a(e)}}function s(e){try{c(r.throw(e))}catch(e){a(e)}}function c(e){e.done?n(e.value):i(e.value).then(o,s)}c((r=r.apply(e,t||[])).next())})},n=e&&e.__generator||function(e,t){var n={label:0,sent:function(){if(a[0]&1)throw a[1];return a[1]},trys:[],ops:[]},r,i,a,o;return o={next:s(0),throw:s(1),return:s(2)},typeof Symbol==`function`&&(o[Symbol.iterator]=function(){return this}),o;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw TypeError(`Generator is already executing.`);for(;o&&(o=0,s[0]&&(n=0)),n;)try{if(r=1,i&&(a=s[0]&2?i.return:s[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,s[1])).done)return a;switch(i=0,a&&(s=[s[0]&2,a.value]),s[0]){case 0:case 1:a=s;break;case 4:return n.label++,{value:s[1],done:!1};case 5:n.label++,i=s[1],s=[0];continue;case 7:s=n.ops.pop(),n.trys.pop();continue;default:if((a=n.trys,!(a=a.length>0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{Object.defineProperty(e,"__esModule",{value:!0})})),require_server=__commonJSMin((e=>{var t=e&&e.__assign||function(){return t=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{var t=e&&e.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||=Promise)(function(n,a){function o(e){try{c(r.next(e))}catch(e){a(e)}}function s(e){try{c(r.throw(e))}catch(e){a(e)}}function c(e){e.done?n(e.value):i(e.value).then(o,s)}c((r=r.apply(e,t||[])).next())})},n=e&&e.__generator||function(e,t){var n={label:0,sent:function(){if(a[0]&1)throw a[1];return a[1]},trys:[],ops:[]},r,i,a,o;return o={next:s(0),throw:s(1),return:s(2)},typeof Symbol==`function`&&(o[Symbol.iterator]=function(){return this}),o;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw TypeError(`Generator is already executing.`);for(;o&&(o=0,s[0]&&(n=0)),n;)try{if(r=1,i&&(a=s[0]&2?i.return:s[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,s[1])).done)return a;switch(i=0,a&&(s=[s[0]&2,a.value]),s[0]){case 0:case 1:a=s;break;case 4:return n.label++,{value:s[1],done:!1};case 5:n.label++,i=s[1],s=[0];continue;case 7:s=n.ops.pop(),n.trys.pop();continue;default:if((a=n.trys,!(a=a.length>0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{var t=e&&e.__createBinding||(Object.create?(function(e,t,n,r){r===void 0&&(r=n);var i=Object.getOwnPropertyDescriptor(t,n);(!i||(`get`in i?!t.__esModule:i.writable||i.configurable))&&(i={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,r,i)}):(function(e,t,n,r){r===void 0&&(r=n),e[r]=t[n]})),n=e&&e.__exportStar||function(e,n){for(var r in e)r!=="default"&&!Object.prototype.hasOwnProperty.call(n,r)&&t(n,e,r)};Object.defineProperty(e,"__esModule",{value:!0}),n(require_client(),e),n(require_interfaces(),e),n(require_models(),e),n(require_server(),e),n(require_server_and_client(),e)})),import_dist=require_dist(),JSONRPC=class{constructor(e,{onConnected:t,onClosed:n}){this.url=e,this.onConnected=t,this.onClosed=n,this.messageQueue=[],this.serverAndClient=null,this.connect(),this.connectionEstablished=!1,this.timerId=null,this.closed=!1}close(){this.timerId&&clearTimeout(this.timerId),this.webSocket.close(),this.closed=!0}on(e,t){this.serverAndClient.addMethod(e,t)}async requestInternal(e,t,n){let r=await this.serverAndClient.request(e,t);n&&n(r)}requestMessageQueue(){this.messageQueue.forEach(e=>{let[t,n,r]=e;this.requestInternal(t,n,r)}),this.messageQueue=[]}request(e,t,n){this.webSocket.readyState===WebSocket.OPEN?this.requestInternal(e,t,n):this.messageQueue.push([e,t,n])}notify(e,t){switch(this.webSocket.readyState){case WebSocket.OPEN:this.serverAndClient.notify(e,t);break;case WebSocket.CLOSED:break}}connect(e){this.closed||(console.log(`connect`,this.url),this.webSocket=new WebSocket(this.url),this.serverAndClient||=new import_dist.JSONRPCServerAndClient(new import_dist.JSONRPCServer,new import_dist.JSONRPCClient(e=>{try{return this.webSocket.send(JSON.stringify(e)),Promise.resolve()}catch(e){return Promise.reject(e)}})),this.webSocket.onmessage=e=>{this.serverAndClient.receiveAndSend(JSON.parse(e.data.toString()))},this.webSocket.onopen=()=>{console.log(`WebSocket connection established`),this.connectionEstablished=!0,this.onConnected&&this.onConnected(),this.requestMessageQueue()},this.webSocket.onclose=e=>{console.error(`WebScoket closed`,e),this.serverAndClient.rejectAllPendingRequests(`Connection is closed (${e.reason}).`),this.connectionEstablished&&this.onClosed(),this.timerId=setTimeout(()=>{this.connect()},3e3)},this.webSocket.onerror=e=>{console.error(`WebSocket error:`,e),this.webSocket.close()})}},keyevent_exports=__exportAll({convertKeyEvent:()=>convertKeyEvent}),modifierKeys=[`Shift`,`Control`,`Alt`,`Meta`,`CapsLock`],convertKeyTable={Enter:`Return`,ArrowRight:`Right`,ArrowLeft:`Left`,ArrowUp:`Up`,ArrowDown:`Down`,"¡":`1`,"™":`2`,"£":`3`,"¢":`4`,"∞":`5`,"§":`6`,"¶":`7`,"•":`8`,ª:`9`,º:`0`,"–":`-`,"≠":`=`,"“":`[`,"‘":`]`,"«":`\\`,"…":`;`,æ:`'`,"≤":`,`,"≥":`.`,"÷":`/`,"⁄":`!`,"€":`@`,"‹":`#`,"›":`$`,fi:`%`,fl:`^`,"‡":`&`,"°":`*`,"·":`(`,"‚":`)`,"—":`_`,"±":`+`,"”":`{`,"’":`}`,"»":`|`,Ú:`:`,Æ:`"`,"¯":`<`,"˘":`>`,"¿":`?`,œ:`q`,"∑":`w`,"´":`e`,"®":`r`,"†":`t`,"¥":`y`,"¨":`u`,ˆ:`i`,ø:`o`,π:`p`,å:`a`,ß:`s`,"∂":`d`,ƒ:`f`,"©":`g`,"˙":`h`,"∆":`j`,"˚":`k`,"¬":`l`,Ω:`z`,"≈":`x`,ç:`c`,"√":`v`,"∫":`b`,"˜":`n`,µ:`m`,Œ:`Q`,"„":`W`,"´":`E`,"‰":`R`,ˇ:`T`,Á:`Y`,"¨":`U`,ˆ:`I`,Ø:`O`,"∏":`P`,Å:`A`,Í:`S`,Î:`D`,Ï:`F`,"˝":`G`,Ó:`H`,Ô:`J`,"":`K`,Ò:`L`,"¸":`Z`,"˛":`X`,Ç:`C`,"◊":`V`,ı:`B`,"˜":`N`,Â:`M`};function getKey(e){return e.altKey?convertKeyTable[e.key]||(e.code.startsWith(`Key`)?e.code[3].toLowerCase():null)||e.key:convertKeyTable[e.key]||e.key}function convertKeyEvent(e){return modifierKeys.indexOf(e.key)===-1?{key:getKey(e),ctrl:e.ctrlKey,meta:e.altKey,super:e.metaKey,shift:e.shiftKey}:null}var lib_exports=__exportAll({computeWidth:()=>computeWidth,eawVersion:()=>version,getEAW:()=>getEAW}),defs=[[0,31,`N`],[32,126,`Na`],[127,160,`N`],[161,161,`A`],[162,163,`Na`],[164,164,`A`],[165,166,`Na`],[167,168,`A`],[169,169,`N`],[170,170,`A`],[171,171,`N`],[172,172,`Na`],[173,174,`A`],[175,175,`Na`],[176,180,`A`],[181,181,`N`],[182,186,`A`],[187,187,`N`],[188,191,`A`],[192,197,`N`],[198,198,`A`],[199,207,`N`],[208,208,`A`],[209,214,`N`],[215,216,`A`],[217,221,`N`],[222,225,`A`],[226,229,`N`],[230,230,`A`],[231,231,`N`],[232,234,`A`],[235,235,`N`],[236,237,`A`],[238,239,`N`],[240,240,`A`],[241,241,`N`],[242,243,`A`],[244,246,`N`],[247,250,`A`],[251,251,`N`],[252,252,`A`],[253,253,`N`],[254,254,`A`],[255,256,`N`],[257,257,`A`],[258,272,`N`],[273,273,`A`],[274,274,`N`],[275,275,`A`],[276,282,`N`],[283,283,`A`],[284,293,`N`],[294,295,`A`],[296,298,`N`],[299,299,`A`],[300,304,`N`],[305,307,`A`],[308,311,`N`],[312,312,`A`],[313,318,`N`],[319,322,`A`],[323,323,`N`],[324,324,`A`],[325,327,`N`],[328,331,`A`],[332,332,`N`],[333,333,`A`],[334,337,`N`],[338,339,`A`],[340,357,`N`],[358,359,`A`],[360,362,`N`],[363,363,`A`],[364,461,`N`],[462,462,`A`],[463,463,`N`],[464,464,`A`],[465,465,`N`],[466,466,`A`],[467,467,`N`],[468,468,`A`],[469,469,`N`],[470,470,`A`],[471,471,`N`],[472,472,`A`],[473,473,`N`],[474,474,`A`],[475,475,`N`],[476,476,`A`],[477,592,`N`],[593,593,`A`],[594,608,`N`],[609,609,`A`],[610,707,`N`],[708,708,`A`],[709,710,`N`],[711,711,`A`],[712,712,`N`],[713,715,`A`],[716,716,`N`],[717,717,`A`],[718,719,`N`],[720,720,`A`],[721,727,`N`],[728,731,`A`],[732,732,`N`],[733,733,`A`],[734,734,`N`],[735,735,`A`],[736,767,`N`],[768,879,`A`],[880,912,`N`],[913,929,`A`],[930,930,`N`],[931,937,`A`],[938,944,`N`],[945,961,`A`],[962,962,`N`],[963,969,`A`],[970,1024,`N`],[1025,1025,`A`],[1026,1039,`N`],[1040,1103,`A`],[1104,1104,`N`],[1105,1105,`A`],[1106,4351,`N`],[4352,4447,`W`],[4448,8207,`N`],[8208,8208,`A`],[8209,8210,`N`],[8211,8214,`A`],[8215,8215,`N`],[8216,8217,`A`],[8218,8219,`N`],[8220,8221,`A`],[8222,8223,`N`],[8224,8226,`A`],[8227,8227,`N`],[8228,8231,`A`],[8232,8239,`N`],[8240,8240,`A`],[8241,8241,`N`],[8242,8243,`A`],[8244,8244,`N`],[8245,8245,`A`],[8246,8250,`N`],[8251,8251,`A`],[8252,8253,`N`],[8254,8254,`A`],[8255,8307,`N`],[8308,8308,`A`],[8309,8318,`N`],[8319,8319,`A`],[8320,8320,`N`],[8321,8324,`A`],[8325,8360,`N`],[8361,8361,`H`],[8362,8363,`N`],[8364,8364,`A`],[8365,8450,`N`],[8451,8451,`A`],[8452,8452,`N`],[8453,8453,`A`],[8454,8456,`N`],[8457,8457,`A`],[8458,8466,`N`],[8467,8467,`A`],[8468,8469,`N`],[8470,8470,`A`],[8471,8480,`N`],[8481,8482,`A`],[8483,8485,`N`],[8486,8486,`A`],[8487,8490,`N`],[8491,8491,`A`],[8492,8530,`N`],[8531,8532,`A`],[8533,8538,`N`],[8539,8542,`A`],[8543,8543,`N`],[8544,8555,`A`],[8556,8559,`N`],[8560,8569,`A`],[8570,8584,`N`],[8585,8585,`A`],[8586,8591,`N`],[8592,8601,`A`],[8602,8631,`N`],[8632,8633,`A`],[8634,8657,`N`],[8658,8658,`A`],[8659,8659,`N`],[8660,8660,`A`],[8661,8678,`N`],[8679,8679,`A`],[8680,8703,`N`],[8704,8704,`A`],[8705,8705,`N`],[8706,8707,`A`],[8708,8710,`N`],[8711,8712,`A`],[8713,8714,`N`],[8715,8715,`A`],[8716,8718,`N`],[8719,8719,`A`],[8720,8720,`N`],[8721,8721,`A`],[8722,8724,`N`],[8725,8725,`A`],[8726,8729,`N`],[8730,8730,`A`],[8731,8732,`N`],[8733,8736,`A`],[8737,8738,`N`],[8739,8739,`A`],[8740,8740,`N`],[8741,8741,`A`],[8742,8742,`N`],[8743,8748,`A`],[8749,8749,`N`],[8750,8750,`A`],[8751,8755,`N`],[8756,8759,`A`],[8760,8763,`N`],[8764,8765,`A`],[8766,8775,`N`],[8776,8776,`A`],[8777,8779,`N`],[8780,8780,`A`],[8781,8785,`N`],[8786,8786,`A`],[8787,8799,`N`],[8800,8801,`A`],[8802,8803,`N`],[8804,8807,`A`],[8808,8809,`N`],[8810,8811,`A`],[8812,8813,`N`],[8814,8815,`A`],[8816,8833,`N`],[8834,8835,`A`],[8836,8837,`N`],[8838,8839,`A`],[8840,8852,`N`],[8853,8853,`A`],[8854,8856,`N`],[8857,8857,`A`],[8858,8868,`N`],[8869,8869,`A`],[8870,8894,`N`],[8895,8895,`A`],[8896,8977,`N`],[8978,8978,`A`],[8979,8985,`N`],[8986,8987,`W`],[8988,9e3,`N`],[9001,9002,`W`],[9003,9192,`N`],[9193,9196,`W`],[9197,9199,`N`],[9200,9200,`W`],[9201,9202,`N`],[9203,9203,`W`],[9204,9311,`N`],[9312,9449,`A`],[9450,9450,`N`],[9451,9547,`A`],[9548,9551,`N`],[9552,9587,`A`],[9588,9599,`N`],[9600,9615,`A`],[9616,9617,`N`],[9618,9621,`A`],[9622,9631,`N`],[9632,9633,`A`],[9634,9634,`N`],[9635,9641,`A`],[9642,9649,`N`],[9650,9651,`A`],[9652,9653,`N`],[9654,9655,`A`],[9656,9659,`N`],[9660,9661,`A`],[9662,9663,`N`],[9664,9665,`A`],[9666,9669,`N`],[9670,9672,`A`],[9673,9674,`N`],[9675,9675,`A`],[9676,9677,`N`],[9678,9681,`A`],[9682,9697,`N`],[9698,9701,`A`],[9702,9710,`N`],[9711,9711,`A`],[9712,9724,`N`],[9725,9726,`W`],[9727,9732,`N`],[9733,9734,`A`],[9735,9736,`N`],[9737,9737,`A`],[9738,9741,`N`],[9742,9743,`A`],[9744,9747,`N`],[9748,9749,`W`],[9750,9755,`N`],[9756,9756,`A`],[9757,9757,`N`],[9758,9758,`A`],[9759,9791,`N`],[9792,9792,`A`],[9793,9793,`N`],[9794,9794,`A`],[9795,9799,`N`],[9800,9811,`W`],[9812,9823,`N`],[9824,9825,`A`],[9826,9826,`N`],[9827,9829,`A`],[9830,9830,`N`],[9831,9834,`A`],[9835,9835,`N`],[9836,9837,`A`],[9838,9838,`N`],[9839,9839,`A`],[9840,9854,`N`],[9855,9855,`W`],[9856,9874,`N`],[9875,9875,`W`],[9876,9885,`N`],[9886,9887,`A`],[9888,9888,`N`],[9889,9889,`W`],[9890,9897,`N`],[9898,9899,`W`],[9900,9916,`N`],[9917,9918,`W`],[9919,9919,`A`],[9920,9923,`N`],[9924,9925,`W`],[9926,9933,`A`],[9934,9934,`W`],[9935,9939,`A`],[9940,9940,`W`],[9941,9953,`A`],[9954,9954,`N`],[9955,9955,`A`],[9956,9959,`N`],[9960,9961,`A`],[9962,9962,`W`],[9963,9969,`A`],[9970,9971,`W`],[9972,9972,`A`],[9973,9973,`W`],[9974,9977,`A`],[9978,9978,`W`],[9979,9980,`A`],[9981,9981,`W`],[9982,9983,`A`],[9984,9988,`N`],[9989,9989,`W`],[9990,9993,`N`],[9994,9995,`W`],[9996,10023,`N`],[10024,10024,`W`],[10025,10044,`N`],[10045,10045,`A`],[10046,10059,`N`],[10060,10060,`W`],[10061,10061,`N`],[10062,10062,`W`],[10063,10066,`N`],[10067,10069,`W`],[10070,10070,`N`],[10071,10071,`W`],[10072,10101,`N`],[10102,10111,`A`],[10112,10132,`N`],[10133,10135,`W`],[10136,10159,`N`],[10160,10160,`W`],[10161,10174,`N`],[10175,10175,`W`],[10176,10213,`N`],[10214,10221,`Na`],[10222,10628,`N`],[10629,10630,`Na`],[10631,11034,`N`],[11035,11036,`W`],[11037,11087,`N`],[11088,11088,`W`],[11089,11092,`N`],[11093,11093,`W`],[11094,11097,`A`],[11098,11903,`N`],[11904,11929,`W`],[11930,11930,`N`],[11931,12019,`W`],[12020,12031,`N`],[12032,12245,`W`],[12246,12271,`N`],[12272,12287,`W`],[12288,12288,`F`],[12289,12350,`W`],[12351,12352,`N`],[12353,12438,`W`],[12439,12440,`N`],[12441,12543,`W`],[12544,12548,`N`],[12549,12591,`W`],[12592,12592,`N`],[12593,12686,`W`],[12687,12687,`N`],[12688,12771,`W`],[12772,12782,`N`],[12783,12830,`W`],[12831,12831,`N`],[12832,12871,`W`],[12872,12879,`A`],[12880,19903,`W`],[19904,19967,`N`],[19968,42124,`W`],[42125,42127,`N`],[42128,42182,`W`],[42183,43359,`N`],[43360,43388,`W`],[43389,44031,`N`],[44032,55203,`W`],[55204,57343,`N`],[57344,63743,`A`],[63744,64255,`W`],[64256,65023,`N`],[65024,65039,`A`],[65040,65049,`W`],[65050,65071,`N`],[65072,65106,`W`],[65107,65107,`N`],[65108,65126,`W`],[65127,65127,`N`],[65128,65131,`W`],[65132,65280,`N`],[65281,65376,`F`],[65377,65470,`H`],[65471,65473,`N`],[65474,65479,`H`],[65480,65481,`N`],[65482,65487,`H`],[65488,65489,`N`],[65490,65495,`H`],[65496,65497,`N`],[65498,65500,`H`],[65501,65503,`N`],[65504,65510,`F`],[65511,65511,`N`],[65512,65518,`H`],[65519,65532,`N`],[65533,65533,`A`],[65534,94175,`N`],[94176,94180,`W`],[94181,94191,`N`],[94192,94193,`W`],[94194,94207,`N`],[94208,100343,`W`],[100344,100351,`N`],[100352,101589,`W`],[101590,101631,`N`],[101632,101640,`W`],[101641,110575,`N`],[110576,110579,`W`],[110580,110580,`N`],[110581,110587,`W`],[110588,110588,`N`],[110589,110590,`W`],[110591,110591,`N`],[110592,110882,`W`],[110883,110897,`N`],[110898,110898,`W`],[110899,110927,`N`],[110928,110930,`W`],[110931,110932,`N`],[110933,110933,`W`],[110934,110947,`N`],[110948,110951,`W`],[110952,110959,`N`],[110960,111355,`W`],[111356,126979,`N`],[126980,126980,`W`],[126981,127182,`N`],[127183,127183,`W`],[127184,127231,`N`],[127232,127242,`A`],[127243,127247,`N`],[127248,127277,`A`],[127278,127279,`N`],[127280,127337,`A`],[127338,127343,`N`],[127344,127373,`A`],[127374,127374,`W`],[127375,127376,`A`],[127377,127386,`W`],[127387,127404,`A`],[127405,127487,`N`],[127488,127490,`W`],[127491,127503,`N`],[127504,127547,`W`],[127548,127551,`N`],[127552,127560,`W`],[127561,127567,`N`],[127568,127569,`W`],[127570,127583,`N`],[127584,127589,`W`],[127590,127743,`N`],[127744,127776,`W`],[127777,127788,`N`],[127789,127797,`W`],[127798,127798,`N`],[127799,127868,`W`],[127869,127869,`N`],[127870,127891,`W`],[127892,127903,`N`],[127904,127946,`W`],[127947,127950,`N`],[127951,127955,`W`],[127956,127967,`N`],[127968,127984,`W`],[127985,127987,`N`],[127988,127988,`W`],[127989,127991,`N`],[127992,128062,`W`],[128063,128063,`N`],[128064,128064,`W`],[128065,128065,`N`],[128066,128252,`W`],[128253,128254,`N`],[128255,128317,`W`],[128318,128330,`N`],[128331,128334,`W`],[128335,128335,`N`],[128336,128359,`W`],[128360,128377,`N`],[128378,128378,`W`],[128379,128404,`N`],[128405,128406,`W`],[128407,128419,`N`],[128420,128420,`W`],[128421,128506,`N`],[128507,128591,`W`],[128592,128639,`N`],[128640,128709,`W`],[128710,128715,`N`],[128716,128716,`W`],[128717,128719,`N`],[128720,128722,`W`],[128723,128724,`N`],[128725,128727,`W`],[128728,128731,`N`],[128732,128735,`W`],[128736,128746,`N`],[128747,128748,`W`],[128749,128755,`N`],[128756,128764,`W`],[128765,128991,`N`],[128992,129003,`W`],[129004,129007,`N`],[129008,129008,`W`],[129009,129291,`N`],[129292,129338,`W`],[129339,129339,`N`],[129340,129349,`W`],[129350,129350,`N`],[129351,129535,`W`],[129536,129647,`N`],[129648,129660,`W`],[129661,129663,`N`],[129664,129672,`W`],[129673,129679,`N`],[129680,129725,`W`],[129726,129726,`N`],[129727,129733,`W`],[129734,129741,`N`],[129742,129755,`W`],[129756,129759,`N`],[129760,129768,`W`],[129769,129775,`N`],[129776,129784,`W`],[129785,131071,`N`],[131072,196605,`W`],[196606,196607,`N`],[196608,262141,`W`],[262142,917759,`N`],[917760,917999,`A`],[918e3,983039,`N`],[983040,1048573,`A`],[1048574,1048575,`N`],[1048576,1114109,`A`],[1114110,1114111,`N`]],version=`15.1.0`;function getEAWOfCodePoint(e){let t=0,n=defs.length-1;for(;t!==n;){let r=t+(n-t>>1),[i,a,o]=defs[r];if(ea)t=r+1;else return o}return defs[t][2]}function getEAW(e,t=0){let n=e.codePointAt(t);if(n!==void 0)return getEAWOfCodePoint(n)}var defaultWidths={N:1,Na:1,W:2,F:2,H:1,A:1};function computeWidth(e,t){let n=0;for(let r of e){let e=getEAW(r);n+=t&&t[e]||defaultWidths[e]}return n}var textOffsetY=5,isSafari=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);function isWideChar(e){switch(getEAW(e)){case`F`:case`W`:return!0;default:return!1}}function isMacOS(){return window.navigator.userAgent.indexOf(`Mac OS X`)!==-1}function computeFontSize(e){let t=document.createElement(`canvas`).getContext(`2d`);t.font=e;let n=t.measureText(`W`);return[Math.floor(n.width),Math.round(n.fontBoundingBoxAscent+textOffsetY+(n.emHeightDescent||0))]}function drawBlock({ctx:e,x:t,y:n,width:r,height:i,style:a}){e.fillStyle=a,e.fillRect(t,n,r,i)}function drawText({ctx:e,x:t,y:n,text:r,font:i,style:a,option:o}){n+=Math.round(textOffsetY),e.fillStyle=a,e.font=i,e.textBaseline=`top`;for(let i of r)isWideChar(i)?(e.fillText(i,t,n,o.fontWidth*2),t+=o.fontWidth*2):(e.fillText(i,t,n,o.fontWidth),t+=o.fontWidth)}function drawHorizontalLine({ctx:e,x:t,y:n,width:r,style:i,lineWidth:a=1}){e.strokeStyle=i,e.lineWidth=a,e.setLineDash=[],e.beginPath(),e.moveTo(t,n),e.lineTo(t+r,n),e.stroke()}var Option=class{constructor({fontName:e,fontSize:t}){this.setFont(e,t),this.foreground=`#cccccc`,this.background=`#2d2d2d`}setFont(e,t){let n=t+`px `+e,[r,i]=computeFontSize(n);this.fontName=e,this.fontSize=t,this.fontWidth=r,this.fontHeight=i,this.font=n}};function getLemEditorElement(){return document.getElementById(`lem-editor`)}function normalizeWheelDelta(e,t,n,r){switch(n){case 0:return{dx:e/r,dy:t/r};case 2:return{dx:e*20,dy:t*20};default:return{dx:e,dy:t}}}function extractWholeLines(e,t){let n=Math.trunc(e),r=Math.trunc(t);return{scrollX:n,scrollY:r,remainderX:e-n,remainderY:t-r}}function cursorPosition(e,t){let[n,r]=t.getDisplayRectangle(),i=e.clientX-n,a=e.clientY-r;return{pixelX:i,pixelY:a,x:Math.floor(i/t.option.fontWidth),y:Math.floor(a/t.option.fontHeight)}}function makeWheelHandler(e){let t={x:0,y:0},n=!1,r={pixelX:0,pixelY:0,x:0,y:0};return i=>{i.preventDefault(),r=cursorPosition(i,e);let{dx:a,dy:o}=normalizeWheelDelta(i.deltaX,i.deltaY,i.deltaMode,e.option.fontHeight);t={x:t.x+a,y:t.y+o},n||(n=!0,requestAnimationFrame(()=>{n=!1;let{scrollX:i,scrollY:a,remainderX:o,remainderY:s}=extractWholeLines(t.x,t.y);t={x:o,y:s},(i!==0||a!==0)&&e.jsonrpc.notify(`input`,{kind:`wheel`,value:{...r,wheelX:-i,wheelY:-a}})}))}}function addMouseEventListeners({dom:e,editor:t,isDraggable:n,draggableStyle:r}){e.addEventListener(`contextmenu`,e=>{e.preventDefault()});let i=(e,n)=>{e.preventDefault();let[r,i]=t.getDisplayRectangle(),a=e.clientX-r,o=e.clientY-i,s=Math.floor(a/t.option.fontWidth),c=Math.floor(o/t.option.fontHeight);t.jsonrpc.notify(`input`,{kind:n,value:{x:s,y:c,pixelX:a,pixelY:o,button:e.button,clicks:e.detail}})};e.addEventListener(`mousedown`,e=>{n&&(document.body.style.cursor=r),t.focusHiddenInput(),i(e,`mousedown`)}),e.addEventListener(`mouseup`,e=>{n&&(document.body.style.cursor=`default`),i(e,`mouseup`)});let a=0;e.addEventListener(`mousemove`,e=>{e.preventDefault();let n=Date.now();if(n-a>50){a=n;let[r,i]=t.getDisplayRectangle(),o=e.clientX-r,s=e.clientY-i,c=Math.floor(o/t.option.fontWidth),l=Math.floor(s/t.option.fontHeight);t.jsonrpc.notify(`input`,{kind:`mousemove`,value:{x:c,y:l,pixelX:o,pixelY:s,button:e.buttons===0?null:e.buttons-1}})}}),n&&(e.addEventListener(`mouseover`,()=>{document.body.style.cursor=r}),e.addEventListener(`mouseout`,e=>{e.buttons!==1&&(document.body.style.cursor=`default`)})),e.addEventListener(`wheel`,makeWheelHandler(t))}var zIndexTable={"floating-window":200,modeline:100,"vertical-border":100,"horizontal-border":101};function zindex(e){return zIndexTable[e]||0}var borderOffsetX=5,borderOffsetY=10,BaseSurface=class{constructor({editor:e}){this.editor=e,this.mainDOM=null,this.wrapper=null}delete(){this.wrapper?getLemEditorElement().removeChild(this.wrapper):getLemEditorElement().removeChild(this.mainDOM)}setupDOM({dom:e,isFloating:t,border:n,cssClassName:r}){this.mainDOM=e,t&&n?(this.wrapper=document.createElement(`div`),r&&(this.wrapper.className=r),this.wrapper.style.position=`absolute`,this.wrapper.style.backgroundColor=this.editor.option.background,this.wrapper.style.zIndex=zindex(`floating-window`),this.wrapper.appendChild(e),getLemEditorElement().appendChild(this.wrapper)):(r&&(e.className=r),getLemEditorElement().appendChild(e))}move(e,t,n,r){let[i,a]=this.editor.getDisplayRectangle(),o=Math.floor(n==null?i+e*this.editor.option.fontWidth:i+n),s=Math.floor(r==null?a+t*this.editor.option.fontHeight:a+r);this.wrapper?(this.wrapper.style.left=o-borderOffsetX+`px`,this.wrapper.style.top=s-borderOffsetY+`px`,this.mainDOM.style.left=borderOffsetX+`px`,this.mainDOM.style.top=borderOffsetY+`px`):(this.mainDOM.style.left=o+`px`,this.mainDOM.style.top=s+`px`)}_resize(e,t,n,r){let i=window.devicePixelRatio||1,a=n??e*this.editor.option.fontWidth,o=r??t*this.editor.option.fontHeight;this.mainDOM.width=a*i,this.mainDOM.height=o*i,this.mainDOM.style.width=a+`px`,this.mainDOM.style.height=o+`px`,this.wrapper&&(this.wrapper.style.width=a+borderOffsetX*2+`px`,this.wrapper.style.height=o+borderOffsetY*2+`px`)}drawBlock(e,t,n,r,i){}drawText(e,t,n,r,i){}drawImage(e,t,n,r,i,a,o){}clearImages(e,t){}clearAllImages(){}touch(){}evalIn(code){return eval(code)}},CanvasSurface=class extends BaseSurface{constructor({editor:e,view:t,x:n,y:r,width:i,height:a,styles:o,isFloating:s,border:c,cssClassName:l}){super({editor:e});let u=this.setupCanvas(o);this.setupDOM({dom:u,isFloating:s,border:c,cssClassName:l}),this.move(n,r),this.resize(i,a),this.drawingQueue=[],addMouseEventListeners({dom:u,editor:e})}setupCanvas(e){let t=document.createElement(`canvas`);if(t.style.position=`absolute`,e)for(let n in e)t.style[n]=e[n];return t}resize(e,t,n,r){this._resize(e,t,n,r);let i=window.devicePixelRatio||1;this.mainDOM.getContext(`2d`).scale(i,i)}move(e,t,n,r){if(super.move(e,t,n,r),this.imageEls)for(let[,e]of this.imageEls)this.positionImage(e)}delete(){this.clearAllImages(),super.delete()}drawBlock(e,t,n,r,i){let a=this.editor.option;this.drawingQueue.push(function(o){drawBlock({ctx:o,x:e*a.fontWidth,y:t*a.fontHeight,width:n*a.fontWidth,height:r*a.fontHeight,style:i})})}drawText(e,t,n,r,i,a){let o=this.editor.option;this.drawingQueue.push(function(s){if(a=a?`${o.fontSize}px ${a}`:o.font,!i)drawBlock({ctx:s,x:e*o.fontWidth,y:t*o.fontHeight,width:r*o.fontWidth,height:o.fontHeight,style:o.background}),drawText({ctx:s,x:e*o.fontWidth,y:t*o.fontHeight,text:n,style:o.foreground,font:a,option:o});else{let{foreground:c,background:l,bold:u,reverse:d,underline:f,cursor:p}=i;if(c||=o.foreground,l||=o.background,d){let e=l;l=c,c=e}p&&(l=o.background);let m=e*o.fontWidth,h=t*o.fontHeight;drawBlock({ctx:s,x:m,y:h,width:r*o.fontWidth,height:o.fontHeight,style:l}),drawText({ctx:s,x:m,y:h,text:n,style:c,font:u?`bold `+a:a,option:o}),f&&drawHorizontalLine({ctx:s,x:m,y:h+o.fontHeight-2,width:r*o.fontWidth,style:typeof f==`string`?f:c,lineWidth:2})}})}imageBaseLeft(){return parseFloat(this.mainDOM.style.left)||0}imageBaseTop(){return parseFloat(this.mainDOM.style.top)||0}drawImage(e,t,n,r,i,a,o){this.imageEls||=new Map;let s=e+`,`+t,c=this.imageEls.get(s);if(c&&c.url!==o&&(c.el.remove(),this.imageEls.delete(s),c=null),!c){let e=document.createElement(`img`);e.style.position=`absolute`,e.style.pointerEvents=`none`,e.style.zIndex=`1`,e.src=o,this.mainDOM.parentNode.appendChild(e),c={el:e,url:o},this.imageEls.set(s,c)}c.x=e,c.y=t,c.widthCells=n,c.heightCells=r,c.pixelWidth=i,c.pixelHeight=a,this.positionImage(c)}positionImage(e){let t=this.editor.option,n=e.widthCells*t.fontWidth,r=e.heightCells*t.fontHeight;e.el.style.left=this.imageBaseLeft()+e.x*t.fontWidth+`px`,e.el.style.top=this.imageBaseTop()+e.y*t.fontHeight+`px`,e.el.style.width=(e.pixelWidth==null?n:Math.min(e.pixelWidth,n))+`px`,e.el.style.height=(e.pixelHeight==null?r:Math.min(e.pixelHeight,r))+`px`}clearImages(e,t){if(this.imageEls)for(let[n,r]of this.imageEls)r.ye&&(r.el.remove(),this.imageEls.delete(n))}clearAllImages(){if(this.imageEls){for(let[,e]of this.imageEls)e.el.remove();this.imageEls.clear()}}touch(){let e=this.mainDOM.getContext(`2d`);for(let t of this.drawingQueue)t(e);this.drawingQueue=[]}activate(){this.mainDOM.dataset.store=`active`}deactivate(){this.mainDOM.dataset.store=`inactive`}},HTMLSurface=class extends BaseSurface{constructor({editor:e,x:t,y:n,width:r,height:i,styles:a,option:o,isFloating:s,border:c,html:l}){super({editor:e});let u=document.createElement(`iframe`);this.setupDOM({dom:u,isFloating:s,border:c}),u.style.position=`absolute`,u.style.backgroundColor=o.background,u.setAttribute(`sandbox`,`allow-scripts allow-same-origin`),u.srcdoc=l,u.addEventListener(`load`,()=>{let e=u.contentWindow;e.invokeLem=(e,t)=>parent.postMessage({type:`invoke-lem`,method:e,args:t})}),this.iframe=u,this.move(t,n),this.resize(r,i)}resize(e,t,n,r){this._resize(e,t,n,r)}update(e){let t=this.iframe.contentWindow.scrollY;this.iframe.srcdoc=e,this.iframe.onload=()=>{this.iframe.onload=null,this.iframe.contentWindow.scrollTo(0,t)}}evalIn(e){return this.iframe.contentWindow.eval(e)}},VerticalBorder=class{constructor({x:e,y:t,height:n,option:r,editor:i}){this.option=r,this.editor=i,this.line=document.createElement(`div`),this.line.className=`lem-editor__vertical-border`,this.line.style.height=n*r.fontHeight+`px`,this.line.style.position=`absolute`,this.line.style.zIndex=zindex(`vertical-border`),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:i,isDraggable:!0,draggableStyle:`col-resize`})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.line.style.left=Math.floor(n+e*this.option.fontWidth-this.option.fontWidth/2)+`px`,this.line.style.top=r+t*this.option.fontHeight+`px`}resize(e){this.line.style.height=e*this.option.fontHeight+`px`}},HorizontalBorder=class{constructor({x:e,y:t,width:n,option:r,editor:i}){this.option=r,this.editor=i,this.line=document.createElement(`div`),this.line.className=`lem-editor__horizontal-border`,this.line.style.width=n*r.fontWidth+`px`,this.line.style.position=`absolute`,this.line.style.zIndex=zindex(`horizontal-border`),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:i,isDraggable:!0,draggableStyle:`row-resize`})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.line.style.left=n+e*this.option.fontWidth+`px`,this.line.style.top=Math.floor(r+t*this.option.fontHeight-4)+`px`}resize(e){this.line.style.width=e*this.option.fontWidth+`px`}},viewStyles={header:()=>{},tile:()=>{},floating:e=>({boxSizing:`border-box`,borderColor:e.foreground,backgroundColor:e.background})};function getViewStyle(e,t){return viewStyles[e](t)||{}}var View=class{constructor({id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,useModeline:l,kind:u,type:d,content:f,border:p,borderShape:m,option:h,editor:g}){switch(this.option=h,this.id=e,this.x=t,this.y=n,this.width=r,this.height=i,this.pixelX=a,this.pixelY=o,this.pixelWidth=s,this.pixelHeight=c,this.useModeline=l,this.kind=u,this.type=d,this.border=p,this.borderShape=m,this.editor=g,this.bottomBar=null,this.leftsideBar=null,u){case`tile`:this.mainSurface=this.makeSurface(d,f),this.leftSideBar=new VerticalBorder({x:t,y:n,height:i+ +!!l,option:h,editor:g}),l||(this.bottomBar=new HorizontalBorder({x:t,y:n+i-1,width:r,option:h,editor:g}));break;case`header`:this.mainSurface=this.makeSurface(d,f);break;case`floating`:this.mainSurface=this.makeSurface(d,f),m===`left-border`&&(this.leftSideBar=new VerticalBorder({x:t,y:n,height:i,option:h,editor:g}));break}this.modelineSurface=l?this.makeModelineSurface():null,u===`floating`&&(a!=null||o!=null)&&this.move(t,n,a,o)}delete(){this.mainSurface.delete(),this.modelineSurface&&this.modelineSurface.delete(),this.leftSideBar&&this.leftSideBar.delete(),this.bottomBar&&this.bottomBar.delete()}move(e,t,n,r){if(this.x=e,this.y=t,this.pixelX=n,this.pixelY=r,this.mainSurface.move(e,t,n,r),this.modelineSurface){let i=r!=null&&this.pixelHeight!=null?r+this.pixelHeight:null;this.modelineSurface.move(e,t+this.height,n,i)}this.leftSideBar&&this.leftSideBar.move(e,t),this.bottomBar&&this.bottomBar.move(e,t+this.height)}resize(e,t,n,r){if(this.width=e,this.height=t,this.pixelWidth=n,this.pixelHeight=r,this.mainSurface.resize(e,t,n,r),this.modelineSurface){let t=this.pixelY!=null&&r!=null?this.pixelY+r:null;this.modelineSurface.move(this.x,this.y+this.height,this.pixelX,t),this.modelineSurface.resize(e,1)}this.leftSideBar&&this.leftSideBar.resize(t+ +!!this.modelineSurface),this.bottomBar&&this.bottomBar.resize(e)}clear(){this.mainSurface.drawBlock(0,0,this.width,this.height,this.option.background)}clearEol(e,t,n=1){this.mainSurface.drawBlock(e,t,this.width-e,n,this.option.background),this.mainSurface.clearImages(t,t+n)}clearEob(e,t){this.mainSurface.drawBlock(e,t,this.width,this.height-t,this.option.background),this.mainSurface.clearImages(t,this.height)}print(e,t,n,r,i,a){this.mainSurface.drawText(e,t,n,r,i,a)}printImage(e,t,n,r,i,a,o){this.mainSurface.drawImage(e,t,n,r,i,a,o)}printToModeline(e,t,n,r,i){this.modelineSurface&&this.modelineSurface.drawText(e,t,n,r,i)}touch(e){this.mainSurface.touch(),this.modelineSurface&&(this.modelineSurface.touch(),e?this.modelineSurface.activate():this.modelineSurface.deactivate())}makeSurface(e,t){switch(e){case`html`:return this.makeHTMLSurface(t);case`editor`:return this.makeEditorSurface();default:console.error(`unknown type: ${e}`)}}makeHTMLSurface(e){return new HTMLSurface({editor:this.editor,x:this.x,y:this.y,width:this.width,height:this.height,styles:getViewStyle(this.kind,this.option),option:this.option,isFloating:this.kind===`floating`,border:this.border,html:e})}makeEditorSurface(){let e=this.borderShape===`left-border`?0:this.border,t=this.kind===`floating`;return new CanvasSurface({option:this.editor.option,x:this.x,y:this.y,width:this.width,height:this.height,styles:getViewStyle(this.kind,this.option),editor:this.editor,border:e,isFloating:t,view:this,cssClassName:t&&e?`lem-editor__floating-window--bordered`:null})}makeModelineSurface(){let e=new CanvasSurface({option:this.editor.option,x:this.x,y:this.y+this.height,width:this.width,height:1,editor:this.editor,view:this,styles:{zIndex:zindex(`modeline`)},cssClassName:`lem-editor__mode-line`});return addMouseEventListeners({dom:e.mainDOM,editor:this.editor,isDraggable:!0,draggableStyle:`row-resize`}),e}changeToHTMLContent(e){this.mainSurface.constructor.name===`HTMLSurface`?this.mainSurface.update(e):(this.mainSurface.delete(),this.mainSurface=this.makeHTMLSurface(e))}changeToEditorContent(){this.mainSurface.delete(),this.mainSurface=this.makeEditorSurface()}evalIn(e){return this.mainSurface.evalIn(e)}};function isPasteKeyEvent(e){return isMacOS()?e.metaKey&&e.key===`v`:e.ctrlKey&&e.shiftKey&&e.key===`V`}var Input=class{constructor(e){let t=e.option;this.editor=e,this.composition=!1,this.ignoreKeydownAfterCompositionend=!1,this.span=document.createElement(`span`),this.span.style.color=t.foreground,this.span.style.backgroundColor=t.background,this.span.style.position=`absolute`,this.span.style.zIndex=1e6,this.span.style.top=`0`,this.span.style.left=`0`,this.span.style.font=t.font,this.input=document.createElement(`input`),this.input.style.backgroundColor=`transparent`,this.input.style.color=`transparent`,this.input.style.width=`0`,this.input.style.padding=`0`,this.input.style.margin=`0`,this.input.style.border=`none`,this.input.style.position=`absolute`,this.input.style.zIndex=`-10`,this.input.style.top=`0`,this.input.style.left=`0`,this.input.style.font=t.font,this.input.addEventListener(`blur`,e=>{this.input.focus()}),this.input.addEventListener(`input`,e=>{this.composition===!1&&(this.input.value=``,this.span.innerHTML=``,this.input.style.width=`0`,isMacOS()||this.editor.emitInputString(e.data))}),this.input.addEventListener(`paste`,async e=>{e.preventDefault();let t=e.clipboardData||window.Clipboard.data,n=t?.getData(`text`)??t?.getData(`text/plain`);if(n&&n.length>0){this.editor.emitInputString(n);return}try{if(navigator.clipboard?.readText){let e=await navigator.clipboard.readText();if(e&&e.length>0){this.editor.emitInputString(e);return}}}catch(e){console.warn(`clipboard.readText() failed:`,e)}alert(`Paste failed (permission/environment restriction`)}),this.input.addEventListener(`keydown`,e=>{if(!isPasteKeyEvent(e)&&!(e.isComposing||this.composition)&&e.key!==`Process`){if(this.ignoreKeydownAfterCompositionend&&(isSafari||isMacOS())){e.preventDefault(),this.ignoreKeydownAfterCompositionend=!1;return}if(!(!isMacOS()&&!e.ctrlKey&&!e.altKey&&e.key.length===1)&&(e.preventDefault(),e.isComposing!==!0&&e.code!==``))return setTimeout(()=>{this.composition||(this.editor.emitInput(e),this.input.value=``)},0),!1}}),this.input.addEventListener(`compositionstart`,e=>{this.composition=!0,this.span.innerHTML=this.input.value,this.input.style.width=this.span.offsetWidth+`px`}),this.input.addEventListener(`compositionupdate`,e=>{this.span.innerHTML=e.data,this.input.style.width=this.span.offsetWidth+`px`}),this.input.addEventListener(`compositionend`,e=>{this.composition=!1,this.editor.emitInputString(this.input.value),this.input.value=``,this.span.innerHTML=this.input.value,this.input.style.width=`0`,this.ignoreKeydownAfterCompositionend=!0}),document.body.appendChild(this.input),document.body.appendChild(this.span),this.input.focus()}finalize(){document.body.removeChild(this.input),document.body.removeChild(this.span)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.span.style.top=r+t+`px`,this.span.style.left=n+e+`px`,this.input.style.top=this.span.offsetTop+`px`,this.input.style.left=this.span.offsetLeft+`px`}updateForeground(e){this.span.style.color=e}updateBackground(e){this.span.style.backgroundColor=e}},MessageTable=class{constructor(){this.map=new Map}register(e,t){for(let n in t){let r=t[n];this.map.set(n,r),e.on(n,r)}}get(e){return this.map.get(e)}};function getDisplayRectangleDefault(){return[0,0,window.innerWidth,window.innerHeight]}var Editor=class{constructor({getDisplayRectangle:e=getDisplayRectangleDefault,fontName:t,fontSize:n,url:r,onExit:i,onClosed:a}){this.getDisplayRectangle=e,this.option=new Option({fontName:t,fontSize:n}),this.onExit=i,this.input=new Input(this),this.cursors=new Map,this.cursorOverlay=document.createElement(`div`),this.cursorOverlay.className=`lem-cursor`,this.cursorOverlay.style.width=this.option.fontWidth+`px`,this.cursorOverlay.style.height=this.option.fontHeight+`px`,this.cursorOverlay.style.backgroundColor=`#ffffff`,this.cursorType=`box`,this.viewMap=new Map,this.jsonrpc=new JSONRPC(r,{onClosed:()=>{a()}}),this.messageTable=new MessageTable,this.messageTable.register(this.jsonrpc,{"update-foreground":this.updateForeground.bind(this),"update-background":this.updateBackground.bind(this),"make-view":this.makeView.bind(this),"delete-view":this.deleteView.bind(this),"resize-view":this.resize.bind(this),"move-view":this.move.bind(this),"redraw-view-after":this.redrawViewAfter.bind(this),clear:this.clear.bind(this),"clear-eol":this.clearEol.bind(this),"clear-eob":this.clearEob.bind(this),put:this.put.bind(this),"put-image":this.putImage.bind(this),"modeline-put":this.modelinePut.bind(this),"update-display":this.updateDisplay.bind(this),"move-cursor":this.moveCursor.bind(this),"change-view":this.changeView.bind(this),"resize-display":this.resizeDisplay.bind(this),bulk:this.bulk.bind(this),exit:this.exitEditor.bind(this),"get-clipboard-text":this.getClipboardText.bind(this),"set-clipboard-text":this.setClipboardText.bind(this),"js-eval":this.jsEval.bind(this),"set-font":this.setFont.bind(this),"get-font":this.getFont.bind(this),"get-display-size":this.getDisplaySize.bind(this),"load-css":this.loadCSS.bind(this),"update-cursor-shape":this.updateCursorShape.bind(this)}),this.login(),this.boundedHandleResize=this.handleResize.bind(this),this.focusHiddenInput=this.focusHiddenInput.bind(this)}init(){window.addEventListener(`resize`,this.boundedHandleResize),document.getElementsByTagName(`html`)[0].style[`background-color`]=`#333`,getLemEditorElement().appendChild(this.cursorOverlay)}finalize(){window.removeEventListener(`resize`,this.boundedHandleResize),this.input.finalize(),this.cursorOverlay.parentNode&&this.cursorOverlay.parentNode.removeChild(this.cursorOverlay)}closeConnection(){this.jsonrpc.close()}emitInput(e){let t=convertKeyEvent(e);if(t){if(t.key===`]`&&t.ctrl&&!t.meta&&!t.super&&!t.shift){this.jsonrpc.notify(`input`,{kind:`abort`});return}t.key!==`Unidentified`&&this.jsonrpc.notify(`input`,{kind:`key`,value:t})}}emitInputString(e){e?this.jsonrpc.notify(`input`,{kind:`input-string`,value:e}):console.error(`unexpected argument`,e)}handleResize(e){let t=!0;this.jsonrpc.notify(`redraw`,{size:this.getDisplaySize()})}focusHiddenInput(){let e=this.input?.input;if(e){try{window.focus()}catch{}requestAnimationFrame(()=>{setTimeout(()=>{e.focus({preventScroll:!0})},0)})}}sendNotification(e,t){this.jsonrpc.notify(e,t)}request(e,t,n){this.jsonrpc.request(e,t,n)}getDisplaySize(){let[e,t,n,r]=this.getDisplayRectangle();return{width:Math.floor(n/this.option.fontWidth),height:Math.floor(r/this.option.fontHeight)}}callMessage(e,t){this.messageTable.get(e)(t)}findViewById(e){return this.viewMap.get(e)}login(){this.jsonrpc.request(`login`,{size:this.getDisplaySize(),foreground:this.option.foreground,background:this.option.background,fontWidth:this.option.fontWidth,fontHeight:this.option.fontHeight},e=>{if(this.updateForeground(e.foreground),this.updateBackground(e.background),e.views)for(let t of e.views)this.makeView(t);this.jsonrpc.notify(`redraw`,{size:this.getDisplaySize()})})}updateForeground(e){e!=null&&(this.option.foreground=e,this.input.updateForeground(e))}updateBackground(e){if(e==null)return;this.option.background=e,this.input.updateBackground(e);let t=getLemEditorElement();t.style.backgroundColor=e}makeView({id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,use_modeline:l,kind:u,type:d,content:f,border:p,border_shape:m}){let h=new View({option:this.option,id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,useModeline:l,kind:u,type:d,content:f,border:p,borderShape:m,editor:this});this.viewMap.set(e,h)}deleteView({viewInfo:{id:e}}){this.findViewById(e).delete(),this.viewMap.delete(e)}resize({viewInfo:{id:e},width:t,height:n,pixelWidth:r,pixelHeight:i}){let a=this.findViewById(e);a?a.resize(t,n,r,i):console.warn(`resize: view not found for id ${e}`)}move({viewInfo:{id:e},x:t,y:n,pixelX:r,pixelY:i}){let a=this.findViewById(e);a?a.move(t,n,r,i):console.warn(`move: view not found for id ${e}`)}redrawViewAfter({viewInfo:{id:e},isActive:t}){this.findViewById(e).touch(t)}clear({viewInfo:{id:e}}){this.findViewById(e).clear()}clearEol({viewInfo:{id:e},x:t,y:n,height:r}){this.findViewById(e).clearEol(t,n,r)}clearEob({viewInfo:{id:e},x:t,y:n}){this.findViewById(e).clearEob(t,n)}put({viewInfo:{id:e},x:t,y:n,text:r,textWidth:i,attribute:a,font:o}){this.findViewById(e).print(t,n,r,i,a,o)}putImage({viewInfo:{id:e},x:t,y:n,width:r,height:i,pixelWidth:a,pixelHeight:o,url:s}){this.findViewById(e).printImage(t,n,r,i,a,o,s)}modelinePut({viewInfo:{id:e},x:t,y:n,text:r,textWidth:i,attribute:a}){this.findViewById(e).printToModeline(t,n,r,i,a)}updateDisplay(){}moveCursor({viewInfo:{id:e},x:t,y:n,color:r,cursorText:i,cursorForeground:a}){let o=this.findViewById(e),[s,c]=this.getDisplayRectangle(),l=o.x*this.option.fontWidth+t*this.option.fontWidth,u=o.y*this.option.fontHeight+n*this.option.fontHeight;this.input.move(l,u);let d=r||this.option.foreground,f=a||this.option.background,p=this.cursorOverlay;switch(this.cursorType){case`bar`:p.style.left=s+l+`px`,p.style.top=c+u+`px`,p.style.width=`2px`,p.style.height=this.option.fontHeight+`px`,p.style.backgroundColor=d,p.textContent=``,p.style.color=``,p.style.font=``,p.style.paddingTop=``;break;case`underline`:p.style.left=s+l+`px`,p.style.top=c+u+this.option.fontHeight-2+`px`,p.style.width=this.option.fontWidth+`px`,p.style.height=`2px`,p.style.backgroundColor=d,p.textContent=``,p.style.color=``,p.style.font=``,p.style.paddingTop=``;break;default:p.style.left=s+l+`px`,p.style.top=c+u+`px`,p.style.width=this.option.fontWidth+`px`,p.style.height=this.option.fontHeight+`px`,p.style.backgroundColor=d,p.style.font=this.option.font,p.style.paddingTop=textOffsetY+`px`,p.textContent=i||``,p.style.color=f;break}p.style.animation=`none`,p.offsetHeight,p.style.animation=``}updateCursorShape({cursorType:e}){this.cursorType=e||`box`}changeView({viewInfo:{id:e},type:t,content:n}){let r=this.findViewById(e);switch(t){case`html`:r.changeToHTMLContent(n);break;case`editor`:r.changeToEditorContent();break}}resizeDisplay({width:e,height:t}){let n=getLemEditorElement();n.style.width=Math.floor(e*this.option.fontWidth)+`px`,n.style.height=Math.floor(t*this.option.fontHeight)+`px`}bulk(e){for(let{method:t,argument:n}of e)this.callMessage(t,n)}exitEditor(){this.onExit&&this.onExit()}getClipboardText(){navigator.clipboard?.readText().then(e=>{this.jsonrpc.notify(`got-clipboard-text`,{text:e})})}setClipboardText({text:e}){navigator.clipboard&&navigator.clipboard.writeText(e)}jsEval({viewInfo:{id:e},code:t}){let n=this.findViewById(e).evalIn(t);return n&&n.toString()}setFont({fontName:e,fontSize:t}){this.option.setFont(e||this.option.fontName,t||this.option.fontSize)}getFont(){return{name:this.option.fontName,size:this.option.fontSize}}loadCSS({content:e}){let t=document.createElement(`style`);t.textContent=e,document.head.appendChild(t)}notifyToServer(e,t){this.jsonrpc.notify(`invoke`,{method:e,args:t})}},canvas=document.querySelector(`#editor`);async function main(){await Promise.all([document.fonts.load(`19px file-icons`),document.fonts.load(`19px AllTheIcons`),document.fonts.load(`19px fontawesome`),document.fonts.load(`19px material-design-icons`),document.fonts.load(`19px octicons`)]),await document.fonts.ready;let e=new Editor({canvas,fontName:`Monospace`,fontSize:18,url:`${window.location.protocol===`https:`?`wss`:`ws`}://${window.location.hostname}:${window.location.port}`,onExit:null,onClosed:null});window.addEventListener(`message`,t=>{t.data.type===`invoke-lem`&&e.notifyToServer(t.data.method,t.data.args)}),e.init()}main(); \ No newline at end of file diff --git a/frontends/server/frontend/editor.js b/frontends/server/frontend/editor.js index 55027113a..c52afb54b 100644 --- a/frontends/server/frontend/editor.js +++ b/frontends/server/frontend/editor.js @@ -346,6 +346,10 @@ class BaseSurface { drawBlock(x, y, width, height, color) { } drawText(x, y, text, textWidth, attribute) { } + drawImage(x, y, widthCells, heightCells, pixelWidth, pixelHeight, url) { } + + clearImages(yStart, yEnd) { } + clearAllImages() { } touch() { return; @@ -388,6 +392,18 @@ class CanvasSurface extends BaseSurface { ctx.scale(ratio, ratio); } + move(x, y, pixelX, pixelY) { + super.move(x, y, pixelX, pixelY); + if (this.imageEls) { + for (const [, entry] of this.imageEls) this.positionImage(entry); + } + } + + delete() { + this.clearAllImages(); + super.delete(); + } + drawBlock(x, y, width, height, color) { const option = this.editor.option; this.drawingQueue.push(function(ctx) { @@ -475,6 +491,69 @@ class CanvasSurface extends BaseSurface { }); } + // images are rendered as DOM elements on a layer above the canvas rather than into it. + imageBaseLeft() { return parseFloat(this.mainDOM.style.left) || 0; } + imageBaseTop() { return parseFloat(this.mainDOM.style.top) || 0; } + + drawImage(x, y, widthCells, heightCells, pixelWidth, pixelHeight, url) { + if (!this.imageEls) + // mapping "x,y" to { el, url, x, y, widthCells, heightCells, pixelWidth, pixelHeight } + this.imageEls = new Map(); + const key = x + ',' + y; + let entry = this.imageEls.get(key); + if (entry && entry.url !== url) { + entry.el.remove(); + this.imageEls.delete(key); + entry = null; + } + if (!entry) { + const el = document.createElement('img'); + el.style.position = 'absolute'; + el.style.pointerEvents = 'none'; + // above the surface's own canvas but below the modeline/floating windows. + el.style.zIndex = '1'; + el.src = url; + this.mainDOM.parentNode.appendChild(el); + entry = { el, url }; + this.imageEls.set(key, entry); + } + entry.x = x; + entry.y = y; + entry.widthCells = widthCells; + entry.heightCells = heightCells; + entry.pixelWidth = pixelWidth; + entry.pixelHeight = pixelHeight; + this.positionImage(entry); + } + + positionImage(entry) { + const option = this.editor.option; + // we reserved widthCells x heightCells cells for this image. + const boxWidth = entry.widthCells * option.fontWidth; + const boxHeight = entry.heightCells * option.fontHeight; + entry.el.style.left = (this.imageBaseLeft() + entry.x * option.fontWidth) + 'px'; + entry.el.style.top = (this.imageBaseTop() + entry.y * option.fontHeight) + 'px'; + entry.el.style.width = (entry.pixelWidth != null ? Math.min(entry.pixelWidth, boxWidth) : boxWidth) + 'px'; + entry.el.style.height = (entry.pixelHeight != null ? Math.min(entry.pixelHeight, boxHeight) : boxHeight) + 'px'; + } + + // remove image elements whose row span intersects [yStart, yEnd). + clearImages(yStart, yEnd) { + if (!this.imageEls) return; + for (const [key, entry] of this.imageEls) { + if (entry.y < yEnd && (entry.y + entry.heightCells) > yStart) { + entry.el.remove(); + this.imageEls.delete(key); + } + } + } + + clearAllImages() { + if (!this.imageEls) return; + for (const [, entry] of this.imageEls) entry.el.remove(); + this.imageEls.clear(); + } + touch() { const ctx = this.mainDOM.getContext('2d'); for (let fn of this.drawingQueue) { @@ -778,14 +857,15 @@ class View { ); } - clearEol(x, y) { + clearEol(x, y, height=1) { this.mainSurface.drawBlock( x, y, this.width - x, - 1, + height, this.option.background, ); + this.mainSurface.clearImages(y, y + height); } clearEob(x, y) { @@ -796,6 +876,7 @@ class View { this.height - y, this.option.background, ); + this.mainSurface.clearImages(y, this.height); } print(x, y, text, textWidth, attribute, font) { @@ -809,6 +890,10 @@ class View { ); } + printImage(x, y, width, height, pixelWidth, pixelHeight, url) { + this.mainSurface.drawImage(x, y, width, height, pixelWidth, pixelHeight, url); + } + printToModeline(x, y, text, textWidth, attribute) { if (this.modelineSurface) { this.modelineSurface.drawText( @@ -1166,6 +1251,7 @@ export class Editor { 'clear-eol': this.clearEol.bind(this), 'clear-eob': this.clearEob.bind(this), 'put': this.put.bind(this), + 'put-image': this.putImage.bind(this), 'modeline-put': this.modelinePut.bind(this), 'update-display': this.updateDisplay.bind(this), 'move-cursor': this.moveCursor.bind(this), @@ -1280,6 +1366,8 @@ export class Editor { size: this.getDisplaySize(), foreground: this.option.foreground, background: this.option.background, + fontWidth: this.option.fontWidth, + fontHeight: this.option.fontHeight, }, (response) => { this.updateForeground(response.foreground); this.updateBackground(response.background); @@ -1364,9 +1452,9 @@ export class Editor { view.clear(); } - clearEol({ viewInfo: { id }, x, y }) { + clearEol({ viewInfo: { id }, x, y, height }) { const view = this.findViewById(id); - view.clearEol(x, y); + view.clearEol(x, y, height); } clearEob({ viewInfo: { id }, x, y }) { @@ -1379,6 +1467,11 @@ export class Editor { view.print(x, y, text, textWidth, attribute, font); } + putImage({ viewInfo: { id }, x, y, width, height, pixelWidth, pixelHeight, url }) { + const view = this.findViewById(id); + view.printImage(x, y, width, height, pixelWidth, pixelHeight, url); + } + modelinePut({ viewInfo: { id }, x, y, text, textWidth, attribute }) { const view = this.findViewById(id); view.printToModeline(x, y, text, textWidth, attribute); diff --git a/frontends/server/main.lisp b/frontends/server/main.lisp index aa6b8570b..4a30ed42e 100644 --- a/frontends/server/main.lisp +++ b/frontends/server/main.lisp @@ -101,7 +101,12 @@ (message-queue :initform (queue:make-queue) :reader jsonrpc-message-queue) (editor-thread :initform nil - :accessor jsonrpc-editor-thread)) + :accessor jsonrpc-editor-thread) + ;; pixel size of one character cell, reported by the client. + (cell-width :initform nil + :accessor jsonrpc-cell-width) + (cell-height :initform nil + :accessor jsonrpc-cell-height)) (:default-initargs :name :jsonrpc :redraw-after-modifying-floating-window t @@ -162,6 +167,10 @@ the same immutable instance for every subsequent message." (let ((width (gethash "width" size)) (height (gethash "height" size))) (resize-display jsonrpc width height))) + (alexandria:when-let ((fw (gethash "fontWidth" params))) + (when (plusp fw) (setf (jsonrpc-cell-width jsonrpc) fw))) + (alexandria:when-let ((fh (gethash "fontHeight" params))) + (when (plusp fh) (setf (jsonrpc-cell-height jsonrpc) fh))) (when background (alexandria:when-let (color (lem:parse-color background)) (setf (jsonrpc-background-color jsonrpc) color))) @@ -584,7 +593,34 @@ the same immutable instance for every subsequent message." (lem-core:string-width (lem-core/display:text-object-string drawing-object))) (defmethod object-width ((drawing-object display:image-object)) - 0) + ;; width in character cells. when :pixel-width is given (and the client's cell size is known), + ;; round it up to whole cells so the column reserves enough grid space. otherwise use :width + ;; (a cell count) from the attribute. + (let ((pw (image-pixel-dimension drawing-object :pixel-width)) + (cw (jsonrpc-cell-width (lem-core:implementation)))) + (if (and pw cw) + (ceiling pw cw) + (or (display:image-object-width drawing-object) 1)))) + +(defgeneric object-height (drawing-object) + (:documentation "height of DRAWING-OBJECT in character cells. +we advance the vertical position of the next line by the tallest object's height (see +`max-height-of-objects'), so returning more than 1 for an image makes its line grow to fit.")) + +(defmethod object-height (drawing-object) + 1) + +(defmethod object-height ((drawing-object display:image-object)) + (let ((ph (image-pixel-dimension drawing-object :pixel-height)) + (ch (jsonrpc-cell-height (lem-core:implementation)))) + (if (and ph ch) + (ceiling ph ch) + (or (display:image-object-height drawing-object) 1)))) + +(defun image-pixel-dimension (object key) + "pixel value of KEY (:pixel-width / :pixel-height) on OBJECT's attribute, or NIL." + (let ((attribute (display:image-object-attribute object))) + (and attribute (lem:attribute-value attribute key)))) (defgeneric draw-object (jsonrpc object x y view)) @@ -703,8 +739,42 @@ same hash." attribute :text-width width))) +(defun image-object-url (object) + "return a URL the JS client can load for OBJECT's image, or NIL. +a pathname or plain-string path is served through the existing /local static route. +a string already carrying a data:/https: URL is passed through unchanged." + (let ((image (display:image-object-image object))) + (typecase image + (pathname (format nil "/local~A" (namestring image))) + (string (if (or (alexandria:starts-with-subseq "data:" image) + (alexandria:starts-with-subseq "http:" image) + (alexandria:starts-with-subseq "https:" image)) + image + (format nil "/local~A" image))) + (t nil)))) + (defmethod draw-object (jsonrpc (object display:image-object) x y view) - (values)) + (let ((url (image-object-url object))) + (when url + (with-error-handler () + ;; use the attribute's :pixel-width/:pixel-height if given, else the reserved cell box + ;; (cells * cell pixel size) when the cell size is known. + (let ((pw (or (image-pixel-dimension object :pixel-width) + (alexandria:when-let ((cw (jsonrpc-cell-width jsonrpc))) + (* (object-width object) cw)))) + (ph (or (image-pixel-dimension object :pixel-height) + (alexandria:when-let ((ch (jsonrpc-cell-height jsonrpc))) + (* (object-height object) ch))))) + (notify* jsonrpc + "put-image" + (hash "viewInfo" (view-id-hash view) + "x" x + "y" y + "width" (object-width object) + "height" (object-height object) + "pixelWidth" pw + "pixelHeight" ph + "url" url))))))) (defun render-line (jsonrpc view x y objects) (loop :for object :in objects @@ -719,11 +789,14 @@ same hash." (defmethod lem-if:render-line ((jsonrpc jsonrpc) view x y objects height) (with-error-handler () + ;; clear the line's full height (not just one row) since a tall object such as an image may + ;; occupy several rows. (notify* jsonrpc "clear-eol" (hash "viewInfo" (view-id-hash view) "x" x - "y" y)) + "y" y + "height" height)) (render-line jsonrpc view x y objects))) (defmethod lem-if:render-line-on-modeline ((jsonrpc jsonrpc) view left-objects right-objects @@ -745,7 +818,7 @@ same hash." (object-width drawing-object)) (defmethod lem-if:object-height ((jsonrpc jsonrpc) drawing-object) - 1) + (object-height drawing-object)) (defmethod lem-if:clear-to-end-of-window ((jsonrpc jsonrpc) view y) (notify* jsonrpc diff --git a/src/display/physical-line.lisp b/src/display/physical-line.lisp index 9fb31f600..fa905ea48 100644 --- a/src/display/physical-line.lisp +++ b/src/display/physical-line.lisp @@ -103,9 +103,9 @@ (line-end-object-offset drawing-object-2)))) (defmethod drawing-object-equal ((drawing-object-1 image-object) (drawing-object-2 image-object)) - (and (eq (image-object-image drawing-object-1) (image-object-image drawing-object-1)) - (equal (image-object-width drawing-object-1) (image-object-width drawing-object-1)) - (equal (image-object-height drawing-object-1) (image-object-height drawing-object-1)))) + (and (eq (image-object-image drawing-object-1) (image-object-image drawing-object-2)) + (equal (image-object-width drawing-object-1) (image-object-width drawing-object-2)) + (equal (image-object-height drawing-object-1) (image-object-height drawing-object-2)))) (defgeneric drawing-object-mergable-p (drawing-object-1 drawing-object-2)) @@ -137,12 +137,6 @@ (equal (line-end-object-offset drawing-object-1) (line-end-object-offset drawing-object-2)))) -(defmethod drawing-object-mergable-p ((drawing-object-1 image-object) (drawing-object-2 image-object)) - (and (eq (image-object-image drawing-object-1) (image-object-image drawing-object-1)) - (equal (image-object-width drawing-object-1) (image-object-width drawing-object-1)) - (equal (image-object-height drawing-object-1) (image-object-height drawing-object-1)))) - - (defgeneric drawing-object-merge (drawing-object-1 drawing-object-2)) (defmethod drawing-object-merge ((drawing-object-1 void-object) (drawing-object-2 void-object)) diff --git a/src/internal-packages.lisp b/src/internal-packages.lisp index 8a6e8b04e..22b461bb7 100644 --- a/src/internal-packages.lisp +++ b/src/internal-packages.lisp @@ -14,6 +14,7 @@ :folder-object :icon-object :image-object + :image-object-attribute :image-object-height :image-object-image :image-object-width From 580e51171b0783f67ea946ac3bfdfe1ad84f56c5 Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Tue, 28 Jul 2026 21:58:10 +0300 Subject: [PATCH 10/26] evict drawing-cache entries a tall line overlaps --- src/display/physical-line.lisp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/display/physical-line.lisp b/src/display/physical-line.lisp index fa905ea48..36b174cfb 100644 --- a/src/display/physical-line.lisp +++ b/src/display/physical-line.lisp @@ -371,14 +371,18 @@ Assumes inputs are already reduced (no adjacent mergeable objects)." (drawing-objects-equal objects cache-objects)) :return t)) +(defun remove-drawing-cache-entries-overlapping (entries y height) + "Return ENTRIES with every entry whose rows overlap [Y, Y+HEIGHT) removed." + (remove-if (lambda (elt) + (destructuring-bind (cache-y cache-height drawing-objects) elt + (declare (ignore drawing-objects)) + (and (< cache-y (+ y height)) + (< y (+ cache-y cache-height))))) + entries)) + (defun invalidate-cache (window y height) (setf (drawing-cache window) - (remove-if (lambda (elt) - (destructuring-bind (cache-y cache-height drawing-objects) elt - (declare (ignore drawing-objects)) - (and (<= cache-y y) - (<= (+ y height) (+ cache-y cache-height))))) - (drawing-cache window)))) + (remove-drawing-cache-entries-overlapping (drawing-cache window) y height))) (defun remove-drawing-cache-entries-from (entries y) "Return ENTRIES with drawing-cache rows at or below Y removed. From 6f09e2d553b88f8b78d68585e4139068b0bbed52 Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Tue, 28 Jul 2026 21:58:32 +0300 Subject: [PATCH 11/26] drop the fingerprint entries of the rows a tall line covers --- src/display/physical-line.lisp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/display/physical-line.lisp b/src/display/physical-line.lisp index 36b174cfb..296dfd52a 100644 --- a/src/display/physical-line.lisp +++ b/src/display/physical-line.lisp @@ -545,10 +545,16 @@ over the top-level spine and tolerant of improper (dotted) lists." (when (and found (eql (car entry) fingerprint)) (cdr entry))))) +(defun evict-line-fingerprint-shadow (cache y height) + "Remove entries in CACHE for the rows a HEIGHT-tall line at Y covers." + (loop :for row :from (1+ y) :below (+ y height) + :do (remhash row cache))) + (defun update-line-fingerprint (window y fingerprint height) - "Store the fingerprint and height for line at Y." - (setf (gethash y (line-fingerprint-cache window)) - (cons fingerprint height))) + "Store the fingerprint and height for line at Y, and drop the rows it covers." + (let ((cache (line-fingerprint-cache window))) + (setf (gethash y cache) (cons fingerprint height)) + (evict-line-fingerprint-shadow cache y height))) (defun redraw-logical-line-when-line-wrapping (window y From e4e19a8cbcfe3a1032b6afe9b0feb0f3a5cfd444 Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Fri, 31 Jul 2026 19:30:41 +0300 Subject: [PATCH 12/26] rename lem-if:get-char-width/height to cell-width/cell-height they now report the size of one character cell in the frontend's layout units (1 on terminal and webview, pixels on sdl2) --- extensions/pixel-demo/pixel-demo.lisp | 12 ++++++------ frontends/fake-interface/fake-interface.lisp | 4 ++-- frontends/ncurses/ncurses.lisp | 5 ++++- frontends/sdl2/main.lisp | 4 ++-- frontends/sdl2/tree.lisp | 2 +- frontends/server/main.lisp | 4 ++-- src/display/physical-line.lisp | 2 +- src/interface.lisp | 10 ++++++++-- src/internal-packages.lisp | 4 ++-- src/mouse.lisp | 4 ++-- src/window/floating-window.lisp | 4 ++-- 11 files changed, 32 insertions(+), 23 deletions(-) diff --git a/extensions/pixel-demo/pixel-demo.lisp b/extensions/pixel-demo/pixel-demo.lisp index 9013df18b..9bbb53dac 100644 --- a/extensions/pixel-demo/pixel-demo.lisp +++ b/extensions/pixel-demo/pixel-demo.lisp @@ -64,8 +64,8 @@ (defun animation-step () "Perform one animation step." (when (and *demo-window* (eq *demo-mode* :animate)) - (let* ((char-width (lem-if:get-char-width (implementation))) - (char-height (lem-if:get-char-height (implementation))) + (let* ((char-width (lem-if:cell-width (implementation))) + (char-height (lem-if:cell-height (implementation))) (display-width (* (display-width) char-width)) (display-height (* (display-height) char-height)) (win-width (or (floating-window-pixel-width *demo-window*) @@ -177,8 +177,8 @@ A floating window follows your mouse cursor at pixel precision." "Update coordinate debug display." (when (and *demo-window* (eq *demo-mode* :debug)) (let* ((mouse-event (lem-core::last-mouse-event)) - (char-width (lem-if:get-char-width (implementation))) - (char-height (lem-if:get-char-height (implementation)))) + (char-width (lem-if:cell-width (implementation))) + (char-height (lem-if:cell-height (implementation)))) (multiple-value-bind (win-px win-py win-pw win-ph) (floating-window-pixel-bounds *demo-window*) (let ((content @@ -230,8 +230,8 @@ Shows real-time pixel and character coordinate information." (defun compare-animation-step () "Animate both windows for comparison." (when (eq *demo-mode* :compare) - (let* ((char-width (lem-if:get-char-width (implementation))) - (char-height (lem-if:get-char-height (implementation))) + (let* ((char-width (lem-if:cell-width (implementation))) + (char-height (lem-if:cell-height (implementation))) (display-width (* (display-width) char-width)) (display-height (* (display-height) char-height)) (win-width (* 20 char-width))) diff --git a/frontends/fake-interface/fake-interface.lisp b/frontends/fake-interface/fake-interface.lisp index 1f9aa94d0..1e676e9fe 100644 --- a/frontends/fake-interface/fake-interface.lisp +++ b/frontends/fake-interface/fake-interface.lisp @@ -103,10 +103,10 @@ (defmethod lem-if:clear-to-end-of-window ((implementation fake-interface) view y) nil) -(defmethod lem-if:get-char-width ((implementation fake-interface)) +(defmethod lem-if:cell-width ((implementation fake-interface)) 1) -(defmethod lem-if:get-char-height ((implementation fake-interface)) +(defmethod lem-if:cell-height ((implementation fake-interface)) 1) (defmethod lem-if:render-line-on-modeline ((implementation fake-interface) diff --git a/frontends/ncurses/ncurses.lisp b/frontends/ncurses/ncurses.lisp index 5f1c60bd3..61b168604 100644 --- a/frontends/ncurses/ncurses.lisp +++ b/frontends/ncurses/ncurses.lisp @@ -106,7 +106,10 @@ (defmethod lem-if:clear-to-end-of-window ((implementation ncurses) view y) (lem-ncurses/render:clear-to-end-of-window view y)) -(defmethod lem-if:get-char-width ((implementation ncurses)) +(defmethod lem-if:cell-width ((implementation ncurses)) + 1) + +(defmethod lem-if:cell-height ((implementation ncurses)) 1) ;; for mouse control diff --git a/frontends/sdl2/main.lisp b/frontends/sdl2/main.lisp index 3940e6ef3..278c0a60b 100644 --- a/frontends/sdl2/main.lisp +++ b/frontends/sdl2/main.lisp @@ -470,11 +470,11 @@ (values (display:scaled-char-width display x) (display:scaled-char-height display y)))))) -(defmethod lem-if:get-char-width ((implementation sdl2)) +(defmethod lem-if:cell-width ((implementation sdl2)) (display:with-display (display) (display:display-char-width display))) -(defmethod lem-if:get-char-height ((implementation sdl2)) +(defmethod lem-if:cell-height ((implementation sdl2)) (display:with-display (display) (display:display-char-height display))) diff --git a/frontends/sdl2/tree.lisp b/frontends/sdl2/tree.lisp index 998635296..036e17d22 100644 --- a/frontends/sdl2/tree.lisp +++ b/frontends/sdl2/tree.lisp @@ -29,7 +29,7 @@ (defmethod tree-view-scroll-vertically ((buffer tree-view-buffer) window n) (incf (tree-view-buffer-scroll-y buffer) n) (let* ((height (* (1- (window-height window)) - (lem-if:get-char-height (implementation)))) + (lem-if:cell-height (implementation)))) (last-y (max 0 (- (tree-view-buffer-height buffer) height)))) (cond ((< last-y (tree-view-buffer-scroll-y buffer)) diff --git a/frontends/server/main.lisp b/frontends/server/main.lisp index 4a30ed42e..2deac249c 100644 --- a/frontends/server/main.lisp +++ b/frontends/server/main.lisp @@ -470,10 +470,10 @@ the same immutable instance for every subsequent message." (defmethod lem-if:get-mouse-position ((jsonrpc jsonrpc)) (mouse:get-position)) -(defmethod lem-if:get-char-width ((jsonrpc jsonrpc)) +(defmethod lem-if:cell-width ((jsonrpc jsonrpc)) ;; TODO 1) -(defmethod lem-if:get-char-height ((jsonrpc jsonrpc)) +(defmethod lem-if:cell-height ((jsonrpc jsonrpc)) ;; TODO 1) diff --git a/src/display/physical-line.lisp b/src/display/physical-line.lisp index 296dfd52a..4c130aebb 100644 --- a/src/display/physical-line.lisp +++ b/src/display/physical-line.lisp @@ -736,7 +736,7 @@ creating zero temporary letter-objects." (invalidate-drawing-cache-from window y) (lem-if:clear-to-end-of-window (implementation) (window-view window) y)) (setf (window-left-width window) - (floor left-side-width (lem-if:get-char-width (implementation))))))) + (floor left-side-width (lem-if:cell-width (implementation))))))) (defun call-with-display-error (function) (handler-bind ((error (lambda (e) diff --git a/src/interface.lisp b/src/interface.lisp index ef762a86a..a4f33a9b6 100644 --- a/src/interface.lisp +++ b/src/interface.lisp @@ -175,8 +175,14 @@ PIXEL-X, PIXEL-Y, PIXEL-WIDTH, PIXEL-HEIGHT are in pixels (may be nil for auto-c (:method (implementation) (values -1 -1))) -(defgeneric lem-if:get-char-width (implementation)) -(defgeneric lem-if:get-char-height (implementation)) +(defgeneric lem-if:cell-width (implementation) + (:documentation "Width of one character cell in the frontend's native layout units. +1 on a cell-based frontend (a terminal counts in cells), pixels on a pixel-based one. These are +the units `object-width' / `object-height' are counted in.")) + +(defgeneric lem-if:cell-height (implementation) + (:documentation "Height of one character cell in the frontend's native layout units. +Unit-relative like `cell-width'.")) (defgeneric lem-if:render-line (implementation view x y objects height)) (defgeneric lem-if:render-line-on-modeline (implementation view left-objects right-objects diff --git a/src/internal-packages.lisp b/src/internal-packages.lisp index 22b461bb7..0e1d14249 100644 --- a/src/internal-packages.lisp +++ b/src/internal-packages.lisp @@ -819,8 +819,8 @@ :get-font-by-name-and-style :get-font :get-mouse-position - :get-char-width - :get-char-height + :cell-width + :cell-height :clear-to-end-of-window :js-eval :render-line diff --git a/src/mouse.lisp b/src/mouse.lisp index c08f9e105..7faa8548d 100644 --- a/src/mouse.lisp +++ b/src/mouse.lisp @@ -64,10 +64,10 @@ (y (mouse-event-pixel-y mouse-event))) (values (- x (* (window-x window) - (lem-if:get-char-width (implementation)))) + (lem-if:cell-width (implementation)))) (- y (* (window-y window) - (lem-if:get-char-height (implementation))))))) + (lem-if:cell-height (implementation))))))) (defun get-point-from-window-with-coordinates (window x y &optional (allow-overflow-column t)) (with-point ((point (buffer-point (window-buffer window)))) diff --git a/src/window/floating-window.lisp b/src/window/floating-window.lisp index d37516a1d..ca93109d5 100644 --- a/src/window/floating-window.lisp +++ b/src/window/floating-window.lisp @@ -142,8 +142,8 @@ This updates the window's pixel dimensions and notifies the frontend." Returns (values pixel-x pixel-y pixel-width pixel-height). If pixel coordinates are not set, calculates from character coordinates." (check-type window floating-window) - (let ((char-width (lem-if:get-char-width (implementation))) - (char-height (lem-if:get-char-height (implementation)))) + (let ((char-width (lem-if:cell-width (implementation))) + (char-height (lem-if:cell-height (implementation)))) (values (or (floating-window-pixel-x window) (* (window-x window) char-width)) (or (floating-window-pixel-y window) From 3d2ea17c52028d81c31a4e7dd24e91378dc4b3f3 Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Fri, 31 Jul 2026 19:47:25 +0300 Subject: [PATCH 13/26] move default object-width/object-height into the core every frontend measured the same way, so the arithmetic now lives once in physical-line.lisp. frontends keep a method only for what they draw at another size. an image with no size of its own asks for one through the new lem-if:image-natural-size. --- frontends/ncurses/drawing-object.lisp | 29 ------ frontends/ncurses/lem-ncurses.asd | 1 - frontends/ncurses/ncurses.lisp | 10 +- frontends/ncurses/render.lisp | 4 +- frontends/sdl2/drawing.lisp | 136 +++++--------------------- src/display/physical-line.lisp | 36 +++++++ src/interface.lisp | 24 ++++- src/internal-packages.lisp | 5 + 8 files changed, 98 insertions(+), 147 deletions(-) delete mode 100644 frontends/ncurses/drawing-object.lisp diff --git a/frontends/ncurses/drawing-object.lisp b/frontends/ncurses/drawing-object.lisp deleted file mode 100644 index b0e92122d..000000000 --- a/frontends/ncurses/drawing-object.lisp +++ /dev/null @@ -1,29 +0,0 @@ -(defpackage :lem-ncurses/drawing-object - (:use :cl - :lem-core/display) - (:export :object-width - :object-height)) -(in-package :lem-ncurses/drawing-object) - -(defgeneric object-width (drawing-object)) - -(defmethod object-width ((drawing-object void-object)) - 0) - -(defmethod object-width ((drawing-object text-object)) - (lem-core:string-width (text-object-string drawing-object))) - -(defmethod object-width ((drawing-object eol-cursor-object)) - 0) - -(defmethod object-width ((drawing-object extend-to-eol-object)) - 0) - -(defmethod object-width ((drawing-object line-end-object)) - (lem-core:string-width (text-object-string drawing-object))) - -(defmethod object-width ((drawing-object image-object)) - 0) - -(defmethod object-height (drawing-object) - 1) diff --git a/frontends/ncurses/lem-ncurses.asd b/frontends/ncurses/lem-ncurses.asd index 1c33af19f..2036ca22f 100644 --- a/frontends/ncurses/lem-ncurses.asd +++ b/frontends/ncurses/lem-ncurses.asd @@ -12,7 +12,6 @@ (:file "style") (:file "key") (:file "attribute") - (:file "drawing-object") (:file "view") (:file "render") (:file "input") diff --git a/frontends/ncurses/ncurses.lisp b/frontends/ncurses/ncurses.lisp index 61b168604..e1df0eaf4 100644 --- a/frontends/ncurses/ncurses.lisp +++ b/frontends/ncurses/ncurses.lisp @@ -97,11 +97,13 @@ height) (lem-ncurses/render:render-line-on-modeline view left-objects right-objects default-attribute)) -(defmethod lem-if:object-width ((implementation ncurses) drawing-object) - (lem-ncurses/drawing-object:object-width drawing-object)) +(defmethod lem-if:object-width ((implementation ncurses) + (drawing-object lem-core/display:image-object)) + 0) -(defmethod lem-if:object-height ((implementation ncurses) drawing-object) - (lem-ncurses/drawing-object:object-height drawing-object)) +(defmethod lem-if:object-height ((implementation ncurses) + (drawing-object lem-core/display:image-object)) + 1) (defmethod lem-if:clear-to-end-of-window ((implementation ncurses) view y) (lem-ncurses/render:clear-to-end-of-window view y)) diff --git a/frontends/ncurses/render.lisp b/frontends/ncurses/render.lisp index e67f4387b..3065c74a6 100644 --- a/frontends/ncurses/render.lisp +++ b/frontends/ncurses/render.lisp @@ -64,7 +64,7 @@ (defun render-line-from-behind (view y objects scrwin) (loop :with current-x := (lem-if:view-width (lem:implementation) view) :for object :in objects - :do (decf current-x (lem-ncurses/drawing-object:object-width object)) + :do (decf current-x (lem-if:object-width (lem:implementation) object)) (draw-object object current-x y view scrwin))) (defun clear-line (view x y) @@ -74,7 +74,7 @@ (defun %render-line (view x y objects scrwin) (loop :for object :in objects :do (draw-object object x y view scrwin) - (incf x (lem-ncurses/drawing-object:object-width object)))) + (incf x (lem-if:object-width (lem:implementation) object)))) (defun render-line (view x y objects) (clear-line view x y) diff --git a/frontends/sdl2/drawing.lisp b/frontends/sdl2/drawing.lisp index e941f850f..2ea30b7c8 100644 --- a/frontends/sdl2/drawing.lisp +++ b/frontends/sdl2/drawing.lisp @@ -65,100 +65,18 @@ Uses a sentinel key so it participates in the normal cache lifecycle "__folder_icon__" nil :folder surface) surface))) -(defgeneric object-width (drawing-object display)) +(defmethod lem-if:image-natural-size ((implementation lem-sdl2/sdl2:sdl2) image) + (values (sdl2:surface-width image) (sdl2:surface-height image))) -(defmethod object-width ((drawing-object void-object) display) - 0) - -(defun text-cell-width (drawing-object display) - "Cell-aligned pixel width of a text-object: string-width × char-width. -Mirrors lem-ncurses/drawing-object:object-width semantics (logical -column width) so SDL2 text aligns on the character grid regardless of -per-string SDL_ttf surface-width drift." - (* (lem-core:string-width (text-object-string drawing-object)) - (display:display-char-width display))) - -(defmethod object-width ((drawing-object text-object) display) - (text-cell-width drawing-object display)) - -(defmethod object-width ((drawing-object control-character-object) display) - (* 2 (display:display-char-width display))) - -(defmethod object-width ((drawing-object icon-object) display) - ;; Cell-aligned advance (typically 2 * char-width). The icon font's natural - ;; glyph surface is usually wider than this; draw-object scales it to fit. - (text-cell-width drawing-object display)) - -(defmethod object-width ((drawing-object folder-object) display) - (* 2 (display:display-char-width display))) - -(defmethod object-width ((drawing-object emoji-object) display) - (* (display:display-char-width display) 2 (length (text-object-string drawing-object)))) - -(defmethod object-width ((drawing-object eol-cursor-object) display) - 0) - -(defmethod object-width ((drawing-object extend-to-eol-object) display) - 0) - -(defmethod object-width ((drawing-object line-end-object) display) - (text-cell-width drawing-object display)) - -(defmethod object-width ((drawing-object image-object) display) - (or (image-object-width drawing-object) - (sdl2:surface-width (image-object-image drawing-object)))) - - -(defgeneric object-height (drawing-object display)) - -(defmethod object-height ((drawing-object void-object) display) - (display:display-char-height display)) - -(defmethod object-height ((drawing-object text-object) display) - ;; Use the stable row cell-height (derived from font metrics at the - ;; display level) rather than the per-string SDL_ttf surface height. - ;; SDL_ttf can return slightly different surface heights for different - ;; strings (e.g. ones containing descenders like `p'/`g'/`y' versus - ;; ones without), which would otherwise leak into the background - ;; rectangle drawn by `draw-text-glyph-surface', producing the - ;; uneven-extent "padding around the problem letters" artefact at - ;; attribute boundaries on a highlighted row. The natural surface - ;; height is still read inside `draw-text-glyph-surface' directly - ;; from the surface for baseline-anchored glyph blitting. - (display:display-char-height display)) - -(defmethod object-height ((drawing-object icon-object) display) - (display:display-char-height display)) - -(defmethod object-height ((drawing-object control-character-object) display) - (display:display-char-height display)) - -(defmethod object-height ((drawing-object folder-object) display) - (display:display-char-height display)) - -(defmethod object-height ((drawing-object emoji-object) display) - (display:display-char-height display)) - -(defmethod object-height ((drawing-object eol-cursor-object) display) - (display:display-char-height display)) - -(defmethod object-height ((drawing-object extend-to-eol-object) display) - (display:display-char-height display)) - -(defmethod object-height ((drawing-object line-end-object) display) - (display:display-char-height display)) - -(defmethod object-height ((drawing-object image-object) display) - (or (image-object-height drawing-object) - (sdl2:surface-height (image-object-image drawing-object)))) - -(defmethod lem-if:object-width ((implementation lem-sdl2/sdl2:sdl2) drawing-object) +(defmethod lem-if:object-width ((implementation lem-sdl2/sdl2:sdl2) + (drawing-object folder-object)) (display:with-display (display) - (object-width drawing-object display))) + (* 2 (display:display-char-width display)))) -(defmethod lem-if:object-height ((implementation lem-sdl2/sdl2:sdl2) drawing-object) +(defmethod lem-if:object-width ((implementation lem-sdl2/sdl2:sdl2) + (drawing-object emoji-object)) (display:with-display (display) - (object-height drawing-object display))) + (* (display:display-char-width display) 2 (length (text-object-string drawing-object))))) (defmethod draw-object ((drawing-object void-object) x bottom-y display view) 0) @@ -208,7 +126,7 @@ is not erased by the next glyph's background fill." (let* ((surface (get-surface drawing-object display)) (surface-width (sdl2:surface-width surface)) (surface-height (sdl2:surface-height surface)) - (cell-height (object-height drawing-object display)) + (cell-height (object-height drawing-object)) (attribute (text-object-attribute drawing-object)) (background (lem-core:attribute-background-with-reverse attribute)) (y (- bottom-y cell-height)) @@ -275,14 +193,14 @@ is not erased by the next glyph's background fill." (or (lem:parse-color underline) (lem-core:attribute-foreground-color attribute)))))))) -(defun text-object-letter-objects-and-widths (drawing-object display) +(defun text-object-letter-objects-and-widths (drawing-object) "Return two parallel lists: per-character letter-objects and their cell widths, for the multi-character text run DRAWING-OBJECT." (let ((attribute (text-object-attribute drawing-object))) (loop :for c :across (text-object-string drawing-object) :for letter := (make-letter-object c attribute) :collect letter :into letters - :collect (object-width letter display) :into widths + :collect (object-width letter) :into widths :finally (return (values letters widths))))) (defun draw-text-object-phase (drawing-object x bottom-y display view phase) @@ -293,11 +211,11 @@ width so the rasterizer's right-edge anti-aliasing tail is preserved." (let ((string (text-object-string drawing-object))) (cond ((<= (length string) 1) (draw-text-glyph-surface drawing-object x bottom-y display view - (object-width drawing-object display) + (object-width drawing-object) :clip t :phase phase)) (t (multiple-value-bind (letter-objects letter-widths) - (text-object-letter-objects-and-widths drawing-object display) + (text-object-letter-objects-and-widths drawing-object) (loop :with current-x := x :for letter-object :in letter-objects :for letter-width :in letter-widths @@ -319,7 +237,7 @@ width so the rasterizer's right-edge anti-aliasing tail is preserved." ;; The cross-text-object equivalent (an adjacent text-object's :bg erasing ;; the previous text-object's AA tail) is handled by `redraw-physical-line', ;; which lifts the two-pass to span the entire physical line. - (let ((total-width (object-width drawing-object display))) + (let ((total-width (object-width drawing-object))) (draw-text-object-phase drawing-object x bottom-y display view :bg) (draw-text-object-phase drawing-object x bottom-y display view :glyph) total-width)) @@ -327,14 +245,14 @@ width so the rasterizer's right-edge anti-aliasing tail is preserved." (defmethod draw-object ((drawing-object icon-object) x bottom-y display view) ;; Icon font glyphs typically render much wider than the 2-cell column the ;; layout reserves for them; draw-text-glyph-surface scales them down to fit. - (let ((cell-width (object-width drawing-object display))) + (let ((cell-width (object-width drawing-object))) (draw-text-glyph-surface drawing-object x bottom-y display view cell-width) cell-width)) (defmethod draw-object ((drawing-object folder-object) x bottom-y display view) ;; Folder PNG surface is much wider than 2 cells; render once and scale to fit. ;; Overrides the text-object per-character loop so the icon stays atomic. - (let ((cell-width (object-width drawing-object display))) + (let ((cell-width (object-width drawing-object))) (draw-text-glyph-surface drawing-object x bottom-y display view cell-width) cell-width)) @@ -343,21 +261,21 @@ width so the rasterizer's right-edge anti-aliasing tail is preserved." ;; ZWJ sequences, ...) that must be rendered as one composed glyph. Use the ;; single-surface path with cell-aligned scaling so we neither split the ;; sequence per-codepoint nor let an oversized emoji surface spill over. - (let ((cell-width (object-width drawing-object display))) + (let ((cell-width (object-width drawing-object))) (draw-text-glyph-surface drawing-object x bottom-y display view cell-width) cell-width)) (defmethod draw-object ((drawing-object eol-cursor-object) x bottom-y display view) (display:set-render-color display (eol-cursor-object-color drawing-object)) - (let ((y (- bottom-y (object-height drawing-object display)))) + (let ((y (- bottom-y (object-height drawing-object)))) (lem-sdl2/view:set-cursor-position view x y) (draw-cursor display x y (display:display-char-width display) - (object-height drawing-object display) + (object-height drawing-object) (eol-cursor-object-color drawing-object))) - (object-width drawing-object display)) + (object-width drawing-object)) (defmethod draw-object ((drawing-object extend-to-eol-object) x bottom-y display view) (display:set-render-color display (extend-to-eol-object-color drawing-object)) @@ -367,7 +285,7 @@ width so the rasterizer's right-edge anti-aliasing tail is preserved." (- (lem-if:view-width (lem-core:implementation) view) x) (display:display-char-height display)) (sdl2:render-fill-rect (display:display-renderer display) rect)) - (object-width drawing-object display)) + (object-width drawing-object)) (defmethod draw-object ((drawing-object line-end-object) x bottom-y display view) (call-next-method drawing-object @@ -379,8 +297,8 @@ width so the rasterizer's right-edge anti-aliasing tail is preserved." view)) (defmethod draw-object ((drawing-object image-object) x bottom-y display view) - (let* ((surface-width (object-width drawing-object display)) - (surface-height (object-height drawing-object display)) + (let* ((surface-width (object-width drawing-object)) + (surface-height (object-height drawing-object)) (texture (sdl2:create-texture-from-surface (display:display-renderer display) (image-object-image drawing-object))) (y (- bottom-y surface-height))) @@ -418,18 +336,18 @@ rather than the row-wide two-pass)." :for object :in objects :while (< current-x display-width) :collect (cons object current-x) - :do (incf current-x (object-width object display))))) + :do (incf current-x (object-width object))))) (flet ((draw-text-pass (object obj-x phase) ;; Honour the wrap-to-letters branch the old code used when a ;; text-object would extend past the display width. (cond ((< display-width - (+ obj-x (object-width object display))) + (+ obj-x (object-width object))) (loop :with current-x := obj-x :for c :across (text-object-string object) :while (< current-x display-width) :for letter := (make-letter-object c (text-object-attribute object)) - :for letter-width := (object-width letter display) + :for letter-width := (object-width letter) :do (draw-text-glyph-surface letter current-x bottom-y display view letter-width :clip t :phase phase) @@ -452,7 +370,7 @@ rather than the row-wide two-pass)." (loop :with current-x := (lem-if:view-width (lem-core:implementation) view) :and y := (lem-if:view-height (lem-core:implementation) view) :for object :in objects - :do (decf current-x (object-width object display)) + :do (decf current-x (object-width object)) (draw-object object current-x y display view))) (defun fill-to-end-of-line (display view x y height &optional default-attribute) diff --git a/src/display/physical-line.lisp b/src/display/physical-line.lisp index 4c130aebb..75de4b114 100644 --- a/src/display/physical-line.lisp +++ b/src/display/physical-line.lisp @@ -62,6 +62,42 @@ (height :initarg :height :reader image-object-height) (attribute :initarg :attribute :reader image-object-attribute))) +(defun image-draw-width (implementation object) + "Pixel width OBJECT's image is drawn at. +:width on the object is a pixel count. An image carrying none is drawn at its natural size if the +frontend can report one (`lem-if:image-natural-size'), otherwise one cell wide." + (or (image-object-width object) + (nth-value 0 (lem-if:image-natural-size implementation (image-object-image object))) + (lem-if:cell-width implementation))) + +(defun image-draw-height (implementation object) + "Pixel height OBJECT's image is drawn at, as `image-draw-width' on the other axis." + (or (image-object-height object) + (nth-value 1 (lem-if:image-natural-size implementation (image-object-image object))) + (lem-if:cell-height implementation))) + +(defmethod lem-if:object-width (implementation (drawing-object void-object)) + 0) + +(defmethod lem-if:object-width (implementation (drawing-object text-object)) + (* (string-width (text-object-string drawing-object)) + (lem-if:cell-width implementation))) + +(defmethod lem-if:object-width (implementation (drawing-object eol-cursor-object)) + 0) + +(defmethod lem-if:object-width (implementation (drawing-object extend-to-eol-object)) + 0) + +(defmethod lem-if:object-width (implementation (drawing-object image-object)) + (image-draw-width implementation drawing-object)) + +(defmethod lem-if:object-height (implementation (drawing-object drawing-object)) + (lem-if:cell-height implementation)) + +(defmethod lem-if:object-height (implementation (drawing-object image-object)) + (image-draw-height implementation drawing-object)) + (defmethod cursor-object-p (drawing-object) nil) diff --git a/src/interface.lisp b/src/interface.lisp index a4f33a9b6..64b25d868 100644 --- a/src/interface.lisp +++ b/src/interface.lisp @@ -187,8 +187,28 @@ Unit-relative like `cell-width'.")) (defgeneric lem-if:render-line (implementation view x y objects height)) (defgeneric lem-if:render-line-on-modeline (implementation view left-objects right-objects default-attribute height)) -(defgeneric lem-if:object-width (implementation drawing-object)) -(defgeneric lem-if:object-height (implementation drawing-object)) +(defgeneric lem-if:object-width (implementation drawing-object) + (:documentation "Width DRAWING-OBJECT occupies, in the same units as `cell-width'. +The default methods in src/display/physical-line.lisp derive this from `cell-width'. A +`lem-core/display:text-object' (one run of characters sharing an attribute) is as wide as its +string: `string-width' cells, counting a wide glyph as two. An image takes the pixel width it is +drawn at (`lem-core/display:image-draw-width'). +Specialize this only for an object the frontend draws at some other size, as sdl2 does for its +folder and emoji glyphs.")) + +(defgeneric lem-if:object-height (implementation drawing-object) + (:documentation "Height DRAWING-OBJECT occupies, in the same units as `cell-height'. +Defaults to one cell for every object but an image, which takes the pixel height it is drawn at +(`lem-core/display:image-draw-height'). Specialize it as in `object-width'.")) + +(defgeneric lem-if:image-natural-size (implementation image) + (:documentation "Fallback size for an image whose object requests no particular size. +Returns (values WIDTH HEIGHT) in pixels, or NIL NIL if the frontend can't tell. IMAGE is the +frontend's own loaded-image handle (an SDL surface, a path handed to a browser, ...), the value +stored in an `lem-core/display:image-object' and obtained via `image-object-image'. See +`lem-core/display:image-draw-width'.") + (:method (implementation image) + (values nil nil))) (defgeneric lem-if:clear-to-end-of-window (implementation view y)) (defgeneric lem-if:js-eval (implementation view code &key wait) diff --git a/src/internal-packages.lisp b/src/internal-packages.lisp index 0e1d14249..9f1a87e13 100644 --- a/src/internal-packages.lisp +++ b/src/internal-packages.lisp @@ -18,6 +18,10 @@ :image-object-height :image-object-image :image-object-width + :image-draw-width + :image-draw-height + :object-height + :object-width :line-end-object :line-end-object-offset :text-object @@ -827,4 +831,5 @@ :render-line-on-modeline :object-width :object-height + :image-natural-size :set-frame-color)) From d0c48e636fb9d15a172e8b6629db93a3d6cc9566 Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Fri, 31 Jul 2026 21:06:01 +0300 Subject: [PATCH 14/26] lay rows out in the core, hand frontends a laid-out row lem-if:render-line handed frontends loose objects and left placement to each one. it is replaced by lem-if:render-row, taking a lem-core/display:row with height, fill and per-object placement from layout-row. objects hang from a shared baseline, reported via lem-if:cell-pixel-size and lem-if:object-ascent. extend-to-eol becomes row-fill-x/row-fill-color rather than an object. --- .claude/rules/frontend.md | 2 +- frontends/fake-interface/fake-interface.lisp | 12 +- frontends/ncurses/ncurses.lisp | 20 +- frontends/ncurses/render.lisp | 64 +++-- frontends/ncurses/view.lisp | 4 +- frontends/sdl2/display.lisp | 2 +- frontends/sdl2/drawing.lisp | 239 +++++++++---------- frontends/sdl2/main.lisp | 6 + frontends/server/main.lisp | 73 +++--- src/color-theme.lisp | 2 +- src/display/physical-line.lisp | 192 ++++++++++++--- src/interface.lisp | 42 +++- src/internal-packages.lisp | 21 +- 13 files changed, 407 insertions(+), 272 deletions(-) diff --git a/.claude/rules/frontend.md b/.claude/rules/frontend.md index 3cae97a9e..cec33ccb9 100644 --- a/.claude/rules/frontend.md +++ b/.claude/rules/frontend.md @@ -20,7 +20,7 @@ Frontends subclass `implementation` and implement `lem-if:*` generics. (lem-if:set-view-pos impl view x y) ;; Rendering -(lem-if:render-line impl view x y objects height) +(lem-if:render-row impl view row) ; ROW is a laid-out lem-core/display:row (lem-if:clear-to-end-of-window impl view y) ``` diff --git a/frontends/fake-interface/fake-interface.lisp b/frontends/fake-interface/fake-interface.lisp index 1e676e9fe..241004004 100644 --- a/frontends/fake-interface/fake-interface.lisp +++ b/frontends/fake-interface/fake-interface.lisp @@ -97,7 +97,7 @@ (defmethod lem-if:object-height ((implementation fake-interface) object) 1) -(defmethod lem-if:render-line ((implementation fake-interface) view x y objects height) +(defmethod lem-if:render-row ((implementation fake-interface) view row) nil) (defmethod lem-if:clear-to-end-of-window ((implementation fake-interface) view y) @@ -109,12 +109,10 @@ (defmethod lem-if:cell-height ((implementation fake-interface)) 1) -(defmethod lem-if:render-line-on-modeline ((implementation fake-interface) - view - left-objects - right-objects - default-attribute - height) +(defmethod lem-if:render-modeline-row ((implementation fake-interface) + view + row + default-attribute) nil) (defmacro with-fake-interface (() &body body) diff --git a/frontends/ncurses/ncurses.lisp b/frontends/ncurses/ncurses.lisp index e1df0eaf4..3c1060820 100644 --- a/frontends/ncurses/ncurses.lisp +++ b/frontends/ncurses/ncurses.lisp @@ -85,17 +85,11 @@ (defmethod lem-if:view-height ((implementation ncurses) view) (lem-ncurses/view:view-height view)) -(defmethod lem-if:render-line ((implementation ncurses) - view x y objects height) - (lem-ncurses/render:render-line view x y objects)) - -(defmethod lem-if:render-line-on-modeline ((implementation ncurses) - view - left-objects - right-objects - default-attribute - height) - (lem-ncurses/render:render-line-on-modeline view left-objects right-objects default-attribute)) +(defmethod lem-if:render-row ((implementation ncurses) view row) + (lem-ncurses/render:render-row view row)) + +(defmethod lem-if:render-modeline-row ((implementation ncurses) view row default-attribute) + (lem-ncurses/render:render-modeline-row view row default-attribute)) (defmethod lem-if:object-width ((implementation ncurses) (drawing-object lem-core/display:image-object)) @@ -105,6 +99,10 @@ (drawing-object lem-core/display:image-object)) 1) +(defmethod lem-if:object-ascent ((implementation ncurses) + (drawing-object lem-core/display:image-object)) + 1) + (defmethod lem-if:clear-to-end-of-window ((implementation ncurses) view y) (lem-ncurses/render:clear-to-end-of-window view y)) diff --git a/frontends/ncurses/render.lisp b/frontends/ncurses/render.lisp index 3065c74a6..cd96fef35 100644 --- a/frontends/ncurses/render.lisp +++ b/frontends/ncurses/render.lisp @@ -1,8 +1,8 @@ (defpackage :lem-ncurses/render (:use :cl :lem-core/display) - (:export :render-line - :render-line-on-modeline + (:export :render-row + :render-modeline-row :clear-to-end-of-window)) (in-package :lem-ncurses/render) @@ -37,17 +37,6 @@ " " (lem:make-attribute :foreground (lem:color-to-hex-string (eol-cursor-object-color object))))) -(defmethod draw-object ((object extend-to-eol-object) x y view scrwin) - (let ((width (lem-if:view-width (lem:implementation) view))) - (when (< x width) - (print-string - scrwin - x - y - (make-string (- width x) :initial-element #\space) - (lem:make-attribute :background - (lem:color-to-hex-string (extend-to-eol-object-color object))))))) - (defmethod draw-object ((object line-end-object) x y view scrwin) (let ((string (text-object-string object)) (attribute (text-object-attribute object))) @@ -61,40 +50,43 @@ (defmethod draw-object ((object image-object) x y view scrwin) (values)) -(defun render-line-from-behind (view y objects scrwin) - (loop :with current-x := (lem-if:view-width (lem:implementation) view) - :for object :in objects - :do (decf current-x (lem-if:object-width (lem:implementation) object)) - (draw-object object current-x y view scrwin))) - (defun clear-line (view x y) (charms/ll:wmove (lem-ncurses/view:view-scrwin view) y x) (charms/ll:wclrtoeol (lem-ncurses/view:view-scrwin view))) -(defun %render-line (view x y objects scrwin) - (loop :for object :in objects - :do (draw-object object x y view scrwin) - (incf x (lem-if:object-width (lem:implementation) object)))) +(defun draw-row (view row scrwin) + "Draw ROW's background fill, then everything placed on it. +The fill is spaces carrying the background color, since a terminal cell only takes a color by +having a character written into it." + (let ((width (lem-if:view-width (lem:implementation) view))) + (when (and (row-fill-color row) + (< (row-fill-x row) width)) + (print-string scrwin + (row-fill-x row) + (row-top row) + (make-string (- width (row-fill-x row)) :initial-element #\space) + (lem:make-attribute :background + (lem:color-to-hex-string (row-fill-color row)))))) + (loop :for placement :in (row-placements row) + :do (draw-object (placement-object placement) + (placement-x placement) + (placement-top placement) + view + scrwin))) -(defun render-line (view x y objects) - (clear-line view x y) - (%render-line view x y objects (lem-ncurses/view:view-scrwin view))) +(defun render-row (view row) + (clear-line view 0 (row-top row)) + (draw-row view row (lem-ncurses/view:view-scrwin view))) -(defun render-line-on-modeline (view - left-objects - right-objects - default-attribute) +(defun render-modeline-row (view row default-attribute) + ;; the modeline gets its own curses window, so its row (laid out at top 0) needs no translation. (print-string (lem-ncurses/view:view-modeline-scrwin view) 0 - 0 + (row-top row) (make-string (lem-ncurses/view:view-width view) :initial-element #\space) default-attribute) - (%render-line view 0 0 left-objects (lem-ncurses/view:view-modeline-scrwin view)) - (render-line-from-behind view - 0 - right-objects - (lem-ncurses/view:view-modeline-scrwin view))) + (draw-row view row (lem-ncurses/view:view-modeline-scrwin view))) (defun clear-to-end-of-window (view y) (let ((win (lem-ncurses/view:view-scrwin view))) diff --git a/frontends/ncurses/view.lisp b/frontends/ncurses/view.lisp index b5d31c674..25225f85c 100644 --- a/frontends/ncurses/view.lisp +++ b/frontends/ncurses/view.lisp @@ -16,8 +16,8 @@ :set-view-pos :redraw-view-after :redraw-display-after - :render-line - :render-line-on-modeline + :render-row + :render-modeline-row :clear-to-end-of-window :update-display :set-last-print-cursor)) diff --git a/frontends/sdl2/display.lisp b/frontends/sdl2/display.lisp index ff3255866..b48955dbf 100644 --- a/frontends/sdl2/display.lisp +++ b/frontends/sdl2/display.lisp @@ -112,7 +112,7 @@ Retina to a 1x display).") :documentation "Pre-allocated SDL_Rect reused across all render calls to avoid heap-allocating a new rect + Lisp wrapper on every draw-rect, -fill-to-end-of-line, render-texture, etc. Mutated in-place by +fill-row, render-texture, etc. Mutated in-place by `call-with-scratch-rect' / `with-scratch-rect'; do not rely on its contents outside the dynamic extent of that call."))) diff --git a/frontends/sdl2/drawing.lisp b/frontends/sdl2/drawing.lisp index 2ea30b7c8..ec949b938 100644 --- a/frontends/sdl2/drawing.lisp +++ b/frontends/sdl2/drawing.lisp @@ -78,7 +78,12 @@ Uses a sentinel key so it participates in the normal cache lifecycle (display:with-display (display) (* (display:display-char-width display) 2 (length (text-object-string drawing-object))))) -(defmethod draw-object ((drawing-object void-object) x bottom-y display view) +(defgeneric draw-object (drawing-object x top display view) + (:documentation "Draw DRAWING-OBJECT into VIEW with its top-left corner at (X, TOP). +Returns the pixel width it occupied. +`lem-core/display:layout-row' already chose TOP as the row's baseline minus this object's ascent.")) + +(defmethod draw-object ((drawing-object void-object) x top display view) 0) (defun draw-rect (display x y width height color) @@ -95,10 +100,10 @@ Uses a sentinel key so it participates in the normal cache lifecycle (:underline (draw-rect display x (+ y surface-height -1) surface-width 1 background)))) -(defun draw-text-glyph-surface (drawing-object x bottom-y display view cell-width +(defun draw-text-glyph-surface (drawing-object x top display view cell-width &key clip (phase :both)) "Draws DRAWING-OBJECT's cached SDL surface in a (CELL-WIDTH × cell-height) slot -at (X, BOTTOM-Y). Cell-height is taken from the drawing-object's OBJECT-HEIGHT so +at (X, TOP). Cell-height is taken from the drawing-object's OBJECT-HEIGHT so non-text surfaces (folder PNG, icon font, emoji font) get scaled to the editor's character row height instead of being placed at their natural pixel height. @@ -129,15 +134,15 @@ is not erased by the next glyph's background fill." (cell-height (object-height drawing-object)) (attribute (text-object-attribute drawing-object)) (background (lem-core:attribute-background-with-reverse attribute)) - (y (- bottom-y cell-height)) + (bottom-y (+ top cell-height)) (draw-width (if clip surface-width (min surface-width cell-width))) (draw-height (min surface-height cell-height))) (when (member phase '(:bg :both)) (cond ((and attribute (lem-core:cursor-attribute-p attribute)) - (lem-sdl2/view:set-cursor-position view x y) - (draw-cursor display x y cell-width cell-height background)) + (lem-sdl2/view:set-cursor-position view x top) + (draw-cursor display x top cell-width cell-height background)) (t - (draw-rect display x y cell-width cell-height background)))) + (draw-rect display x top cell-width cell-height background)))) (when (member phase '(:glyph :both)) (let ((texture (lem-sdl2/text-surface-cache:get-or-create-texture (display:display-renderer display) @@ -173,7 +178,7 @@ is not erased by the next glyph's background fill." :dest-rect dst-rect :flip (list :none))))) (t - (display:with-scratch-rect (dst-rect display x y draw-width draw-height) + (display:with-scratch-rect (dst-rect display x top draw-width draw-height) (sdl2:render-copy-ex (display:display-renderer display) texture :source-rect nil @@ -184,9 +189,9 @@ is not erased by the next glyph's background fill." (lem:attribute-underline attribute)) (display:render-line display x - (1- (+ y cell-height)) + (1- bottom-y) (+ x cell-width) - (1- (+ y cell-height)) + (1- bottom-y) :color (let ((underline (lem:attribute-underline attribute))) (if (eq underline t) (lem-core:attribute-foreground-color attribute) @@ -203,14 +208,14 @@ widths, for the multi-character text run DRAWING-OBJECT." :collect (object-width letter) :into widths :finally (return (values letters widths))))) -(defun draw-text-object-phase (drawing-object x bottom-y display view phase) +(defun draw-text-object-phase (drawing-object x top display view phase) "Render the text-object DRAWING-OBJECT for one of the two-pass phases (:BG or :GLYPH). PHASE :BG paints backgrounds, cursors and underlines for every cell of the run; PHASE :GLYPH blits each glyph at its natural surface width so the rasterizer's right-edge anti-aliasing tail is preserved." (let ((string (text-object-string drawing-object))) (cond ((<= (length string) 1) - (draw-text-glyph-surface drawing-object x bottom-y display view + (draw-text-glyph-surface drawing-object x top display view (object-width drawing-object) :clip t :phase phase)) (t @@ -219,12 +224,12 @@ width so the rasterizer's right-edge anti-aliasing tail is preserved." (loop :with current-x := x :for letter-object :in letter-objects :for letter-width :in letter-widths - :do (draw-text-glyph-surface letter-object current-x bottom-y + :do (draw-text-glyph-surface letter-object current-x top display view letter-width :clip t :phase phase) (incf current-x letter-width))))))) -(defmethod draw-object ((drawing-object text-object) x bottom-y display view) +(defmethod draw-object ((drawing-object text-object) x top display view) ;; Render each character individually on the cell grid. SDL_ttf's blended ;; surface for a multi-character string has metrics that do not equal the ;; sum of its per-character metrics, so a glyph would otherwise land on a @@ -235,74 +240,62 @@ width so the rasterizer's right-edge anti-aliasing tail is preserved." ;; ;; This per-text-object two-pass preserves the AA overhang within the run. ;; The cross-text-object equivalent (an adjacent text-object's :bg erasing - ;; the previous text-object's AA tail) is handled by `redraw-physical-line', - ;; which lifts the two-pass to span the entire physical line. + ;; the previous text-object's AA tail) is handled by `draw-row-objects', + ;; which lifts the two-pass to span the entire row. (let ((total-width (object-width drawing-object))) - (draw-text-object-phase drawing-object x bottom-y display view :bg) - (draw-text-object-phase drawing-object x bottom-y display view :glyph) + (draw-text-object-phase drawing-object x top display view :bg) + (draw-text-object-phase drawing-object x top display view :glyph) total-width)) -(defmethod draw-object ((drawing-object icon-object) x bottom-y display view) +(defmethod draw-object ((drawing-object icon-object) x top display view) ;; Icon font glyphs typically render much wider than the 2-cell column the ;; layout reserves for them; draw-text-glyph-surface scales them down to fit. (let ((cell-width (object-width drawing-object))) - (draw-text-glyph-surface drawing-object x bottom-y display view cell-width) + (draw-text-glyph-surface drawing-object x top display view cell-width) cell-width)) -(defmethod draw-object ((drawing-object folder-object) x bottom-y display view) +(defmethod draw-object ((drawing-object folder-object) x top display view) ;; Folder PNG surface is much wider than 2 cells; render once and scale to fit. ;; Overrides the text-object per-character loop so the icon stays atomic. (let ((cell-width (object-width drawing-object))) - (draw-text-glyph-surface drawing-object x bottom-y display view cell-width) + (draw-text-glyph-surface drawing-object x top display view cell-width) cell-width)) -(defmethod draw-object ((drawing-object emoji-object) x bottom-y display view) +(defmethod draw-object ((drawing-object emoji-object) x top display view) ;; Emoji strings may span multiple codepoints (base + variation selector, ;; ZWJ sequences, ...) that must be rendered as one composed glyph. Use the ;; single-surface path with cell-aligned scaling so we neither split the ;; sequence per-codepoint nor let an oversized emoji surface spill over. (let ((cell-width (object-width drawing-object))) - (draw-text-glyph-surface drawing-object x bottom-y display view cell-width) + (draw-text-glyph-surface drawing-object x top display view cell-width) cell-width)) -(defmethod draw-object ((drawing-object eol-cursor-object) x bottom-y display view) +(defmethod draw-object ((drawing-object eol-cursor-object) x top display view) (display:set-render-color display (eol-cursor-object-color drawing-object)) - (let ((y (- bottom-y (object-height drawing-object)))) - (lem-sdl2/view:set-cursor-position view x y) - (draw-cursor display - x - y - (display:display-char-width display) - (object-height drawing-object) - (eol-cursor-object-color drawing-object))) + (lem-sdl2/view:set-cursor-position view x top) + (draw-cursor display + x + top + (display:display-char-width display) + (object-height drawing-object) + (eol-cursor-object-color drawing-object)) (object-width drawing-object)) -(defmethod draw-object ((drawing-object extend-to-eol-object) x bottom-y display view) - (display:set-render-color display (extend-to-eol-object-color drawing-object)) - (display:with-scratch-rect (rect display - x - (- bottom-y (display:display-char-height display)) - (- (lem-if:view-width (lem-core:implementation) view) x) - (display:display-char-height display)) - (sdl2:render-fill-rect (display:display-renderer display) rect)) - (object-width drawing-object)) - -(defmethod draw-object ((drawing-object line-end-object) x bottom-y display view) +(defmethod draw-object ((drawing-object line-end-object) x top display view) (call-next-method drawing-object (+ x (* (line-end-object-offset drawing-object) (display:display-char-width display))) - bottom-y + top display view)) -(defmethod draw-object ((drawing-object image-object) x bottom-y display view) +(defmethod draw-object ((drawing-object image-object) x top display view) (let* ((surface-width (object-width drawing-object)) (surface-height (object-height drawing-object)) (texture (sdl2:create-texture-from-surface (display:display-renderer display) - (image-object-image drawing-object))) - (y (- bottom-y surface-height))) - (display:with-scratch-rect (dest-rect display x y surface-width surface-height) + (image-object-image drawing-object)))) + (display:with-scratch-rect (dest-rect display x top surface-width surface-height) (sdl2:render-copy-ex (display:display-renderer display) texture :source-rect nil @@ -318,93 +311,85 @@ one of its specialised subclasses `icon-object', `folder-object', or rather than the row-wide two-pass)." (eq (class-of object) (find-class 'text-object))) -(defun redraw-physical-line (display view x y objects height) - ;; Two-pass over the whole physical line: paint every plain text-object's - ;; backgrounds first, then blit every plain text-object's glyphs. This - ;; preserves the 1-pixel right-edge AA tail at attribute boundaries (e.g. - ;; on the dashboard's highlighted row, where a `p' or `g' at the end of - ;; one attribute run would otherwise be eroded by the next text-object's +(defun draw-row-objects (display view row) + ;; Two-pass over the whole row: paint every plain text-object's backgrounds + ;; first, then blit every plain text-object's glyphs. This preserves the + ;; 1-pixel right-edge AA tail at attribute boundaries (e.g. on the + ;; dashboard's highlighted row, where a `p' or `g' at the end of one + ;; attribute run would otherwise be eroded by the next text-object's ;; full-width background fill). Everything else (icon / folder / emoji - ;; text-object subclasses, images, eol-cursor, extend-to-eol) draws fully - ;; in the first pass via its own `draw-object' method — those don't have - ;; AA overhang to preserve and need their bespoke rendering (scale-to-fit - ;; for icon/folder/emoji surfaces, fill for extend-to-eol, etc.). - (let* ((bottom-y (+ y height)) - (display-width (round (* (display:display-window-width display) + ;; text-object subclasses, images, eol-cursor) draws fully in the first pass + ;; via its own `draw-object' method — those don't have AA overhang to + ;; preserve and need their bespoke rendering (scale-to-fit for + ;; icon/folder/emoji surfaces, etc.). + (let* ((display-width (round (* (display:display-window-width display) (first (display:display-scale display))))) - (placed (loop :with current-x := x - :for object :in objects - :while (< current-x display-width) - :collect (cons object current-x) - :do (incf current-x (object-width object))))) - (flet ((draw-text-pass (object obj-x phase) - ;; Honour the wrap-to-letters branch the old code used when a - ;; text-object would extend past the display width. - (cond ((< display-width - (+ obj-x (object-width object))) - (loop :with current-x := obj-x - :for c :across (text-object-string object) - :while (< current-x display-width) - :for letter := (make-letter-object - c (text-object-attribute object)) - :for letter-width := (object-width letter) - :do (draw-text-glyph-surface letter current-x bottom-y - display view letter-width - :clip t :phase phase) - (incf current-x letter-width))) - (t - (draw-text-object-phase object obj-x bottom-y - display view phase))))) + (placements (remove-if (lambda (placement) + (<= display-width (placement-x placement))) + (row-placements row)))) + (flet ((draw-text-pass (placement phase) + (let* ((object (placement-object placement)) + (x (placement-x placement)) + (top (placement-top placement))) + (cond ((< display-width (+ x (object-width object))) + ;; a run reaching past the edge is drawn letter by letter, as far as it fits. + (loop :with current-x := x + :for c :across (text-object-string object) + :while (< current-x display-width) + :for letter := (make-letter-object + c (text-object-attribute object)) + :for letter-width := (object-width letter) + :do (draw-text-glyph-surface letter current-x top + display view letter-width + :clip t :phase phase) + (incf current-x letter-width))) + (t + (draw-text-object-phase object x top display view phase)))))) ;; Pass 1: plain-text backgrounds + everything else (subclassed text- ;; objects and non-text objects) in normal order. - (loop :for (object . obj-x) :in placed - :do (if (plain-text-object-p object) - (draw-text-pass object obj-x :bg) - (draw-object object obj-x bottom-y display view))) + (loop :for placement :in placements + :do (if (plain-text-object-p (placement-object placement)) + (draw-text-pass placement :bg) + (draw-object (placement-object placement) + (placement-x placement) + (placement-top placement) + display + view))) ;; Pass 2: plain-text glyphs. - (loop :for (object . obj-x) :in placed - :when (plain-text-object-p object) - :do (draw-text-pass object obj-x :glyph))))) - -(defun redraw-physical-line-from-behind (display view objects) - (loop :with current-x := (lem-if:view-width (lem-core:implementation) view) - :and y := (lem-if:view-height (lem-core:implementation) view) - :for object :in objects - :do (decf current-x (object-width object)) - (draw-object object current-x y display view))) - -(defun fill-to-end-of-line (display view x y height &optional default-attribute) - (display:with-scratch-rect (rect display x y (- (lem-if:view-width (lem-core:implementation) view) x) height) - (display:set-render-color display - (lem-core:attribute-background-color default-attribute)) + (loop :for placement :in placements + :when (plain-text-object-p (placement-object placement)) + :do (draw-text-pass placement :glyph))))) + +(defun fill-row (display view row x color) + "Paint COLOR from X to the right edge of VIEW, over ROW's full height." + (display:with-scratch-rect (rect display + x + (row-top row) + (- (lem-if:view-width (lem-core:implementation) view) x) + (row-height row)) + (display:set-render-color display color) (sdl2:render-fill-rect (display:display-renderer display) rect))) -(defmethod lem-if:render-line ((implementation lem-sdl2/sdl2:sdl2) view x y objects height) +(defun draw-row (display view row background) + "Blank ROW to BACKGROUND, paint whatever fill it carries, then draw everything placed on it. +Both fills cover the row's full height, which may exceed a single text line's height when a +tall object (e.g. an image) sits on the row." + (fill-row display view row 0 background) + (when (row-fill-color row) + (fill-row display view row (row-fill-x row) (row-fill-color row))) + (draw-row-objects display view row)) + +(defmethod lem-if:render-row ((implementation lem-sdl2/sdl2:sdl2) view row) (display:with-display (display) - (fill-to-end-of-line display view x y height) - (redraw-physical-line display view x y objects height))) - -(defmethod lem-if:render-line-on-modeline ((implementation lem-sdl2/sdl2:sdl2) - view - left-objects - right-objects - default-attribute - height) + (draw-row display view row (lem-core:attribute-background-color nil)))) + +(defmethod lem-if:render-modeline-row ((implementation lem-sdl2/sdl2:sdl2) view row + default-attribute) (display:with-display (display) - (fill-to-end-of-line display - view - 0 - (- (lem-if:view-height (lem-core:implementation) view) height) - height - default-attribute) - (redraw-physical-line display - view - 0 - (- (lem-if:view-height (lem-core:implementation) view) - (display:display-char-height display)) - left-objects - height) - (redraw-physical-line-from-behind display view right-objects))) + (draw-row display + view + (translate-row row (- (lem-if:view-height implementation view) (row-height row))) + (lem-core:attribute-background-color default-attribute)))) (defmethod lem-if:clear-to-end-of-window ((implementation lem-sdl2/sdl2:sdl2) view y) (display:with-display (display) diff --git a/frontends/sdl2/main.lisp b/frontends/sdl2/main.lisp index 278c0a60b..d218d664c 100644 --- a/frontends/sdl2/main.lisp +++ b/frontends/sdl2/main.lisp @@ -478,6 +478,12 @@ (display:with-display (display) (display:display-char-height display))) +(defmethod lem-if:cell-pixel-size ((implementation sdl2)) + (display:with-display (display) + (values (display:display-char-width display) + (display:display-char-height display) + (display:display-font-ascent display)))) + (defmethod lem-if:view-width ((implementation sdl2) view) (display:with-display (display) (* (display:display-char-width display) diff --git a/frontends/server/main.lisp b/frontends/server/main.lisp index 2deac249c..7d74ca73b 100644 --- a/frontends/server/main.lisp +++ b/frontends/server/main.lisp @@ -604,8 +604,8 @@ the same immutable instance for every subsequent message." (defgeneric object-height (drawing-object) (:documentation "height of DRAWING-OBJECT in character cells. -we advance the vertical position of the next line by the tallest object's height (see -`max-height-of-objects'), so returning more than 1 for an image makes its line grow to fit.")) +`lem-core/display:layout-row' grows the row to fit everything on it, so returning more than 1 for +an image gives it the cells it needs.")) (defmethod object-height (drawing-object) 1) @@ -716,17 +716,6 @@ same hash." (lem-core:set-cursor-attribute attr) (put jsonrpc view x y " " attr :text-width 1))) -(defmethod draw-object (jsonrpc (object display:extend-to-eol-object) x y view) - (let ((width (lem-if:view-width (lem-core:implementation) view))) - (when (< x width) - (let ((fill-width (- width x))) - (put jsonrpc view x y - (make-string fill-width :initial-element #\space) - (lem:make-attribute - :background - (lem:color-to-hex-string (display:extend-to-eol-object-color object))) - :text-width fill-width))))) - (defmethod draw-object (jsonrpc (object display:line-end-object) x y view) (let ((string (display:text-object-string object)) (attribute (display:text-object-attribute object)) @@ -776,43 +765,55 @@ a string already carrying a data:/https: URL is passed through unchanged." "pixelHeight" ph "url" url))))))) -(defun render-line (jsonrpc view x y objects) - (loop :for object :in objects - :do (draw-object jsonrpc object x y view) - (incf x (object-width object)))) - -(defun render-line-from-behind (jsonrpc view y objects) - (loop :with current-x := (view-width view) - :for object :in objects - :do (decf current-x (object-width object)) - (draw-object jsonrpc object current-x y view))) - -(defmethod lem-if:render-line ((jsonrpc jsonrpc) view x y objects height) +(defun draw-row (jsonrpc view row) + "draw ROW's background fill, then everything placed on it. +`clear-eol'/`clear-eob' only ever paint the editor's plain background, so a fill in an arbitrary +color (e.g. a highlighted row) goes out as spaces carrying that color via `put'." + (let ((width (view-width view))) + (when (and (display:row-fill-color row) + (< (display:row-fill-x row) width)) + (let ((fill-width (- width (display:row-fill-x row)))) + (put jsonrpc + view + (display:row-fill-x row) + (display:row-top row) + (make-string fill-width :initial-element #\space) + (lem:make-attribute + :background + (lem:color-to-hex-string (display:row-fill-color row))) + :text-width fill-width)))) + (loop :for placement :in (display:row-placements row) + :do (draw-object jsonrpc + (display:placement-object placement) + (display:placement-x placement) + (display:placement-top placement) + view))) + +(defmethod lem-if:render-row ((jsonrpc jsonrpc) view row) (with-error-handler () - ;; clear the line's full height (not just one row) since a tall object such as an image may - ;; occupy several rows. + ;; clear the row's full height (not just one line of text) since a tall object such as an image + ;; may occupy several. (notify* jsonrpc "clear-eol" (hash "viewInfo" (view-id-hash view) - "x" x - "y" y - "height" height)) - (render-line jsonrpc view x y objects))) + "x" 0 + "y" (display:row-top row) + "height" (display:row-height row))) + (draw-row jsonrpc view row))) -(defmethod lem-if:render-line-on-modeline ((jsonrpc jsonrpc) view left-objects right-objects - default-attribute height) +(defmethod lem-if:render-modeline-row ((jsonrpc jsonrpc) view row default-attribute) + ;; the modeline has a surface of its own here, so the row is drawn where it was laid out. (let ((*put-target* :modeline)) (with-error-handler () (notify* jsonrpc "modeline-put" (hash "viewInfo" (view-id-hash view) "x" 0 - "y" 0 + "y" (display:row-top row) "text" (make-string (view-width view) :initial-element #\space) "textWidth" (view-width view) "attribute" (attribute-to-hash default-attribute))) - (render-line jsonrpc view 0 0 left-objects) - (render-line-from-behind jsonrpc view 0 right-objects)))) + (draw-row jsonrpc view row)))) (defmethod lem-if:object-width ((jsonrpc jsonrpc) drawing-object) (object-width drawing-object)) diff --git a/src/color-theme.lisp b/src/color-theme.lisp index 760301ecc..b97c69fd4 100644 --- a/src/color-theme.lisp +++ b/src/color-theme.lisp @@ -101,7 +101,7 @@ for example, to maintain an attribute like CURSOR.") ;; The per-window drawing-cache compares attributes via ensure-attribute, ;; which resolves to the *current* theme on both sides — so after a color ;; change the cache silently treats every line as unchanged and skips - ;; render-line. With :no-force-needed implementations (e.g. webview), + ;; render-row. With :no-force-needed implementations (e.g. webview), ;; (redraw-display :force t) above also strips force, so non-current ;; windows never invalidate their cache. Mark every window dirty so ;; clear-cache-if-screen-modified drops the stale cache. diff --git a/src/display/physical-line.lisp b/src/display/physical-line.lisp index 75de4b114..252d0bd9d 100644 --- a/src/display/physical-line.lisp +++ b/src/display/physical-line.lisp @@ -98,6 +98,14 @@ frontend can report one (`lem-if:image-natural-size'), otherwise one cell wide." (defmethod lem-if:object-height (implementation (drawing-object image-object)) (image-draw-height implementation drawing-object)) +(defmethod lem-if:object-ascent (implementation (drawing-object drawing-object)) + ;; anything drawn in the editor's font shares that font's baseline, the cell ascent, when the + ;; frontend reports one. + (multiple-value-bind (cell-width cell-height cell-ascent) + (lem-if:cell-pixel-size implementation) + (declare (ignore cell-width cell-height)) + (or cell-ascent (lem-if:object-height implementation drawing-object)))) + (defmethod cursor-object-p (drawing-object) nil) @@ -220,6 +228,9 @@ frontend can report one (`lem-if:image-natural-size'), otherwise one cell wide." (defun object-height (drawing-object) (lem-if:object-height (implementation) drawing-object)) +(defun object-ascent (drawing-object) + (lem-if:object-ascent (implementation) drawing-object)) + (defun split-string-by-character-type (string) (loop :with pos := 0 :and items := '() :while (< pos (length string)) @@ -361,8 +372,8 @@ frontend can report one (`lem-if:image-natural-size'), otherwise one cell wide." (push object physical-line-objects))) :finally (return (nreverse physical-line-objects)))))) -(defun render-line (view x y objects height) - (lem-if:render-line (implementation) view x y objects height)) +(defun render-row (view row) + (lem-if:render-row (implementation) view row)) (defun reduce-list (list &key (test (alexandria:required-argument :test)) @@ -440,23 +451,128 @@ leaving the row blank on persistent-texture frontends such as SDL2." (remove-drawing-cache-entries-from (drawing-cache window) y))) (defun update-and-validate-cache-p (window y height objects) - "Check cache validity, reducing objects once before storing. + "Check cache validity for the already-reduced OBJECTS, storing them when they differ. Returns T if the cached entry matches (render can be skipped)." - (let ((reduced (reduce-objects objects))) - (cond ((validate-cache-p window y height reduced) t) - (t - (invalidate-cache window y height) - (push (list y height reduced) - (drawing-cache window)) - nil)))) - -(defun render-line-with-caching (window x y objects height) - (unless (update-and-validate-cache-p window y height objects) - (render-line (window-view window) x y objects height))) - -(defun max-height-of-objects (objects) - (loop :for object :in objects - :maximize (object-height object))) + (cond ((validate-cache-p window y height objects) t) + (t + (invalidate-cache window y height) + (push (list y height objects) + (drawing-cache window)) + nil))) + +(defun render-row-with-caching (window y objects) + "Lay OBJECTS out as one screen row of WINDOW at Y and draw it, unless it is already on screen." + (let* ((reduced (reduce-objects objects)) + (row (layout-row y reduced))) + (unless (update-and-validate-cache-p window y (row-height row) reduced) + (render-row (window-view window) row)) + (row-height row))) + +(defun text-row-metrics () + "The ascent and height of a row holding nothing but text, as (values ASCENT HEIGHT). +A frontend that does not report a baseline is taken to put it at the bottom of the row." + (multiple-value-bind (cell-width cell-height cell-ascent) + (lem-if:cell-pixel-size (implementation)) + (declare (ignore cell-width)) + (let ((height (or cell-height (lem-if:cell-height (implementation))))) + (values (or cell-ascent height) height)))) + +(defun row-metrics-of-objects (&rest object-lists) + "The ascent and height a row of all the objects in OBJECT-LISTS needs, as (values ASCENT HEIGHT). +Everything shares one baseline, so the height is max ascent plus max descent, which can exceed any +single object's own height: an object with a tall ascent and short descent and one with a short +ascent and tall descent can each set one half of the row independently, so the row ends up taller +than either. An empty row is still one row of text tall. +The returned ASCENT is also the baseline's offset from the row's top, since the baseline sits +exactly ASCENT below it. `layout-row' uses it that way to hang everything on the row from it." + (multiple-value-bind (ascent height) (text-row-metrics) + (let ((descent (- height ascent))) + (dolist (objects object-lists) + (dolist (object objects) + (let ((object-ascent (object-ascent object))) + (setf ascent (max ascent object-ascent)) + (setf descent (max descent (- (object-height object) object-ascent)))))) + (values ascent (+ ascent descent))))) + +(defstruct (placement (:constructor make-placement (object x top))) + "Where one drawing object goes, top-left corner at (X, TOP), in the frontend's units. +TOP is the row's baseline minus this object's ascent, so objects of different heights hang from one +baseline instead of sharing a top edge." + object + x + top) + +(defstruct row + "One screen row, laid out by `layout-row' and ready for a frontend to draw. +TOP/HEIGHT already account for every object on the row, including one taller than a line of text. +A frontend should size the row from these fields rather than re-deriving its extent from any +single object's own height." + top + height + ;; needed by a frontend that draws a text-object letter by letter, to put each letter on it. + baseline + ;; where each object goes, its own x and top. + placements + ;; from an `extend-to-eol-object', if the row holds one. A frontend paints FILL-COLOR first, + ;; before any of the row's objects, over the rectangle from FILL-X to the right edge and down + ;; the row's full height. NIL FILL-COLOR means nothing to paint. + fill-x + fill-color) + +(defun layout-row (top objects + &key + right-objects + (right-edge (and right-objects + (alexandria:required-argument :right-edge)))) + "Lay OBJECTS out as one screen row with its top edge at TOP, as a `row'. +RIGHT-OBJECTS are laid out leftwards from RIGHT-EDGE instead, for a row drawn from both ends (the +modeline), so the left- and right-aligned objects share one baseline. Everything, including an +image, is positioned by its own ascent measured from that one shared baseline (see +`image-object-ascent'), so it stays correctly placed relative to the text beside it however tall +the row is. +An `extend-to-eol-object' is not placed. It draws nothing of its own and colors the row's full +height, so it becomes ROW-FILL-X and ROW-FILL-COLOR." + (multiple-value-bind (ascent height) (row-metrics-of-objects objects right-objects) + (let ((baseline (+ top ascent)) + (placements) + (fill-x) + (fill-color)) + (flet ((place (object x) + (if (typep object 'extend-to-eol-object) + ;; only the first can show, it colors everything from its x rightwards. + (unless fill-color + (setf fill-x x + fill-color (extend-to-eol-object-color object))) + (push (make-placement object x (- baseline (object-ascent object))) + placements)))) + (loop :with x := 0 + :for object :in objects + :do (place object x) + (incf x (object-width object))) + (loop :with x := right-edge + :for object :in right-objects + :do (decf x (object-width object)) + (place object x))) + (make-row :top top + :height height + :baseline baseline + :placements (nreverse placements) + :fill-x fill-x + :fill-color fill-color)))) + +(defun translate-row (row dy) + "A copy of ROW moved DY down the view. +For a frontend that draws a row elsewhere than where it was laid out, a modeline drawn into the +bottom of the window's view rather than onto a surface of its own." + (let ((moved (copy-row row))) + (setf (row-top moved) (+ (row-top row) dy) + (row-baseline moved) (+ (row-baseline row) dy) + (row-placements moved) + (loop :for placement :in (row-placements row) + :collect (make-placement (placement-object placement) + (placement-x placement) + (+ (placement-top placement) dy)))) + moved)) ;;; Line fingerprint cache — avoids creating drawing objects for unchanged lines @@ -615,8 +731,7 @@ over the top-level spine and tolerant of improper (dotted) lists." (loop (unless objects (return)) (let* ((all-objects (append left-side-objects objects)) - (height (max-height-of-objects all-objects))) - (render-line-with-caching window 0 y all-objects height) + (height (render-row-with-caching window y all-objects))) (incf y height) (setq left-side-objects wrapped-left-side-objects) (incf total-height height) @@ -709,10 +824,8 @@ creating zero temporary letter-objects." ;; Early exit if line content unchanged (alexandria:when-let ((cached-height (check-line-fingerprint window y fingerprint))) (return-from redraw-logical-line-when-horizontal-scroll cached-height)) - (let* ((objects (create-drawing-objects logical-line)) - (height - (max (max-height-of-objects left-side-objects) - (max-height-of-objects objects)))) + (let ((objects (create-drawing-objects logical-line)) + (height 0)) (multiple-value-bind (cursor-object cursor-x) (find-cursor-object objects) (when cursor-object @@ -726,13 +839,13 @@ creating zero temporary letter-objects." (+ (- cursor-x width) (object-width cursor-object))))))) (setf objects - (reduce-objects - (clip-objects-to-display-range - objects - (horizontal-scroll-start window) - (+ (horizontal-scroll-start window) - (window-view-width window))))) - (render-line-with-caching window 0 y (append left-side-objects objects) height)) + (clip-objects-to-display-range + objects + (horizontal-scroll-start window) + (+ (horizontal-scroll-start window) + (window-view-width window)))) + (setf height + (render-row-with-caching window y (append left-side-objects objects)))) ;; Reuse fingerprint if scroll position didn't change; avoids redundant sxhash (update-line-fingerprint window y @@ -819,13 +932,16 @@ creating zero temporary letter-objects." 'modeline-inactive)))) (multiple-value-bind (left-objects right-objects) (make-modeline-objects window default-attribute) - (lem-if:render-line-on-modeline (implementation) - view - left-objects - right-objects - default-attribute - (max (max-height-of-objects left-objects) - (max-height-of-objects right-objects))))))) + ;; top 0: only the frontend knows where the modeline actually goes on screen. see + ;; `lem-if:render-modeline-row'. + (lem-if:render-modeline-row (implementation) + view + (layout-row 0 + left-objects + :right-objects right-objects + :right-edge (lem-if:view-width (implementation) + view)) + default-attribute))))) (defun get-background-color-of-window (window) (cond ((typep window 'floating-window) diff --git a/src/interface.lisp b/src/interface.lisp index 64b25d868..9be5c1305 100644 --- a/src/interface.lisp +++ b/src/interface.lisp @@ -184,15 +184,30 @@ the units `object-width' / `object-height' are counted in.")) (:documentation "Height of one character cell in the frontend's native layout units. Unit-relative like `cell-width'.")) -(defgeneric lem-if:render-line (implementation view x y objects height)) -(defgeneric lem-if:render-line-on-modeline (implementation view left-objects right-objects - default-attribute height)) +(defgeneric lem-if:cell-pixel-size (implementation) + (:documentation "One character cell in real pixels, as (values WIDTH HEIGHT ASCENT). +ASCENT is how far below the cell's top the text baseline sits, and may be NIL on its own. +All three are NIL on a frontend that does not draw in pixels. +Always pixels, unlike `cell-width' / `cell-height', which are 1 on a cell-based frontend.") + (:method (implementation) + (values nil nil nil))) + +(defgeneric lem-if:render-row (implementation view row) + (:documentation "Draw ROW, one screen row of VIEW, replacing whatever it held before. +ROW is a `lem-core/display:row'. Its height, background and the position of every object on it were +decided by `lem-core/display:layout-row', so a frontend only paints. +Blank the row's full width, `row-top' down by ROW-HEIGHT, first.")) + +(defgeneric lem-if:render-modeline-row (implementation view row default-attribute) + (:documentation "Draw ROW as VIEW's modeline, filled with DEFAULT-ATTRIBUTE's background. +Like `render-row', except ROW was laid out with its top at Y 0, since only the frontend knows +where on screen its modeline goes. One that draws it into the view moves it with +`lem-core/display:translate-row'.")) + (defgeneric lem-if:object-width (implementation drawing-object) (:documentation "Width DRAWING-OBJECT occupies, in the same units as `cell-width'. -The default methods in src/display/physical-line.lisp derive this from `cell-width'. A -`lem-core/display:text-object' (one run of characters sharing an attribute) is as wide as its -string: `string-width' cells, counting a wide glyph as two. An image takes the pixel width it is -drawn at (`lem-core/display:image-draw-width'). +Defaults in src/display/physical-line.lisp: a text-object is `string-width' cells, counting a wide +glyph as two, an image its `lem-core/display:image-draw-width'. Specialize this only for an object the frontend draws at some other size, as sdl2 does for its folder and emoji glyphs.")) @@ -201,12 +216,19 @@ folder and emoji glyphs.")) Defaults to one cell for every object but an image, which takes the pixel height it is drawn at (`lem-core/display:image-draw-height'). Specialize it as in `object-width'.")) +(defgeneric lem-if:object-ascent (implementation drawing-object) + (:documentation "How much of DRAWING-OBJECT sits above the text baseline, in the same units as +`cell-height'. +Everything on a row shares one baseline, so a row is as tall as the furthest anything reaches above +it plus the furthest anything reaches below, which can exceed the tallest single object. +Defaults to the cell ascent a frontend reports through `cell-pixel-size', or the object's bottom +when it reports none, so one that does not know its baseline keeps its old layout exactly.")) + (defgeneric lem-if:image-natural-size (implementation image) (:documentation "Fallback size for an image whose object requests no particular size. Returns (values WIDTH HEIGHT) in pixels, or NIL NIL if the frontend can't tell. IMAGE is the -frontend's own loaded-image handle (an SDL surface, a path handed to a browser, ...), the value -stored in an `lem-core/display:image-object' and obtained via `image-object-image'. See -`lem-core/display:image-draw-width'.") +frontend's own loaded-image handle (an SDL surface, a path handed to a browser, ...), not the +`lem-core/display:image-object' holding it.") (:method (implementation image) (values nil nil))) (defgeneric lem-if:clear-to-end-of-window (implementation view y)) diff --git a/src/internal-packages.lisp b/src/internal-packages.lisp index 9f1a87e13..2c21009cc 100644 --- a/src/internal-packages.lisp +++ b/src/internal-packages.lisp @@ -20,8 +20,23 @@ :image-object-width :image-draw-width :image-draw-height + :object-ascent :object-height :object-width + :row-metrics-of-objects + :layout-row + :translate-row + :row + :row-top + :row-height + :row-baseline + :row-placements + :row-fill-x + :row-fill-color + :placement + :placement-object + :placement-x + :placement-top :line-end-object :line-end-object-offset :text-object @@ -827,9 +842,11 @@ :cell-height :clear-to-end-of-window :js-eval - :render-line - :render-line-on-modeline + :cell-pixel-size + :render-row + :render-modeline-row :object-width :object-height + :object-ascent :image-natural-size :set-frame-color)) From f98c6e72889a4821df092174d2410543a71a1365 Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Sat, 1 Aug 2026 11:52:37 +0300 Subject: [PATCH 15/26] draw the webview frontend in pixels cell-width/cell-height report the client's real font metrics instead of 1, so every coordinate the server sends is a pixel. the client sends its metrics with every redraw, so a font change re-lays out the display the way a resize does. --- .../server/frontend/dist/assets/index.js | 2 +- frontends/server/frontend/editor.js | 302 +++++++++--------- frontends/server/main.lisp | 196 +++++------- frontends/server/view.lisp | 60 +++- 4 files changed, 283 insertions(+), 277 deletions(-) diff --git a/frontends/server/frontend/dist/assets/index.js b/frontends/server/frontend/dist/assets/index.js index 1cc125100..136c84167 100644 --- a/frontends/server/frontend/dist/assets/index.js +++ b/frontends/server/frontend/dist/assets/index.js @@ -1 +1 @@ -var __defProp=Object.defineProperty,__commonJSMin=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),__exportAll=(e,t)=>{let n={};for(var r in e)__defProp(n,r,{get:e[r],enumerable:!0});return t||__defProp(n,Symbol.toStringTag,{value:`Module`}),n};(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var require_models=__commonJSMin((e=>{var t=e&&e.__extends||(function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};return function(t,n){if(typeof n!=`function`&&n!==null)throw TypeError(`Class extends value `+String(n)+` is not a constructor or null`);e(t,n);function r(){this.constructor=t}t.prototype=n===null?Object.create(n):(r.prototype=n.prototype,new r)}})();Object.defineProperty(e,"__esModule",{value:!0}),e.createJSONRPCNotification=e.createJSONRPCRequest=e.createJSONRPCSuccessResponse=e.createJSONRPCErrorResponse=e.JSONRPCErrorCode=e.JSONRPCErrorException=e.isJSONRPCResponses=e.isJSONRPCResponse=e.isJSONRPCRequests=e.isJSONRPCRequest=e.isJSONRPCID=e.JSONRPC=void 0,e.JSONRPC=`2.0`,e.isJSONRPCID=function(e){return typeof e==`string`||typeof e==`number`||e===null},e.isJSONRPCRequest=function(t){return t.jsonrpc===e.JSONRPC&&t.method!==void 0&&t.result===void 0&&t.error===void 0},e.isJSONRPCRequests=function(t){return Array.isArray(t)&&t.every(e.isJSONRPCRequest)},e.isJSONRPCResponse=function(t){return t.jsonrpc===e.JSONRPC&&t.id!==void 0&&(t.result!==void 0||t.error!==void 0)},e.isJSONRPCResponses=function(t){return Array.isArray(t)&&t.every(e.isJSONRPCResponse)};var n=function(e,t,n){var r={code:e,message:t};return n!=null&&(r.data=n),r};e.JSONRPCErrorException=function(e){t(r,e);function r(t,n,i){var a=e.call(this,t)||this;return Object.setPrototypeOf(a,r.prototype),a.code=n,a.data=i,a}return r.prototype.toObject=function(){return n(this.code,this.message,this.data)},r}(Error),(function(e){e[e.ParseError=-32700]=`ParseError`,e[e.InvalidRequest=-32600]=`InvalidRequest`,e[e.MethodNotFound=-32601]=`MethodNotFound`,e[e.InvalidParams=-32602]=`InvalidParams`,e[e.InternalError=-32603]=`InternalError`})(e.JSONRPCErrorCode||={}),e.createJSONRPCErrorResponse=function(t,r,i,a){return{jsonrpc:e.JSONRPC,id:t,error:n(r,i,a)}},e.createJSONRPCSuccessResponse=function(t,n){return{jsonrpc:e.JSONRPC,id:t,result:n??null}},e.createJSONRPCRequest=function(t,n,r){return{jsonrpc:e.JSONRPC,id:t,method:n,params:r}},e.createJSONRPCNotification=function(t,n){return{jsonrpc:e.JSONRPC,method:t,params:n}}})),require_internal=__commonJSMin((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.DefaultErrorCode=void 0,e.DefaultErrorCode=0})),require_client=__commonJSMin((e=>{var t=e&&e.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||=Promise)(function(n,a){function o(e){try{c(r.next(e))}catch(e){a(e)}}function s(e){try{c(r.throw(e))}catch(e){a(e)}}function c(e){e.done?n(e.value):i(e.value).then(o,s)}c((r=r.apply(e,t||[])).next())})},n=e&&e.__generator||function(e,t){var n={label:0,sent:function(){if(a[0]&1)throw a[1];return a[1]},trys:[],ops:[]},r,i,a,o;return o={next:s(0),throw:s(1),return:s(2)},typeof Symbol==`function`&&(o[Symbol.iterator]=function(){return this}),o;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw TypeError(`Generator is already executing.`);for(;o&&(o=0,s[0]&&(n=0)),n;)try{if(r=1,i&&(a=s[0]&2?i.return:s[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,s[1])).done)return a;switch(i=0,a&&(s=[s[0]&2,a.value]),s[0]){case 0:case 1:a=s;break;case 4:return n.label++,{value:s[1],done:!1};case 5:n.label++,i=s[1],s=[0];continue;case 7:s=n.ops.pop(),n.trys.pop();continue;default:if((a=n.trys,!(a=a.length>0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{Object.defineProperty(e,"__esModule",{value:!0})})),require_server=__commonJSMin((e=>{var t=e&&e.__assign||function(){return t=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{var t=e&&e.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||=Promise)(function(n,a){function o(e){try{c(r.next(e))}catch(e){a(e)}}function s(e){try{c(r.throw(e))}catch(e){a(e)}}function c(e){e.done?n(e.value):i(e.value).then(o,s)}c((r=r.apply(e,t||[])).next())})},n=e&&e.__generator||function(e,t){var n={label:0,sent:function(){if(a[0]&1)throw a[1];return a[1]},trys:[],ops:[]},r,i,a,o;return o={next:s(0),throw:s(1),return:s(2)},typeof Symbol==`function`&&(o[Symbol.iterator]=function(){return this}),o;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw TypeError(`Generator is already executing.`);for(;o&&(o=0,s[0]&&(n=0)),n;)try{if(r=1,i&&(a=s[0]&2?i.return:s[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,s[1])).done)return a;switch(i=0,a&&(s=[s[0]&2,a.value]),s[0]){case 0:case 1:a=s;break;case 4:return n.label++,{value:s[1],done:!1};case 5:n.label++,i=s[1],s=[0];continue;case 7:s=n.ops.pop(),n.trys.pop();continue;default:if((a=n.trys,!(a=a.length>0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{var t=e&&e.__createBinding||(Object.create?(function(e,t,n,r){r===void 0&&(r=n);var i=Object.getOwnPropertyDescriptor(t,n);(!i||(`get`in i?!t.__esModule:i.writable||i.configurable))&&(i={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,r,i)}):(function(e,t,n,r){r===void 0&&(r=n),e[r]=t[n]})),n=e&&e.__exportStar||function(e,n){for(var r in e)r!=="default"&&!Object.prototype.hasOwnProperty.call(n,r)&&t(n,e,r)};Object.defineProperty(e,"__esModule",{value:!0}),n(require_client(),e),n(require_interfaces(),e),n(require_models(),e),n(require_server(),e),n(require_server_and_client(),e)})),import_dist=require_dist(),JSONRPC=class{constructor(e,{onConnected:t,onClosed:n}){this.url=e,this.onConnected=t,this.onClosed=n,this.messageQueue=[],this.serverAndClient=null,this.connect(),this.connectionEstablished=!1,this.timerId=null,this.closed=!1}close(){this.timerId&&clearTimeout(this.timerId),this.webSocket.close(),this.closed=!0}on(e,t){this.serverAndClient.addMethod(e,t)}async requestInternal(e,t,n){let r=await this.serverAndClient.request(e,t);n&&n(r)}requestMessageQueue(){this.messageQueue.forEach(e=>{let[t,n,r]=e;this.requestInternal(t,n,r)}),this.messageQueue=[]}request(e,t,n){this.webSocket.readyState===WebSocket.OPEN?this.requestInternal(e,t,n):this.messageQueue.push([e,t,n])}notify(e,t){switch(this.webSocket.readyState){case WebSocket.OPEN:this.serverAndClient.notify(e,t);break;case WebSocket.CLOSED:break}}connect(e){this.closed||(console.log(`connect`,this.url),this.webSocket=new WebSocket(this.url),this.serverAndClient||=new import_dist.JSONRPCServerAndClient(new import_dist.JSONRPCServer,new import_dist.JSONRPCClient(e=>{try{return this.webSocket.send(JSON.stringify(e)),Promise.resolve()}catch(e){return Promise.reject(e)}})),this.webSocket.onmessage=e=>{this.serverAndClient.receiveAndSend(JSON.parse(e.data.toString()))},this.webSocket.onopen=()=>{console.log(`WebSocket connection established`),this.connectionEstablished=!0,this.onConnected&&this.onConnected(),this.requestMessageQueue()},this.webSocket.onclose=e=>{console.error(`WebScoket closed`,e),this.serverAndClient.rejectAllPendingRequests(`Connection is closed (${e.reason}).`),this.connectionEstablished&&this.onClosed(),this.timerId=setTimeout(()=>{this.connect()},3e3)},this.webSocket.onerror=e=>{console.error(`WebSocket error:`,e),this.webSocket.close()})}},keyevent_exports=__exportAll({convertKeyEvent:()=>convertKeyEvent}),modifierKeys=[`Shift`,`Control`,`Alt`,`Meta`,`CapsLock`],convertKeyTable={Enter:`Return`,ArrowRight:`Right`,ArrowLeft:`Left`,ArrowUp:`Up`,ArrowDown:`Down`,"¡":`1`,"™":`2`,"£":`3`,"¢":`4`,"∞":`5`,"§":`6`,"¶":`7`,"•":`8`,ª:`9`,º:`0`,"–":`-`,"≠":`=`,"“":`[`,"‘":`]`,"«":`\\`,"…":`;`,æ:`'`,"≤":`,`,"≥":`.`,"÷":`/`,"⁄":`!`,"€":`@`,"‹":`#`,"›":`$`,fi:`%`,fl:`^`,"‡":`&`,"°":`*`,"·":`(`,"‚":`)`,"—":`_`,"±":`+`,"”":`{`,"’":`}`,"»":`|`,Ú:`:`,Æ:`"`,"¯":`<`,"˘":`>`,"¿":`?`,œ:`q`,"∑":`w`,"´":`e`,"®":`r`,"†":`t`,"¥":`y`,"¨":`u`,ˆ:`i`,ø:`o`,π:`p`,å:`a`,ß:`s`,"∂":`d`,ƒ:`f`,"©":`g`,"˙":`h`,"∆":`j`,"˚":`k`,"¬":`l`,Ω:`z`,"≈":`x`,ç:`c`,"√":`v`,"∫":`b`,"˜":`n`,µ:`m`,Œ:`Q`,"„":`W`,"´":`E`,"‰":`R`,ˇ:`T`,Á:`Y`,"¨":`U`,ˆ:`I`,Ø:`O`,"∏":`P`,Å:`A`,Í:`S`,Î:`D`,Ï:`F`,"˝":`G`,Ó:`H`,Ô:`J`,"":`K`,Ò:`L`,"¸":`Z`,"˛":`X`,Ç:`C`,"◊":`V`,ı:`B`,"˜":`N`,Â:`M`};function getKey(e){return e.altKey?convertKeyTable[e.key]||(e.code.startsWith(`Key`)?e.code[3].toLowerCase():null)||e.key:convertKeyTable[e.key]||e.key}function convertKeyEvent(e){return modifierKeys.indexOf(e.key)===-1?{key:getKey(e),ctrl:e.ctrlKey,meta:e.altKey,super:e.metaKey,shift:e.shiftKey}:null}var lib_exports=__exportAll({computeWidth:()=>computeWidth,eawVersion:()=>version,getEAW:()=>getEAW}),defs=[[0,31,`N`],[32,126,`Na`],[127,160,`N`],[161,161,`A`],[162,163,`Na`],[164,164,`A`],[165,166,`Na`],[167,168,`A`],[169,169,`N`],[170,170,`A`],[171,171,`N`],[172,172,`Na`],[173,174,`A`],[175,175,`Na`],[176,180,`A`],[181,181,`N`],[182,186,`A`],[187,187,`N`],[188,191,`A`],[192,197,`N`],[198,198,`A`],[199,207,`N`],[208,208,`A`],[209,214,`N`],[215,216,`A`],[217,221,`N`],[222,225,`A`],[226,229,`N`],[230,230,`A`],[231,231,`N`],[232,234,`A`],[235,235,`N`],[236,237,`A`],[238,239,`N`],[240,240,`A`],[241,241,`N`],[242,243,`A`],[244,246,`N`],[247,250,`A`],[251,251,`N`],[252,252,`A`],[253,253,`N`],[254,254,`A`],[255,256,`N`],[257,257,`A`],[258,272,`N`],[273,273,`A`],[274,274,`N`],[275,275,`A`],[276,282,`N`],[283,283,`A`],[284,293,`N`],[294,295,`A`],[296,298,`N`],[299,299,`A`],[300,304,`N`],[305,307,`A`],[308,311,`N`],[312,312,`A`],[313,318,`N`],[319,322,`A`],[323,323,`N`],[324,324,`A`],[325,327,`N`],[328,331,`A`],[332,332,`N`],[333,333,`A`],[334,337,`N`],[338,339,`A`],[340,357,`N`],[358,359,`A`],[360,362,`N`],[363,363,`A`],[364,461,`N`],[462,462,`A`],[463,463,`N`],[464,464,`A`],[465,465,`N`],[466,466,`A`],[467,467,`N`],[468,468,`A`],[469,469,`N`],[470,470,`A`],[471,471,`N`],[472,472,`A`],[473,473,`N`],[474,474,`A`],[475,475,`N`],[476,476,`A`],[477,592,`N`],[593,593,`A`],[594,608,`N`],[609,609,`A`],[610,707,`N`],[708,708,`A`],[709,710,`N`],[711,711,`A`],[712,712,`N`],[713,715,`A`],[716,716,`N`],[717,717,`A`],[718,719,`N`],[720,720,`A`],[721,727,`N`],[728,731,`A`],[732,732,`N`],[733,733,`A`],[734,734,`N`],[735,735,`A`],[736,767,`N`],[768,879,`A`],[880,912,`N`],[913,929,`A`],[930,930,`N`],[931,937,`A`],[938,944,`N`],[945,961,`A`],[962,962,`N`],[963,969,`A`],[970,1024,`N`],[1025,1025,`A`],[1026,1039,`N`],[1040,1103,`A`],[1104,1104,`N`],[1105,1105,`A`],[1106,4351,`N`],[4352,4447,`W`],[4448,8207,`N`],[8208,8208,`A`],[8209,8210,`N`],[8211,8214,`A`],[8215,8215,`N`],[8216,8217,`A`],[8218,8219,`N`],[8220,8221,`A`],[8222,8223,`N`],[8224,8226,`A`],[8227,8227,`N`],[8228,8231,`A`],[8232,8239,`N`],[8240,8240,`A`],[8241,8241,`N`],[8242,8243,`A`],[8244,8244,`N`],[8245,8245,`A`],[8246,8250,`N`],[8251,8251,`A`],[8252,8253,`N`],[8254,8254,`A`],[8255,8307,`N`],[8308,8308,`A`],[8309,8318,`N`],[8319,8319,`A`],[8320,8320,`N`],[8321,8324,`A`],[8325,8360,`N`],[8361,8361,`H`],[8362,8363,`N`],[8364,8364,`A`],[8365,8450,`N`],[8451,8451,`A`],[8452,8452,`N`],[8453,8453,`A`],[8454,8456,`N`],[8457,8457,`A`],[8458,8466,`N`],[8467,8467,`A`],[8468,8469,`N`],[8470,8470,`A`],[8471,8480,`N`],[8481,8482,`A`],[8483,8485,`N`],[8486,8486,`A`],[8487,8490,`N`],[8491,8491,`A`],[8492,8530,`N`],[8531,8532,`A`],[8533,8538,`N`],[8539,8542,`A`],[8543,8543,`N`],[8544,8555,`A`],[8556,8559,`N`],[8560,8569,`A`],[8570,8584,`N`],[8585,8585,`A`],[8586,8591,`N`],[8592,8601,`A`],[8602,8631,`N`],[8632,8633,`A`],[8634,8657,`N`],[8658,8658,`A`],[8659,8659,`N`],[8660,8660,`A`],[8661,8678,`N`],[8679,8679,`A`],[8680,8703,`N`],[8704,8704,`A`],[8705,8705,`N`],[8706,8707,`A`],[8708,8710,`N`],[8711,8712,`A`],[8713,8714,`N`],[8715,8715,`A`],[8716,8718,`N`],[8719,8719,`A`],[8720,8720,`N`],[8721,8721,`A`],[8722,8724,`N`],[8725,8725,`A`],[8726,8729,`N`],[8730,8730,`A`],[8731,8732,`N`],[8733,8736,`A`],[8737,8738,`N`],[8739,8739,`A`],[8740,8740,`N`],[8741,8741,`A`],[8742,8742,`N`],[8743,8748,`A`],[8749,8749,`N`],[8750,8750,`A`],[8751,8755,`N`],[8756,8759,`A`],[8760,8763,`N`],[8764,8765,`A`],[8766,8775,`N`],[8776,8776,`A`],[8777,8779,`N`],[8780,8780,`A`],[8781,8785,`N`],[8786,8786,`A`],[8787,8799,`N`],[8800,8801,`A`],[8802,8803,`N`],[8804,8807,`A`],[8808,8809,`N`],[8810,8811,`A`],[8812,8813,`N`],[8814,8815,`A`],[8816,8833,`N`],[8834,8835,`A`],[8836,8837,`N`],[8838,8839,`A`],[8840,8852,`N`],[8853,8853,`A`],[8854,8856,`N`],[8857,8857,`A`],[8858,8868,`N`],[8869,8869,`A`],[8870,8894,`N`],[8895,8895,`A`],[8896,8977,`N`],[8978,8978,`A`],[8979,8985,`N`],[8986,8987,`W`],[8988,9e3,`N`],[9001,9002,`W`],[9003,9192,`N`],[9193,9196,`W`],[9197,9199,`N`],[9200,9200,`W`],[9201,9202,`N`],[9203,9203,`W`],[9204,9311,`N`],[9312,9449,`A`],[9450,9450,`N`],[9451,9547,`A`],[9548,9551,`N`],[9552,9587,`A`],[9588,9599,`N`],[9600,9615,`A`],[9616,9617,`N`],[9618,9621,`A`],[9622,9631,`N`],[9632,9633,`A`],[9634,9634,`N`],[9635,9641,`A`],[9642,9649,`N`],[9650,9651,`A`],[9652,9653,`N`],[9654,9655,`A`],[9656,9659,`N`],[9660,9661,`A`],[9662,9663,`N`],[9664,9665,`A`],[9666,9669,`N`],[9670,9672,`A`],[9673,9674,`N`],[9675,9675,`A`],[9676,9677,`N`],[9678,9681,`A`],[9682,9697,`N`],[9698,9701,`A`],[9702,9710,`N`],[9711,9711,`A`],[9712,9724,`N`],[9725,9726,`W`],[9727,9732,`N`],[9733,9734,`A`],[9735,9736,`N`],[9737,9737,`A`],[9738,9741,`N`],[9742,9743,`A`],[9744,9747,`N`],[9748,9749,`W`],[9750,9755,`N`],[9756,9756,`A`],[9757,9757,`N`],[9758,9758,`A`],[9759,9791,`N`],[9792,9792,`A`],[9793,9793,`N`],[9794,9794,`A`],[9795,9799,`N`],[9800,9811,`W`],[9812,9823,`N`],[9824,9825,`A`],[9826,9826,`N`],[9827,9829,`A`],[9830,9830,`N`],[9831,9834,`A`],[9835,9835,`N`],[9836,9837,`A`],[9838,9838,`N`],[9839,9839,`A`],[9840,9854,`N`],[9855,9855,`W`],[9856,9874,`N`],[9875,9875,`W`],[9876,9885,`N`],[9886,9887,`A`],[9888,9888,`N`],[9889,9889,`W`],[9890,9897,`N`],[9898,9899,`W`],[9900,9916,`N`],[9917,9918,`W`],[9919,9919,`A`],[9920,9923,`N`],[9924,9925,`W`],[9926,9933,`A`],[9934,9934,`W`],[9935,9939,`A`],[9940,9940,`W`],[9941,9953,`A`],[9954,9954,`N`],[9955,9955,`A`],[9956,9959,`N`],[9960,9961,`A`],[9962,9962,`W`],[9963,9969,`A`],[9970,9971,`W`],[9972,9972,`A`],[9973,9973,`W`],[9974,9977,`A`],[9978,9978,`W`],[9979,9980,`A`],[9981,9981,`W`],[9982,9983,`A`],[9984,9988,`N`],[9989,9989,`W`],[9990,9993,`N`],[9994,9995,`W`],[9996,10023,`N`],[10024,10024,`W`],[10025,10044,`N`],[10045,10045,`A`],[10046,10059,`N`],[10060,10060,`W`],[10061,10061,`N`],[10062,10062,`W`],[10063,10066,`N`],[10067,10069,`W`],[10070,10070,`N`],[10071,10071,`W`],[10072,10101,`N`],[10102,10111,`A`],[10112,10132,`N`],[10133,10135,`W`],[10136,10159,`N`],[10160,10160,`W`],[10161,10174,`N`],[10175,10175,`W`],[10176,10213,`N`],[10214,10221,`Na`],[10222,10628,`N`],[10629,10630,`Na`],[10631,11034,`N`],[11035,11036,`W`],[11037,11087,`N`],[11088,11088,`W`],[11089,11092,`N`],[11093,11093,`W`],[11094,11097,`A`],[11098,11903,`N`],[11904,11929,`W`],[11930,11930,`N`],[11931,12019,`W`],[12020,12031,`N`],[12032,12245,`W`],[12246,12271,`N`],[12272,12287,`W`],[12288,12288,`F`],[12289,12350,`W`],[12351,12352,`N`],[12353,12438,`W`],[12439,12440,`N`],[12441,12543,`W`],[12544,12548,`N`],[12549,12591,`W`],[12592,12592,`N`],[12593,12686,`W`],[12687,12687,`N`],[12688,12771,`W`],[12772,12782,`N`],[12783,12830,`W`],[12831,12831,`N`],[12832,12871,`W`],[12872,12879,`A`],[12880,19903,`W`],[19904,19967,`N`],[19968,42124,`W`],[42125,42127,`N`],[42128,42182,`W`],[42183,43359,`N`],[43360,43388,`W`],[43389,44031,`N`],[44032,55203,`W`],[55204,57343,`N`],[57344,63743,`A`],[63744,64255,`W`],[64256,65023,`N`],[65024,65039,`A`],[65040,65049,`W`],[65050,65071,`N`],[65072,65106,`W`],[65107,65107,`N`],[65108,65126,`W`],[65127,65127,`N`],[65128,65131,`W`],[65132,65280,`N`],[65281,65376,`F`],[65377,65470,`H`],[65471,65473,`N`],[65474,65479,`H`],[65480,65481,`N`],[65482,65487,`H`],[65488,65489,`N`],[65490,65495,`H`],[65496,65497,`N`],[65498,65500,`H`],[65501,65503,`N`],[65504,65510,`F`],[65511,65511,`N`],[65512,65518,`H`],[65519,65532,`N`],[65533,65533,`A`],[65534,94175,`N`],[94176,94180,`W`],[94181,94191,`N`],[94192,94193,`W`],[94194,94207,`N`],[94208,100343,`W`],[100344,100351,`N`],[100352,101589,`W`],[101590,101631,`N`],[101632,101640,`W`],[101641,110575,`N`],[110576,110579,`W`],[110580,110580,`N`],[110581,110587,`W`],[110588,110588,`N`],[110589,110590,`W`],[110591,110591,`N`],[110592,110882,`W`],[110883,110897,`N`],[110898,110898,`W`],[110899,110927,`N`],[110928,110930,`W`],[110931,110932,`N`],[110933,110933,`W`],[110934,110947,`N`],[110948,110951,`W`],[110952,110959,`N`],[110960,111355,`W`],[111356,126979,`N`],[126980,126980,`W`],[126981,127182,`N`],[127183,127183,`W`],[127184,127231,`N`],[127232,127242,`A`],[127243,127247,`N`],[127248,127277,`A`],[127278,127279,`N`],[127280,127337,`A`],[127338,127343,`N`],[127344,127373,`A`],[127374,127374,`W`],[127375,127376,`A`],[127377,127386,`W`],[127387,127404,`A`],[127405,127487,`N`],[127488,127490,`W`],[127491,127503,`N`],[127504,127547,`W`],[127548,127551,`N`],[127552,127560,`W`],[127561,127567,`N`],[127568,127569,`W`],[127570,127583,`N`],[127584,127589,`W`],[127590,127743,`N`],[127744,127776,`W`],[127777,127788,`N`],[127789,127797,`W`],[127798,127798,`N`],[127799,127868,`W`],[127869,127869,`N`],[127870,127891,`W`],[127892,127903,`N`],[127904,127946,`W`],[127947,127950,`N`],[127951,127955,`W`],[127956,127967,`N`],[127968,127984,`W`],[127985,127987,`N`],[127988,127988,`W`],[127989,127991,`N`],[127992,128062,`W`],[128063,128063,`N`],[128064,128064,`W`],[128065,128065,`N`],[128066,128252,`W`],[128253,128254,`N`],[128255,128317,`W`],[128318,128330,`N`],[128331,128334,`W`],[128335,128335,`N`],[128336,128359,`W`],[128360,128377,`N`],[128378,128378,`W`],[128379,128404,`N`],[128405,128406,`W`],[128407,128419,`N`],[128420,128420,`W`],[128421,128506,`N`],[128507,128591,`W`],[128592,128639,`N`],[128640,128709,`W`],[128710,128715,`N`],[128716,128716,`W`],[128717,128719,`N`],[128720,128722,`W`],[128723,128724,`N`],[128725,128727,`W`],[128728,128731,`N`],[128732,128735,`W`],[128736,128746,`N`],[128747,128748,`W`],[128749,128755,`N`],[128756,128764,`W`],[128765,128991,`N`],[128992,129003,`W`],[129004,129007,`N`],[129008,129008,`W`],[129009,129291,`N`],[129292,129338,`W`],[129339,129339,`N`],[129340,129349,`W`],[129350,129350,`N`],[129351,129535,`W`],[129536,129647,`N`],[129648,129660,`W`],[129661,129663,`N`],[129664,129672,`W`],[129673,129679,`N`],[129680,129725,`W`],[129726,129726,`N`],[129727,129733,`W`],[129734,129741,`N`],[129742,129755,`W`],[129756,129759,`N`],[129760,129768,`W`],[129769,129775,`N`],[129776,129784,`W`],[129785,131071,`N`],[131072,196605,`W`],[196606,196607,`N`],[196608,262141,`W`],[262142,917759,`N`],[917760,917999,`A`],[918e3,983039,`N`],[983040,1048573,`A`],[1048574,1048575,`N`],[1048576,1114109,`A`],[1114110,1114111,`N`]],version=`15.1.0`;function getEAWOfCodePoint(e){let t=0,n=defs.length-1;for(;t!==n;){let r=t+(n-t>>1),[i,a,o]=defs[r];if(ea)t=r+1;else return o}return defs[t][2]}function getEAW(e,t=0){let n=e.codePointAt(t);if(n!==void 0)return getEAWOfCodePoint(n)}var defaultWidths={N:1,Na:1,W:2,F:2,H:1,A:1};function computeWidth(e,t){let n=0;for(let r of e){let e=getEAW(r);n+=t&&t[e]||defaultWidths[e]}return n}var textOffsetY=5,isSafari=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);function isWideChar(e){switch(getEAW(e)){case`F`:case`W`:return!0;default:return!1}}function isMacOS(){return window.navigator.userAgent.indexOf(`Mac OS X`)!==-1}function computeFontSize(e){let t=document.createElement(`canvas`).getContext(`2d`);t.font=e;let n=t.measureText(`W`);return[Math.floor(n.width),Math.round(n.fontBoundingBoxAscent+textOffsetY+(n.emHeightDescent||0))]}function drawBlock({ctx:e,x:t,y:n,width:r,height:i,style:a}){e.fillStyle=a,e.fillRect(t,n,r,i)}function drawText({ctx:e,x:t,y:n,text:r,font:i,style:a,option:o}){n+=Math.round(textOffsetY),e.fillStyle=a,e.font=i,e.textBaseline=`top`;for(let i of r)isWideChar(i)?(e.fillText(i,t,n,o.fontWidth*2),t+=o.fontWidth*2):(e.fillText(i,t,n,o.fontWidth),t+=o.fontWidth)}function drawHorizontalLine({ctx:e,x:t,y:n,width:r,style:i,lineWidth:a=1}){e.strokeStyle=i,e.lineWidth=a,e.setLineDash=[],e.beginPath(),e.moveTo(t,n),e.lineTo(t+r,n),e.stroke()}var Option=class{constructor({fontName:e,fontSize:t}){this.setFont(e,t),this.foreground=`#cccccc`,this.background=`#2d2d2d`}setFont(e,t){let n=t+`px `+e,[r,i]=computeFontSize(n);this.fontName=e,this.fontSize=t,this.fontWidth=r,this.fontHeight=i,this.font=n}};function getLemEditorElement(){return document.getElementById(`lem-editor`)}function normalizeWheelDelta(e,t,n,r){switch(n){case 0:return{dx:e/r,dy:t/r};case 2:return{dx:e*20,dy:t*20};default:return{dx:e,dy:t}}}function extractWholeLines(e,t){let n=Math.trunc(e),r=Math.trunc(t);return{scrollX:n,scrollY:r,remainderX:e-n,remainderY:t-r}}function cursorPosition(e,t){let[n,r]=t.getDisplayRectangle(),i=e.clientX-n,a=e.clientY-r;return{pixelX:i,pixelY:a,x:Math.floor(i/t.option.fontWidth),y:Math.floor(a/t.option.fontHeight)}}function makeWheelHandler(e){let t={x:0,y:0},n=!1,r={pixelX:0,pixelY:0,x:0,y:0};return i=>{i.preventDefault(),r=cursorPosition(i,e);let{dx:a,dy:o}=normalizeWheelDelta(i.deltaX,i.deltaY,i.deltaMode,e.option.fontHeight);t={x:t.x+a,y:t.y+o},n||(n=!0,requestAnimationFrame(()=>{n=!1;let{scrollX:i,scrollY:a,remainderX:o,remainderY:s}=extractWholeLines(t.x,t.y);t={x:o,y:s},(i!==0||a!==0)&&e.jsonrpc.notify(`input`,{kind:`wheel`,value:{...r,wheelX:-i,wheelY:-a}})}))}}function addMouseEventListeners({dom:e,editor:t,isDraggable:n,draggableStyle:r}){e.addEventListener(`contextmenu`,e=>{e.preventDefault()});let i=(e,n)=>{e.preventDefault();let[r,i]=t.getDisplayRectangle(),a=e.clientX-r,o=e.clientY-i,s=Math.floor(a/t.option.fontWidth),c=Math.floor(o/t.option.fontHeight);t.jsonrpc.notify(`input`,{kind:n,value:{x:s,y:c,pixelX:a,pixelY:o,button:e.button,clicks:e.detail}})};e.addEventListener(`mousedown`,e=>{n&&(document.body.style.cursor=r),t.focusHiddenInput(),i(e,`mousedown`)}),e.addEventListener(`mouseup`,e=>{n&&(document.body.style.cursor=`default`),i(e,`mouseup`)});let a=0;e.addEventListener(`mousemove`,e=>{e.preventDefault();let n=Date.now();if(n-a>50){a=n;let[r,i]=t.getDisplayRectangle(),o=e.clientX-r,s=e.clientY-i,c=Math.floor(o/t.option.fontWidth),l=Math.floor(s/t.option.fontHeight);t.jsonrpc.notify(`input`,{kind:`mousemove`,value:{x:c,y:l,pixelX:o,pixelY:s,button:e.buttons===0?null:e.buttons-1}})}}),n&&(e.addEventListener(`mouseover`,()=>{document.body.style.cursor=r}),e.addEventListener(`mouseout`,e=>{e.buttons!==1&&(document.body.style.cursor=`default`)})),e.addEventListener(`wheel`,makeWheelHandler(t))}var zIndexTable={"floating-window":200,modeline:100,"vertical-border":100,"horizontal-border":101};function zindex(e){return zIndexTable[e]||0}var borderOffsetX=5,borderOffsetY=10,BaseSurface=class{constructor({editor:e}){this.editor=e,this.mainDOM=null,this.wrapper=null}delete(){this.wrapper?getLemEditorElement().removeChild(this.wrapper):getLemEditorElement().removeChild(this.mainDOM)}setupDOM({dom:e,isFloating:t,border:n,cssClassName:r}){this.mainDOM=e,t&&n?(this.wrapper=document.createElement(`div`),r&&(this.wrapper.className=r),this.wrapper.style.position=`absolute`,this.wrapper.style.backgroundColor=this.editor.option.background,this.wrapper.style.zIndex=zindex(`floating-window`),this.wrapper.appendChild(e),getLemEditorElement().appendChild(this.wrapper)):(r&&(e.className=r),getLemEditorElement().appendChild(e))}move(e,t,n,r){let[i,a]=this.editor.getDisplayRectangle(),o=Math.floor(n==null?i+e*this.editor.option.fontWidth:i+n),s=Math.floor(r==null?a+t*this.editor.option.fontHeight:a+r);this.wrapper?(this.wrapper.style.left=o-borderOffsetX+`px`,this.wrapper.style.top=s-borderOffsetY+`px`,this.mainDOM.style.left=borderOffsetX+`px`,this.mainDOM.style.top=borderOffsetY+`px`):(this.mainDOM.style.left=o+`px`,this.mainDOM.style.top=s+`px`)}_resize(e,t,n,r){let i=window.devicePixelRatio||1,a=n??e*this.editor.option.fontWidth,o=r??t*this.editor.option.fontHeight;this.mainDOM.width=a*i,this.mainDOM.height=o*i,this.mainDOM.style.width=a+`px`,this.mainDOM.style.height=o+`px`,this.wrapper&&(this.wrapper.style.width=a+borderOffsetX*2+`px`,this.wrapper.style.height=o+borderOffsetY*2+`px`)}drawBlock(e,t,n,r,i){}drawText(e,t,n,r,i){}drawImage(e,t,n,r,i,a,o){}clearImages(e,t){}clearAllImages(){}touch(){}evalIn(code){return eval(code)}},CanvasSurface=class extends BaseSurface{constructor({editor:e,view:t,x:n,y:r,width:i,height:a,styles:o,isFloating:s,border:c,cssClassName:l}){super({editor:e});let u=this.setupCanvas(o);this.setupDOM({dom:u,isFloating:s,border:c,cssClassName:l}),this.move(n,r),this.resize(i,a),this.drawingQueue=[],addMouseEventListeners({dom:u,editor:e})}setupCanvas(e){let t=document.createElement(`canvas`);if(t.style.position=`absolute`,e)for(let n in e)t.style[n]=e[n];return t}resize(e,t,n,r){this._resize(e,t,n,r);let i=window.devicePixelRatio||1;this.mainDOM.getContext(`2d`).scale(i,i)}move(e,t,n,r){if(super.move(e,t,n,r),this.imageEls)for(let[,e]of this.imageEls)this.positionImage(e)}delete(){this.clearAllImages(),super.delete()}drawBlock(e,t,n,r,i){let a=this.editor.option;this.drawingQueue.push(function(o){drawBlock({ctx:o,x:e*a.fontWidth,y:t*a.fontHeight,width:n*a.fontWidth,height:r*a.fontHeight,style:i})})}drawText(e,t,n,r,i,a){let o=this.editor.option;this.drawingQueue.push(function(s){if(a=a?`${o.fontSize}px ${a}`:o.font,!i)drawBlock({ctx:s,x:e*o.fontWidth,y:t*o.fontHeight,width:r*o.fontWidth,height:o.fontHeight,style:o.background}),drawText({ctx:s,x:e*o.fontWidth,y:t*o.fontHeight,text:n,style:o.foreground,font:a,option:o});else{let{foreground:c,background:l,bold:u,reverse:d,underline:f,cursor:p}=i;if(c||=o.foreground,l||=o.background,d){let e=l;l=c,c=e}p&&(l=o.background);let m=e*o.fontWidth,h=t*o.fontHeight;drawBlock({ctx:s,x:m,y:h,width:r*o.fontWidth,height:o.fontHeight,style:l}),drawText({ctx:s,x:m,y:h,text:n,style:c,font:u?`bold `+a:a,option:o}),f&&drawHorizontalLine({ctx:s,x:m,y:h+o.fontHeight-2,width:r*o.fontWidth,style:typeof f==`string`?f:c,lineWidth:2})}})}imageBaseLeft(){return parseFloat(this.mainDOM.style.left)||0}imageBaseTop(){return parseFloat(this.mainDOM.style.top)||0}drawImage(e,t,n,r,i,a,o){this.imageEls||=new Map;let s=e+`,`+t,c=this.imageEls.get(s);if(c&&c.url!==o&&(c.el.remove(),this.imageEls.delete(s),c=null),!c){let e=document.createElement(`img`);e.style.position=`absolute`,e.style.pointerEvents=`none`,e.style.zIndex=`1`,e.src=o,this.mainDOM.parentNode.appendChild(e),c={el:e,url:o},this.imageEls.set(s,c)}c.x=e,c.y=t,c.widthCells=n,c.heightCells=r,c.pixelWidth=i,c.pixelHeight=a,this.positionImage(c)}positionImage(e){let t=this.editor.option,n=e.widthCells*t.fontWidth,r=e.heightCells*t.fontHeight;e.el.style.left=this.imageBaseLeft()+e.x*t.fontWidth+`px`,e.el.style.top=this.imageBaseTop()+e.y*t.fontHeight+`px`,e.el.style.width=(e.pixelWidth==null?n:Math.min(e.pixelWidth,n))+`px`,e.el.style.height=(e.pixelHeight==null?r:Math.min(e.pixelHeight,r))+`px`}clearImages(e,t){if(this.imageEls)for(let[n,r]of this.imageEls)r.ye&&(r.el.remove(),this.imageEls.delete(n))}clearAllImages(){if(this.imageEls){for(let[,e]of this.imageEls)e.el.remove();this.imageEls.clear()}}touch(){let e=this.mainDOM.getContext(`2d`);for(let t of this.drawingQueue)t(e);this.drawingQueue=[]}activate(){this.mainDOM.dataset.store=`active`}deactivate(){this.mainDOM.dataset.store=`inactive`}},HTMLSurface=class extends BaseSurface{constructor({editor:e,x:t,y:n,width:r,height:i,styles:a,option:o,isFloating:s,border:c,html:l}){super({editor:e});let u=document.createElement(`iframe`);this.setupDOM({dom:u,isFloating:s,border:c}),u.style.position=`absolute`,u.style.backgroundColor=o.background,u.setAttribute(`sandbox`,`allow-scripts allow-same-origin`),u.srcdoc=l,u.addEventListener(`load`,()=>{let e=u.contentWindow;e.invokeLem=(e,t)=>parent.postMessage({type:`invoke-lem`,method:e,args:t})}),this.iframe=u,this.move(t,n),this.resize(r,i)}resize(e,t,n,r){this._resize(e,t,n,r)}update(e){let t=this.iframe.contentWindow.scrollY;this.iframe.srcdoc=e,this.iframe.onload=()=>{this.iframe.onload=null,this.iframe.contentWindow.scrollTo(0,t)}}evalIn(e){return this.iframe.contentWindow.eval(e)}},VerticalBorder=class{constructor({x:e,y:t,height:n,option:r,editor:i}){this.option=r,this.editor=i,this.line=document.createElement(`div`),this.line.className=`lem-editor__vertical-border`,this.line.style.height=n*r.fontHeight+`px`,this.line.style.position=`absolute`,this.line.style.zIndex=zindex(`vertical-border`),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:i,isDraggable:!0,draggableStyle:`col-resize`})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.line.style.left=Math.floor(n+e*this.option.fontWidth-this.option.fontWidth/2)+`px`,this.line.style.top=r+t*this.option.fontHeight+`px`}resize(e){this.line.style.height=e*this.option.fontHeight+`px`}},HorizontalBorder=class{constructor({x:e,y:t,width:n,option:r,editor:i}){this.option=r,this.editor=i,this.line=document.createElement(`div`),this.line.className=`lem-editor__horizontal-border`,this.line.style.width=n*r.fontWidth+`px`,this.line.style.position=`absolute`,this.line.style.zIndex=zindex(`horizontal-border`),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:i,isDraggable:!0,draggableStyle:`row-resize`})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.line.style.left=n+e*this.option.fontWidth+`px`,this.line.style.top=Math.floor(r+t*this.option.fontHeight-4)+`px`}resize(e){this.line.style.width=e*this.option.fontWidth+`px`}},viewStyles={header:()=>{},tile:()=>{},floating:e=>({boxSizing:`border-box`,borderColor:e.foreground,backgroundColor:e.background})};function getViewStyle(e,t){return viewStyles[e](t)||{}}var View=class{constructor({id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,useModeline:l,kind:u,type:d,content:f,border:p,borderShape:m,option:h,editor:g}){switch(this.option=h,this.id=e,this.x=t,this.y=n,this.width=r,this.height=i,this.pixelX=a,this.pixelY=o,this.pixelWidth=s,this.pixelHeight=c,this.useModeline=l,this.kind=u,this.type=d,this.border=p,this.borderShape=m,this.editor=g,this.bottomBar=null,this.leftsideBar=null,u){case`tile`:this.mainSurface=this.makeSurface(d,f),this.leftSideBar=new VerticalBorder({x:t,y:n,height:i+ +!!l,option:h,editor:g}),l||(this.bottomBar=new HorizontalBorder({x:t,y:n+i-1,width:r,option:h,editor:g}));break;case`header`:this.mainSurface=this.makeSurface(d,f);break;case`floating`:this.mainSurface=this.makeSurface(d,f),m===`left-border`&&(this.leftSideBar=new VerticalBorder({x:t,y:n,height:i,option:h,editor:g}));break}this.modelineSurface=l?this.makeModelineSurface():null,u===`floating`&&(a!=null||o!=null)&&this.move(t,n,a,o)}delete(){this.mainSurface.delete(),this.modelineSurface&&this.modelineSurface.delete(),this.leftSideBar&&this.leftSideBar.delete(),this.bottomBar&&this.bottomBar.delete()}move(e,t,n,r){if(this.x=e,this.y=t,this.pixelX=n,this.pixelY=r,this.mainSurface.move(e,t,n,r),this.modelineSurface){let i=r!=null&&this.pixelHeight!=null?r+this.pixelHeight:null;this.modelineSurface.move(e,t+this.height,n,i)}this.leftSideBar&&this.leftSideBar.move(e,t),this.bottomBar&&this.bottomBar.move(e,t+this.height)}resize(e,t,n,r){if(this.width=e,this.height=t,this.pixelWidth=n,this.pixelHeight=r,this.mainSurface.resize(e,t,n,r),this.modelineSurface){let t=this.pixelY!=null&&r!=null?this.pixelY+r:null;this.modelineSurface.move(this.x,this.y+this.height,this.pixelX,t),this.modelineSurface.resize(e,1)}this.leftSideBar&&this.leftSideBar.resize(t+ +!!this.modelineSurface),this.bottomBar&&this.bottomBar.resize(e)}clear(){this.mainSurface.drawBlock(0,0,this.width,this.height,this.option.background)}clearEol(e,t,n=1){this.mainSurface.drawBlock(e,t,this.width-e,n,this.option.background),this.mainSurface.clearImages(t,t+n)}clearEob(e,t){this.mainSurface.drawBlock(e,t,this.width,this.height-t,this.option.background),this.mainSurface.clearImages(t,this.height)}print(e,t,n,r,i,a){this.mainSurface.drawText(e,t,n,r,i,a)}printImage(e,t,n,r,i,a,o){this.mainSurface.drawImage(e,t,n,r,i,a,o)}printToModeline(e,t,n,r,i){this.modelineSurface&&this.modelineSurface.drawText(e,t,n,r,i)}touch(e){this.mainSurface.touch(),this.modelineSurface&&(this.modelineSurface.touch(),e?this.modelineSurface.activate():this.modelineSurface.deactivate())}makeSurface(e,t){switch(e){case`html`:return this.makeHTMLSurface(t);case`editor`:return this.makeEditorSurface();default:console.error(`unknown type: ${e}`)}}makeHTMLSurface(e){return new HTMLSurface({editor:this.editor,x:this.x,y:this.y,width:this.width,height:this.height,styles:getViewStyle(this.kind,this.option),option:this.option,isFloating:this.kind===`floating`,border:this.border,html:e})}makeEditorSurface(){let e=this.borderShape===`left-border`?0:this.border,t=this.kind===`floating`;return new CanvasSurface({option:this.editor.option,x:this.x,y:this.y,width:this.width,height:this.height,styles:getViewStyle(this.kind,this.option),editor:this.editor,border:e,isFloating:t,view:this,cssClassName:t&&e?`lem-editor__floating-window--bordered`:null})}makeModelineSurface(){let e=new CanvasSurface({option:this.editor.option,x:this.x,y:this.y+this.height,width:this.width,height:1,editor:this.editor,view:this,styles:{zIndex:zindex(`modeline`)},cssClassName:`lem-editor__mode-line`});return addMouseEventListeners({dom:e.mainDOM,editor:this.editor,isDraggable:!0,draggableStyle:`row-resize`}),e}changeToHTMLContent(e){this.mainSurface.constructor.name===`HTMLSurface`?this.mainSurface.update(e):(this.mainSurface.delete(),this.mainSurface=this.makeHTMLSurface(e))}changeToEditorContent(){this.mainSurface.delete(),this.mainSurface=this.makeEditorSurface()}evalIn(e){return this.mainSurface.evalIn(e)}};function isPasteKeyEvent(e){return isMacOS()?e.metaKey&&e.key===`v`:e.ctrlKey&&e.shiftKey&&e.key===`V`}var Input=class{constructor(e){let t=e.option;this.editor=e,this.composition=!1,this.ignoreKeydownAfterCompositionend=!1,this.span=document.createElement(`span`),this.span.style.color=t.foreground,this.span.style.backgroundColor=t.background,this.span.style.position=`absolute`,this.span.style.zIndex=1e6,this.span.style.top=`0`,this.span.style.left=`0`,this.span.style.font=t.font,this.input=document.createElement(`input`),this.input.style.backgroundColor=`transparent`,this.input.style.color=`transparent`,this.input.style.width=`0`,this.input.style.padding=`0`,this.input.style.margin=`0`,this.input.style.border=`none`,this.input.style.position=`absolute`,this.input.style.zIndex=`-10`,this.input.style.top=`0`,this.input.style.left=`0`,this.input.style.font=t.font,this.input.addEventListener(`blur`,e=>{this.input.focus()}),this.input.addEventListener(`input`,e=>{this.composition===!1&&(this.input.value=``,this.span.innerHTML=``,this.input.style.width=`0`,isMacOS()||this.editor.emitInputString(e.data))}),this.input.addEventListener(`paste`,async e=>{e.preventDefault();let t=e.clipboardData||window.Clipboard.data,n=t?.getData(`text`)??t?.getData(`text/plain`);if(n&&n.length>0){this.editor.emitInputString(n);return}try{if(navigator.clipboard?.readText){let e=await navigator.clipboard.readText();if(e&&e.length>0){this.editor.emitInputString(e);return}}}catch(e){console.warn(`clipboard.readText() failed:`,e)}alert(`Paste failed (permission/environment restriction`)}),this.input.addEventListener(`keydown`,e=>{if(!isPasteKeyEvent(e)&&!(e.isComposing||this.composition)&&e.key!==`Process`){if(this.ignoreKeydownAfterCompositionend&&(isSafari||isMacOS())){e.preventDefault(),this.ignoreKeydownAfterCompositionend=!1;return}if(!(!isMacOS()&&!e.ctrlKey&&!e.altKey&&e.key.length===1)&&(e.preventDefault(),e.isComposing!==!0&&e.code!==``))return setTimeout(()=>{this.composition||(this.editor.emitInput(e),this.input.value=``)},0),!1}}),this.input.addEventListener(`compositionstart`,e=>{this.composition=!0,this.span.innerHTML=this.input.value,this.input.style.width=this.span.offsetWidth+`px`}),this.input.addEventListener(`compositionupdate`,e=>{this.span.innerHTML=e.data,this.input.style.width=this.span.offsetWidth+`px`}),this.input.addEventListener(`compositionend`,e=>{this.composition=!1,this.editor.emitInputString(this.input.value),this.input.value=``,this.span.innerHTML=this.input.value,this.input.style.width=`0`,this.ignoreKeydownAfterCompositionend=!0}),document.body.appendChild(this.input),document.body.appendChild(this.span),this.input.focus()}finalize(){document.body.removeChild(this.input),document.body.removeChild(this.span)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.span.style.top=r+t+`px`,this.span.style.left=n+e+`px`,this.input.style.top=this.span.offsetTop+`px`,this.input.style.left=this.span.offsetLeft+`px`}updateForeground(e){this.span.style.color=e}updateBackground(e){this.span.style.backgroundColor=e}},MessageTable=class{constructor(){this.map=new Map}register(e,t){for(let n in t){let r=t[n];this.map.set(n,r),e.on(n,r)}}get(e){return this.map.get(e)}};function getDisplayRectangleDefault(){return[0,0,window.innerWidth,window.innerHeight]}var Editor=class{constructor({getDisplayRectangle:e=getDisplayRectangleDefault,fontName:t,fontSize:n,url:r,onExit:i,onClosed:a}){this.getDisplayRectangle=e,this.option=new Option({fontName:t,fontSize:n}),this.onExit=i,this.input=new Input(this),this.cursors=new Map,this.cursorOverlay=document.createElement(`div`),this.cursorOverlay.className=`lem-cursor`,this.cursorOverlay.style.width=this.option.fontWidth+`px`,this.cursorOverlay.style.height=this.option.fontHeight+`px`,this.cursorOverlay.style.backgroundColor=`#ffffff`,this.cursorType=`box`,this.viewMap=new Map,this.jsonrpc=new JSONRPC(r,{onClosed:()=>{a()}}),this.messageTable=new MessageTable,this.messageTable.register(this.jsonrpc,{"update-foreground":this.updateForeground.bind(this),"update-background":this.updateBackground.bind(this),"make-view":this.makeView.bind(this),"delete-view":this.deleteView.bind(this),"resize-view":this.resize.bind(this),"move-view":this.move.bind(this),"redraw-view-after":this.redrawViewAfter.bind(this),clear:this.clear.bind(this),"clear-eol":this.clearEol.bind(this),"clear-eob":this.clearEob.bind(this),put:this.put.bind(this),"put-image":this.putImage.bind(this),"modeline-put":this.modelinePut.bind(this),"update-display":this.updateDisplay.bind(this),"move-cursor":this.moveCursor.bind(this),"change-view":this.changeView.bind(this),"resize-display":this.resizeDisplay.bind(this),bulk:this.bulk.bind(this),exit:this.exitEditor.bind(this),"get-clipboard-text":this.getClipboardText.bind(this),"set-clipboard-text":this.setClipboardText.bind(this),"js-eval":this.jsEval.bind(this),"set-font":this.setFont.bind(this),"get-font":this.getFont.bind(this),"get-display-size":this.getDisplaySize.bind(this),"load-css":this.loadCSS.bind(this),"update-cursor-shape":this.updateCursorShape.bind(this)}),this.login(),this.boundedHandleResize=this.handleResize.bind(this),this.focusHiddenInput=this.focusHiddenInput.bind(this)}init(){window.addEventListener(`resize`,this.boundedHandleResize),document.getElementsByTagName(`html`)[0].style[`background-color`]=`#333`,getLemEditorElement().appendChild(this.cursorOverlay)}finalize(){window.removeEventListener(`resize`,this.boundedHandleResize),this.input.finalize(),this.cursorOverlay.parentNode&&this.cursorOverlay.parentNode.removeChild(this.cursorOverlay)}closeConnection(){this.jsonrpc.close()}emitInput(e){let t=convertKeyEvent(e);if(t){if(t.key===`]`&&t.ctrl&&!t.meta&&!t.super&&!t.shift){this.jsonrpc.notify(`input`,{kind:`abort`});return}t.key!==`Unidentified`&&this.jsonrpc.notify(`input`,{kind:`key`,value:t})}}emitInputString(e){e?this.jsonrpc.notify(`input`,{kind:`input-string`,value:e}):console.error(`unexpected argument`,e)}handleResize(e){let t=!0;this.jsonrpc.notify(`redraw`,{size:this.getDisplaySize()})}focusHiddenInput(){let e=this.input?.input;if(e){try{window.focus()}catch{}requestAnimationFrame(()=>{setTimeout(()=>{e.focus({preventScroll:!0})},0)})}}sendNotification(e,t){this.jsonrpc.notify(e,t)}request(e,t,n){this.jsonrpc.request(e,t,n)}getDisplaySize(){let[e,t,n,r]=this.getDisplayRectangle();return{width:Math.floor(n/this.option.fontWidth),height:Math.floor(r/this.option.fontHeight)}}callMessage(e,t){this.messageTable.get(e)(t)}findViewById(e){return this.viewMap.get(e)}login(){this.jsonrpc.request(`login`,{size:this.getDisplaySize(),foreground:this.option.foreground,background:this.option.background,fontWidth:this.option.fontWidth,fontHeight:this.option.fontHeight},e=>{if(this.updateForeground(e.foreground),this.updateBackground(e.background),e.views)for(let t of e.views)this.makeView(t);this.jsonrpc.notify(`redraw`,{size:this.getDisplaySize()})})}updateForeground(e){e!=null&&(this.option.foreground=e,this.input.updateForeground(e))}updateBackground(e){if(e==null)return;this.option.background=e,this.input.updateBackground(e);let t=getLemEditorElement();t.style.backgroundColor=e}makeView({id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,use_modeline:l,kind:u,type:d,content:f,border:p,border_shape:m}){let h=new View({option:this.option,id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,useModeline:l,kind:u,type:d,content:f,border:p,borderShape:m,editor:this});this.viewMap.set(e,h)}deleteView({viewInfo:{id:e}}){this.findViewById(e).delete(),this.viewMap.delete(e)}resize({viewInfo:{id:e},width:t,height:n,pixelWidth:r,pixelHeight:i}){let a=this.findViewById(e);a?a.resize(t,n,r,i):console.warn(`resize: view not found for id ${e}`)}move({viewInfo:{id:e},x:t,y:n,pixelX:r,pixelY:i}){let a=this.findViewById(e);a?a.move(t,n,r,i):console.warn(`move: view not found for id ${e}`)}redrawViewAfter({viewInfo:{id:e},isActive:t}){this.findViewById(e).touch(t)}clear({viewInfo:{id:e}}){this.findViewById(e).clear()}clearEol({viewInfo:{id:e},x:t,y:n,height:r}){this.findViewById(e).clearEol(t,n,r)}clearEob({viewInfo:{id:e},x:t,y:n}){this.findViewById(e).clearEob(t,n)}put({viewInfo:{id:e},x:t,y:n,text:r,textWidth:i,attribute:a,font:o}){this.findViewById(e).print(t,n,r,i,a,o)}putImage({viewInfo:{id:e},x:t,y:n,width:r,height:i,pixelWidth:a,pixelHeight:o,url:s}){this.findViewById(e).printImage(t,n,r,i,a,o,s)}modelinePut({viewInfo:{id:e},x:t,y:n,text:r,textWidth:i,attribute:a}){this.findViewById(e).printToModeline(t,n,r,i,a)}updateDisplay(){}moveCursor({viewInfo:{id:e},x:t,y:n,color:r,cursorText:i,cursorForeground:a}){let o=this.findViewById(e),[s,c]=this.getDisplayRectangle(),l=o.x*this.option.fontWidth+t*this.option.fontWidth,u=o.y*this.option.fontHeight+n*this.option.fontHeight;this.input.move(l,u);let d=r||this.option.foreground,f=a||this.option.background,p=this.cursorOverlay;switch(this.cursorType){case`bar`:p.style.left=s+l+`px`,p.style.top=c+u+`px`,p.style.width=`2px`,p.style.height=this.option.fontHeight+`px`,p.style.backgroundColor=d,p.textContent=``,p.style.color=``,p.style.font=``,p.style.paddingTop=``;break;case`underline`:p.style.left=s+l+`px`,p.style.top=c+u+this.option.fontHeight-2+`px`,p.style.width=this.option.fontWidth+`px`,p.style.height=`2px`,p.style.backgroundColor=d,p.textContent=``,p.style.color=``,p.style.font=``,p.style.paddingTop=``;break;default:p.style.left=s+l+`px`,p.style.top=c+u+`px`,p.style.width=this.option.fontWidth+`px`,p.style.height=this.option.fontHeight+`px`,p.style.backgroundColor=d,p.style.font=this.option.font,p.style.paddingTop=textOffsetY+`px`,p.textContent=i||``,p.style.color=f;break}p.style.animation=`none`,p.offsetHeight,p.style.animation=``}updateCursorShape({cursorType:e}){this.cursorType=e||`box`}changeView({viewInfo:{id:e},type:t,content:n}){let r=this.findViewById(e);switch(t){case`html`:r.changeToHTMLContent(n);break;case`editor`:r.changeToEditorContent();break}}resizeDisplay({width:e,height:t}){let n=getLemEditorElement();n.style.width=Math.floor(e*this.option.fontWidth)+`px`,n.style.height=Math.floor(t*this.option.fontHeight)+`px`}bulk(e){for(let{method:t,argument:n}of e)this.callMessage(t,n)}exitEditor(){this.onExit&&this.onExit()}getClipboardText(){navigator.clipboard?.readText().then(e=>{this.jsonrpc.notify(`got-clipboard-text`,{text:e})})}setClipboardText({text:e}){navigator.clipboard&&navigator.clipboard.writeText(e)}jsEval({viewInfo:{id:e},code:t}){let n=this.findViewById(e).evalIn(t);return n&&n.toString()}setFont({fontName:e,fontSize:t}){this.option.setFont(e||this.option.fontName,t||this.option.fontSize)}getFont(){return{name:this.option.fontName,size:this.option.fontSize}}loadCSS({content:e}){let t=document.createElement(`style`);t.textContent=e,document.head.appendChild(t)}notifyToServer(e,t){this.jsonrpc.notify(`invoke`,{method:e,args:t})}},canvas=document.querySelector(`#editor`);async function main(){await Promise.all([document.fonts.load(`19px file-icons`),document.fonts.load(`19px AllTheIcons`),document.fonts.load(`19px fontawesome`),document.fonts.load(`19px material-design-icons`),document.fonts.load(`19px octicons`)]),await document.fonts.ready;let e=new Editor({canvas,fontName:`Monospace`,fontSize:18,url:`${window.location.protocol===`https:`?`wss`:`ws`}://${window.location.hostname}:${window.location.port}`,onExit:null,onClosed:null});window.addEventListener(`message`,t=>{t.data.type===`invoke-lem`&&e.notifyToServer(t.data.method,t.data.args)}),e.init()}main(); \ No newline at end of file +var __defProp=Object.defineProperty,__commonJSMin=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),__exportAll=(e,t)=>{let n={};for(var r in e)__defProp(n,r,{get:e[r],enumerable:!0});return t||__defProp(n,Symbol.toStringTag,{value:`Module`}),n};(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var require_models=__commonJSMin((e=>{var t=e&&e.__extends||(function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};return function(t,n){if(typeof n!=`function`&&n!==null)throw TypeError(`Class extends value `+String(n)+` is not a constructor or null`);e(t,n);function r(){this.constructor=t}t.prototype=n===null?Object.create(n):(r.prototype=n.prototype,new r)}})();Object.defineProperty(e,"__esModule",{value:!0}),e.createJSONRPCNotification=e.createJSONRPCRequest=e.createJSONRPCSuccessResponse=e.createJSONRPCErrorResponse=e.JSONRPCErrorCode=e.JSONRPCErrorException=e.isJSONRPCResponses=e.isJSONRPCResponse=e.isJSONRPCRequests=e.isJSONRPCRequest=e.isJSONRPCID=e.JSONRPC=void 0,e.JSONRPC=`2.0`,e.isJSONRPCID=function(e){return typeof e==`string`||typeof e==`number`||e===null},e.isJSONRPCRequest=function(t){return t.jsonrpc===e.JSONRPC&&t.method!==void 0&&t.result===void 0&&t.error===void 0},e.isJSONRPCRequests=function(t){return Array.isArray(t)&&t.every(e.isJSONRPCRequest)},e.isJSONRPCResponse=function(t){return t.jsonrpc===e.JSONRPC&&t.id!==void 0&&(t.result!==void 0||t.error!==void 0)},e.isJSONRPCResponses=function(t){return Array.isArray(t)&&t.every(e.isJSONRPCResponse)};var n=function(e,t,n){var r={code:e,message:t};return n!=null&&(r.data=n),r};e.JSONRPCErrorException=function(e){t(r,e);function r(t,n,i){var a=e.call(this,t)||this;return Object.setPrototypeOf(a,r.prototype),a.code=n,a.data=i,a}return r.prototype.toObject=function(){return n(this.code,this.message,this.data)},r}(Error),(function(e){e[e.ParseError=-32700]=`ParseError`,e[e.InvalidRequest=-32600]=`InvalidRequest`,e[e.MethodNotFound=-32601]=`MethodNotFound`,e[e.InvalidParams=-32602]=`InvalidParams`,e[e.InternalError=-32603]=`InternalError`})(e.JSONRPCErrorCode||={}),e.createJSONRPCErrorResponse=function(t,r,i,a){return{jsonrpc:e.JSONRPC,id:t,error:n(r,i,a)}},e.createJSONRPCSuccessResponse=function(t,n){return{jsonrpc:e.JSONRPC,id:t,result:n??null}},e.createJSONRPCRequest=function(t,n,r){return{jsonrpc:e.JSONRPC,id:t,method:n,params:r}},e.createJSONRPCNotification=function(t,n){return{jsonrpc:e.JSONRPC,method:t,params:n}}})),require_internal=__commonJSMin((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.DefaultErrorCode=void 0,e.DefaultErrorCode=0})),require_client=__commonJSMin((e=>{var t=e&&e.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||=Promise)(function(n,a){function o(e){try{c(r.next(e))}catch(e){a(e)}}function s(e){try{c(r.throw(e))}catch(e){a(e)}}function c(e){e.done?n(e.value):i(e.value).then(o,s)}c((r=r.apply(e,t||[])).next())})},n=e&&e.__generator||function(e,t){var n={label:0,sent:function(){if(a[0]&1)throw a[1];return a[1]},trys:[],ops:[]},r,i,a,o;return o={next:s(0),throw:s(1),return:s(2)},typeof Symbol==`function`&&(o[Symbol.iterator]=function(){return this}),o;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw TypeError(`Generator is already executing.`);for(;o&&(o=0,s[0]&&(n=0)),n;)try{if(r=1,i&&(a=s[0]&2?i.return:s[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,s[1])).done)return a;switch(i=0,a&&(s=[s[0]&2,a.value]),s[0]){case 0:case 1:a=s;break;case 4:return n.label++,{value:s[1],done:!1};case 5:n.label++,i=s[1],s=[0];continue;case 7:s=n.ops.pop(),n.trys.pop();continue;default:if((a=n.trys,!(a=a.length>0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{Object.defineProperty(e,"__esModule",{value:!0})})),require_server=__commonJSMin((e=>{var t=e&&e.__assign||function(){return t=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{var t=e&&e.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||=Promise)(function(n,a){function o(e){try{c(r.next(e))}catch(e){a(e)}}function s(e){try{c(r.throw(e))}catch(e){a(e)}}function c(e){e.done?n(e.value):i(e.value).then(o,s)}c((r=r.apply(e,t||[])).next())})},n=e&&e.__generator||function(e,t){var n={label:0,sent:function(){if(a[0]&1)throw a[1];return a[1]},trys:[],ops:[]},r,i,a,o;return o={next:s(0),throw:s(1),return:s(2)},typeof Symbol==`function`&&(o[Symbol.iterator]=function(){return this}),o;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw TypeError(`Generator is already executing.`);for(;o&&(o=0,s[0]&&(n=0)),n;)try{if(r=1,i&&(a=s[0]&2?i.return:s[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,s[1])).done)return a;switch(i=0,a&&(s=[s[0]&2,a.value]),s[0]){case 0:case 1:a=s;break;case 4:return n.label++,{value:s[1],done:!1};case 5:n.label++,i=s[1],s=[0];continue;case 7:s=n.ops.pop(),n.trys.pop();continue;default:if((a=n.trys,!(a=a.length>0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{var t=e&&e.__createBinding||(Object.create?(function(e,t,n,r){r===void 0&&(r=n);var i=Object.getOwnPropertyDescriptor(t,n);(!i||(`get`in i?!t.__esModule:i.writable||i.configurable))&&(i={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,r,i)}):(function(e,t,n,r){r===void 0&&(r=n),e[r]=t[n]})),n=e&&e.__exportStar||function(e,n){for(var r in e)r!=="default"&&!Object.prototype.hasOwnProperty.call(n,r)&&t(n,e,r)};Object.defineProperty(e,"__esModule",{value:!0}),n(require_client(),e),n(require_interfaces(),e),n(require_models(),e),n(require_server(),e),n(require_server_and_client(),e)})),import_dist=require_dist(),JSONRPC=class{constructor(e,{onConnected:t,onClosed:n}){this.url=e,this.onConnected=t,this.onClosed=n,this.messageQueue=[],this.serverAndClient=null,this.connect(),this.connectionEstablished=!1,this.timerId=null,this.closed=!1}close(){this.timerId&&clearTimeout(this.timerId),this.webSocket.close(),this.closed=!0}on(e,t){this.serverAndClient.addMethod(e,t)}async requestInternal(e,t,n){let r=await this.serverAndClient.request(e,t);n&&n(r)}requestMessageQueue(){this.messageQueue.forEach(e=>{let[t,n,r]=e;this.requestInternal(t,n,r)}),this.messageQueue=[]}request(e,t,n){this.webSocket.readyState===WebSocket.OPEN?this.requestInternal(e,t,n):this.messageQueue.push([e,t,n])}notify(e,t){switch(this.webSocket.readyState){case WebSocket.OPEN:this.serverAndClient.notify(e,t);break;case WebSocket.CLOSED:break}}connect(e){this.closed||(console.log(`connect`,this.url),this.webSocket=new WebSocket(this.url),this.serverAndClient||=new import_dist.JSONRPCServerAndClient(new import_dist.JSONRPCServer,new import_dist.JSONRPCClient(e=>{try{return this.webSocket.send(JSON.stringify(e)),Promise.resolve()}catch(e){return Promise.reject(e)}})),this.webSocket.onmessage=e=>{this.serverAndClient.receiveAndSend(JSON.parse(e.data.toString()))},this.webSocket.onopen=()=>{console.log(`WebSocket connection established`),this.connectionEstablished=!0,this.onConnected&&this.onConnected(),this.requestMessageQueue()},this.webSocket.onclose=e=>{console.error(`WebScoket closed`,e),this.serverAndClient.rejectAllPendingRequests(`Connection is closed (${e.reason}).`),this.connectionEstablished&&this.onClosed(),this.timerId=setTimeout(()=>{this.connect()},3e3)},this.webSocket.onerror=e=>{console.error(`WebSocket error:`,e),this.webSocket.close()})}},keyevent_exports=__exportAll({convertKeyEvent:()=>convertKeyEvent}),modifierKeys=[`Shift`,`Control`,`Alt`,`Meta`,`CapsLock`],convertKeyTable={Enter:`Return`,ArrowRight:`Right`,ArrowLeft:`Left`,ArrowUp:`Up`,ArrowDown:`Down`,"¡":`1`,"™":`2`,"£":`3`,"¢":`4`,"∞":`5`,"§":`6`,"¶":`7`,"•":`8`,ª:`9`,º:`0`,"–":`-`,"≠":`=`,"“":`[`,"‘":`]`,"«":`\\`,"…":`;`,æ:`'`,"≤":`,`,"≥":`.`,"÷":`/`,"⁄":`!`,"€":`@`,"‹":`#`,"›":`$`,fi:`%`,fl:`^`,"‡":`&`,"°":`*`,"·":`(`,"‚":`)`,"—":`_`,"±":`+`,"”":`{`,"’":`}`,"»":`|`,Ú:`:`,Æ:`"`,"¯":`<`,"˘":`>`,"¿":`?`,œ:`q`,"∑":`w`,"´":`e`,"®":`r`,"†":`t`,"¥":`y`,"¨":`u`,ˆ:`i`,ø:`o`,π:`p`,å:`a`,ß:`s`,"∂":`d`,ƒ:`f`,"©":`g`,"˙":`h`,"∆":`j`,"˚":`k`,"¬":`l`,Ω:`z`,"≈":`x`,ç:`c`,"√":`v`,"∫":`b`,"˜":`n`,µ:`m`,Œ:`Q`,"„":`W`,"´":`E`,"‰":`R`,ˇ:`T`,Á:`Y`,"¨":`U`,ˆ:`I`,Ø:`O`,"∏":`P`,Å:`A`,Í:`S`,Î:`D`,Ï:`F`,"˝":`G`,Ó:`H`,Ô:`J`,"":`K`,Ò:`L`,"¸":`Z`,"˛":`X`,Ç:`C`,"◊":`V`,ı:`B`,"˜":`N`,Â:`M`};function getKey(e){return e.altKey?convertKeyTable[e.key]||(e.code.startsWith(`Key`)?e.code[3].toLowerCase():null)||e.key:convertKeyTable[e.key]||e.key}function convertKeyEvent(e){return modifierKeys.indexOf(e.key)===-1?{key:getKey(e),ctrl:e.ctrlKey,meta:e.altKey,super:e.metaKey,shift:e.shiftKey}:null}var lib_exports=__exportAll({computeWidth:()=>computeWidth,eawVersion:()=>version,getEAW:()=>getEAW}),defs=[[0,31,`N`],[32,126,`Na`],[127,160,`N`],[161,161,`A`],[162,163,`Na`],[164,164,`A`],[165,166,`Na`],[167,168,`A`],[169,169,`N`],[170,170,`A`],[171,171,`N`],[172,172,`Na`],[173,174,`A`],[175,175,`Na`],[176,180,`A`],[181,181,`N`],[182,186,`A`],[187,187,`N`],[188,191,`A`],[192,197,`N`],[198,198,`A`],[199,207,`N`],[208,208,`A`],[209,214,`N`],[215,216,`A`],[217,221,`N`],[222,225,`A`],[226,229,`N`],[230,230,`A`],[231,231,`N`],[232,234,`A`],[235,235,`N`],[236,237,`A`],[238,239,`N`],[240,240,`A`],[241,241,`N`],[242,243,`A`],[244,246,`N`],[247,250,`A`],[251,251,`N`],[252,252,`A`],[253,253,`N`],[254,254,`A`],[255,256,`N`],[257,257,`A`],[258,272,`N`],[273,273,`A`],[274,274,`N`],[275,275,`A`],[276,282,`N`],[283,283,`A`],[284,293,`N`],[294,295,`A`],[296,298,`N`],[299,299,`A`],[300,304,`N`],[305,307,`A`],[308,311,`N`],[312,312,`A`],[313,318,`N`],[319,322,`A`],[323,323,`N`],[324,324,`A`],[325,327,`N`],[328,331,`A`],[332,332,`N`],[333,333,`A`],[334,337,`N`],[338,339,`A`],[340,357,`N`],[358,359,`A`],[360,362,`N`],[363,363,`A`],[364,461,`N`],[462,462,`A`],[463,463,`N`],[464,464,`A`],[465,465,`N`],[466,466,`A`],[467,467,`N`],[468,468,`A`],[469,469,`N`],[470,470,`A`],[471,471,`N`],[472,472,`A`],[473,473,`N`],[474,474,`A`],[475,475,`N`],[476,476,`A`],[477,592,`N`],[593,593,`A`],[594,608,`N`],[609,609,`A`],[610,707,`N`],[708,708,`A`],[709,710,`N`],[711,711,`A`],[712,712,`N`],[713,715,`A`],[716,716,`N`],[717,717,`A`],[718,719,`N`],[720,720,`A`],[721,727,`N`],[728,731,`A`],[732,732,`N`],[733,733,`A`],[734,734,`N`],[735,735,`A`],[736,767,`N`],[768,879,`A`],[880,912,`N`],[913,929,`A`],[930,930,`N`],[931,937,`A`],[938,944,`N`],[945,961,`A`],[962,962,`N`],[963,969,`A`],[970,1024,`N`],[1025,1025,`A`],[1026,1039,`N`],[1040,1103,`A`],[1104,1104,`N`],[1105,1105,`A`],[1106,4351,`N`],[4352,4447,`W`],[4448,8207,`N`],[8208,8208,`A`],[8209,8210,`N`],[8211,8214,`A`],[8215,8215,`N`],[8216,8217,`A`],[8218,8219,`N`],[8220,8221,`A`],[8222,8223,`N`],[8224,8226,`A`],[8227,8227,`N`],[8228,8231,`A`],[8232,8239,`N`],[8240,8240,`A`],[8241,8241,`N`],[8242,8243,`A`],[8244,8244,`N`],[8245,8245,`A`],[8246,8250,`N`],[8251,8251,`A`],[8252,8253,`N`],[8254,8254,`A`],[8255,8307,`N`],[8308,8308,`A`],[8309,8318,`N`],[8319,8319,`A`],[8320,8320,`N`],[8321,8324,`A`],[8325,8360,`N`],[8361,8361,`H`],[8362,8363,`N`],[8364,8364,`A`],[8365,8450,`N`],[8451,8451,`A`],[8452,8452,`N`],[8453,8453,`A`],[8454,8456,`N`],[8457,8457,`A`],[8458,8466,`N`],[8467,8467,`A`],[8468,8469,`N`],[8470,8470,`A`],[8471,8480,`N`],[8481,8482,`A`],[8483,8485,`N`],[8486,8486,`A`],[8487,8490,`N`],[8491,8491,`A`],[8492,8530,`N`],[8531,8532,`A`],[8533,8538,`N`],[8539,8542,`A`],[8543,8543,`N`],[8544,8555,`A`],[8556,8559,`N`],[8560,8569,`A`],[8570,8584,`N`],[8585,8585,`A`],[8586,8591,`N`],[8592,8601,`A`],[8602,8631,`N`],[8632,8633,`A`],[8634,8657,`N`],[8658,8658,`A`],[8659,8659,`N`],[8660,8660,`A`],[8661,8678,`N`],[8679,8679,`A`],[8680,8703,`N`],[8704,8704,`A`],[8705,8705,`N`],[8706,8707,`A`],[8708,8710,`N`],[8711,8712,`A`],[8713,8714,`N`],[8715,8715,`A`],[8716,8718,`N`],[8719,8719,`A`],[8720,8720,`N`],[8721,8721,`A`],[8722,8724,`N`],[8725,8725,`A`],[8726,8729,`N`],[8730,8730,`A`],[8731,8732,`N`],[8733,8736,`A`],[8737,8738,`N`],[8739,8739,`A`],[8740,8740,`N`],[8741,8741,`A`],[8742,8742,`N`],[8743,8748,`A`],[8749,8749,`N`],[8750,8750,`A`],[8751,8755,`N`],[8756,8759,`A`],[8760,8763,`N`],[8764,8765,`A`],[8766,8775,`N`],[8776,8776,`A`],[8777,8779,`N`],[8780,8780,`A`],[8781,8785,`N`],[8786,8786,`A`],[8787,8799,`N`],[8800,8801,`A`],[8802,8803,`N`],[8804,8807,`A`],[8808,8809,`N`],[8810,8811,`A`],[8812,8813,`N`],[8814,8815,`A`],[8816,8833,`N`],[8834,8835,`A`],[8836,8837,`N`],[8838,8839,`A`],[8840,8852,`N`],[8853,8853,`A`],[8854,8856,`N`],[8857,8857,`A`],[8858,8868,`N`],[8869,8869,`A`],[8870,8894,`N`],[8895,8895,`A`],[8896,8977,`N`],[8978,8978,`A`],[8979,8985,`N`],[8986,8987,`W`],[8988,9e3,`N`],[9001,9002,`W`],[9003,9192,`N`],[9193,9196,`W`],[9197,9199,`N`],[9200,9200,`W`],[9201,9202,`N`],[9203,9203,`W`],[9204,9311,`N`],[9312,9449,`A`],[9450,9450,`N`],[9451,9547,`A`],[9548,9551,`N`],[9552,9587,`A`],[9588,9599,`N`],[9600,9615,`A`],[9616,9617,`N`],[9618,9621,`A`],[9622,9631,`N`],[9632,9633,`A`],[9634,9634,`N`],[9635,9641,`A`],[9642,9649,`N`],[9650,9651,`A`],[9652,9653,`N`],[9654,9655,`A`],[9656,9659,`N`],[9660,9661,`A`],[9662,9663,`N`],[9664,9665,`A`],[9666,9669,`N`],[9670,9672,`A`],[9673,9674,`N`],[9675,9675,`A`],[9676,9677,`N`],[9678,9681,`A`],[9682,9697,`N`],[9698,9701,`A`],[9702,9710,`N`],[9711,9711,`A`],[9712,9724,`N`],[9725,9726,`W`],[9727,9732,`N`],[9733,9734,`A`],[9735,9736,`N`],[9737,9737,`A`],[9738,9741,`N`],[9742,9743,`A`],[9744,9747,`N`],[9748,9749,`W`],[9750,9755,`N`],[9756,9756,`A`],[9757,9757,`N`],[9758,9758,`A`],[9759,9791,`N`],[9792,9792,`A`],[9793,9793,`N`],[9794,9794,`A`],[9795,9799,`N`],[9800,9811,`W`],[9812,9823,`N`],[9824,9825,`A`],[9826,9826,`N`],[9827,9829,`A`],[9830,9830,`N`],[9831,9834,`A`],[9835,9835,`N`],[9836,9837,`A`],[9838,9838,`N`],[9839,9839,`A`],[9840,9854,`N`],[9855,9855,`W`],[9856,9874,`N`],[9875,9875,`W`],[9876,9885,`N`],[9886,9887,`A`],[9888,9888,`N`],[9889,9889,`W`],[9890,9897,`N`],[9898,9899,`W`],[9900,9916,`N`],[9917,9918,`W`],[9919,9919,`A`],[9920,9923,`N`],[9924,9925,`W`],[9926,9933,`A`],[9934,9934,`W`],[9935,9939,`A`],[9940,9940,`W`],[9941,9953,`A`],[9954,9954,`N`],[9955,9955,`A`],[9956,9959,`N`],[9960,9961,`A`],[9962,9962,`W`],[9963,9969,`A`],[9970,9971,`W`],[9972,9972,`A`],[9973,9973,`W`],[9974,9977,`A`],[9978,9978,`W`],[9979,9980,`A`],[9981,9981,`W`],[9982,9983,`A`],[9984,9988,`N`],[9989,9989,`W`],[9990,9993,`N`],[9994,9995,`W`],[9996,10023,`N`],[10024,10024,`W`],[10025,10044,`N`],[10045,10045,`A`],[10046,10059,`N`],[10060,10060,`W`],[10061,10061,`N`],[10062,10062,`W`],[10063,10066,`N`],[10067,10069,`W`],[10070,10070,`N`],[10071,10071,`W`],[10072,10101,`N`],[10102,10111,`A`],[10112,10132,`N`],[10133,10135,`W`],[10136,10159,`N`],[10160,10160,`W`],[10161,10174,`N`],[10175,10175,`W`],[10176,10213,`N`],[10214,10221,`Na`],[10222,10628,`N`],[10629,10630,`Na`],[10631,11034,`N`],[11035,11036,`W`],[11037,11087,`N`],[11088,11088,`W`],[11089,11092,`N`],[11093,11093,`W`],[11094,11097,`A`],[11098,11903,`N`],[11904,11929,`W`],[11930,11930,`N`],[11931,12019,`W`],[12020,12031,`N`],[12032,12245,`W`],[12246,12271,`N`],[12272,12287,`W`],[12288,12288,`F`],[12289,12350,`W`],[12351,12352,`N`],[12353,12438,`W`],[12439,12440,`N`],[12441,12543,`W`],[12544,12548,`N`],[12549,12591,`W`],[12592,12592,`N`],[12593,12686,`W`],[12687,12687,`N`],[12688,12771,`W`],[12772,12782,`N`],[12783,12830,`W`],[12831,12831,`N`],[12832,12871,`W`],[12872,12879,`A`],[12880,19903,`W`],[19904,19967,`N`],[19968,42124,`W`],[42125,42127,`N`],[42128,42182,`W`],[42183,43359,`N`],[43360,43388,`W`],[43389,44031,`N`],[44032,55203,`W`],[55204,57343,`N`],[57344,63743,`A`],[63744,64255,`W`],[64256,65023,`N`],[65024,65039,`A`],[65040,65049,`W`],[65050,65071,`N`],[65072,65106,`W`],[65107,65107,`N`],[65108,65126,`W`],[65127,65127,`N`],[65128,65131,`W`],[65132,65280,`N`],[65281,65376,`F`],[65377,65470,`H`],[65471,65473,`N`],[65474,65479,`H`],[65480,65481,`N`],[65482,65487,`H`],[65488,65489,`N`],[65490,65495,`H`],[65496,65497,`N`],[65498,65500,`H`],[65501,65503,`N`],[65504,65510,`F`],[65511,65511,`N`],[65512,65518,`H`],[65519,65532,`N`],[65533,65533,`A`],[65534,94175,`N`],[94176,94180,`W`],[94181,94191,`N`],[94192,94193,`W`],[94194,94207,`N`],[94208,100343,`W`],[100344,100351,`N`],[100352,101589,`W`],[101590,101631,`N`],[101632,101640,`W`],[101641,110575,`N`],[110576,110579,`W`],[110580,110580,`N`],[110581,110587,`W`],[110588,110588,`N`],[110589,110590,`W`],[110591,110591,`N`],[110592,110882,`W`],[110883,110897,`N`],[110898,110898,`W`],[110899,110927,`N`],[110928,110930,`W`],[110931,110932,`N`],[110933,110933,`W`],[110934,110947,`N`],[110948,110951,`W`],[110952,110959,`N`],[110960,111355,`W`],[111356,126979,`N`],[126980,126980,`W`],[126981,127182,`N`],[127183,127183,`W`],[127184,127231,`N`],[127232,127242,`A`],[127243,127247,`N`],[127248,127277,`A`],[127278,127279,`N`],[127280,127337,`A`],[127338,127343,`N`],[127344,127373,`A`],[127374,127374,`W`],[127375,127376,`A`],[127377,127386,`W`],[127387,127404,`A`],[127405,127487,`N`],[127488,127490,`W`],[127491,127503,`N`],[127504,127547,`W`],[127548,127551,`N`],[127552,127560,`W`],[127561,127567,`N`],[127568,127569,`W`],[127570,127583,`N`],[127584,127589,`W`],[127590,127743,`N`],[127744,127776,`W`],[127777,127788,`N`],[127789,127797,`W`],[127798,127798,`N`],[127799,127868,`W`],[127869,127869,`N`],[127870,127891,`W`],[127892,127903,`N`],[127904,127946,`W`],[127947,127950,`N`],[127951,127955,`W`],[127956,127967,`N`],[127968,127984,`W`],[127985,127987,`N`],[127988,127988,`W`],[127989,127991,`N`],[127992,128062,`W`],[128063,128063,`N`],[128064,128064,`W`],[128065,128065,`N`],[128066,128252,`W`],[128253,128254,`N`],[128255,128317,`W`],[128318,128330,`N`],[128331,128334,`W`],[128335,128335,`N`],[128336,128359,`W`],[128360,128377,`N`],[128378,128378,`W`],[128379,128404,`N`],[128405,128406,`W`],[128407,128419,`N`],[128420,128420,`W`],[128421,128506,`N`],[128507,128591,`W`],[128592,128639,`N`],[128640,128709,`W`],[128710,128715,`N`],[128716,128716,`W`],[128717,128719,`N`],[128720,128722,`W`],[128723,128724,`N`],[128725,128727,`W`],[128728,128731,`N`],[128732,128735,`W`],[128736,128746,`N`],[128747,128748,`W`],[128749,128755,`N`],[128756,128764,`W`],[128765,128991,`N`],[128992,129003,`W`],[129004,129007,`N`],[129008,129008,`W`],[129009,129291,`N`],[129292,129338,`W`],[129339,129339,`N`],[129340,129349,`W`],[129350,129350,`N`],[129351,129535,`W`],[129536,129647,`N`],[129648,129660,`W`],[129661,129663,`N`],[129664,129672,`W`],[129673,129679,`N`],[129680,129725,`W`],[129726,129726,`N`],[129727,129733,`W`],[129734,129741,`N`],[129742,129755,`W`],[129756,129759,`N`],[129760,129768,`W`],[129769,129775,`N`],[129776,129784,`W`],[129785,131071,`N`],[131072,196605,`W`],[196606,196607,`N`],[196608,262141,`W`],[262142,917759,`N`],[917760,917999,`A`],[918e3,983039,`N`],[983040,1048573,`A`],[1048574,1048575,`N`],[1048576,1114109,`A`],[1114110,1114111,`N`]],version=`15.1.0`;function getEAWOfCodePoint(e){let t=0,n=defs.length-1;for(;t!==n;){let r=t+(n-t>>1),[i,a,o]=defs[r];if(ea)t=r+1;else return o}return defs[t][2]}function getEAW(e,t=0){let n=e.codePointAt(t);if(n!==void 0)return getEAWOfCodePoint(n)}var defaultWidths={N:1,Na:1,W:2,F:2,H:1,A:1};function computeWidth(e,t){let n=0;for(let r of e){let e=getEAW(r);n+=t&&t[e]||defaultWidths[e]}return n}var textOffsetY=5,isSafari=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);function isWideChar(e){switch(getEAW(e)){case`F`:case`W`:return!0;default:return!1}}function isMacOS(){return window.navigator.userAgent.indexOf(`Mac OS X`)!==-1}function computeFontSize(e){let t=document.createElement(`canvas`).getContext(`2d`);t.font=e;let n=t.measureText(`W`);return[Math.floor(n.width),Math.round(n.fontBoundingBoxAscent+textOffsetY+(n.emHeightDescent||0)),Math.round(n.fontBoundingBoxAscent+textOffsetY)]}function drawBlock({ctx:e,x:t,y:n,width:r,height:i,style:a}){e.fillStyle=a,e.fillRect(t,n,r,i)}function drawText({ctx:e,x:t,y:n,text:r,font:i,style:a,option:o}){n+=Math.round(textOffsetY),e.fillStyle=a,e.font=i,e.textBaseline=`top`;for(let i of r)isWideChar(i)?(e.fillText(i,t,n,o.fontWidth*2),t+=o.fontWidth*2):(e.fillText(i,t,n,o.fontWidth),t+=o.fontWidth)}function drawHorizontalLine({ctx:e,x:t,y:n,width:r,style:i,lineWidth:a=1}){e.strokeStyle=i,e.lineWidth=a,e.setLineDash=[],e.beginPath(),e.moveTo(t,n),e.lineTo(t+r,n),e.stroke()}var Option=class{constructor({fontName:e,fontSize:t}){this.setFont(e,t),this.foreground=`#cccccc`,this.background=`#2d2d2d`}setFont(e,t){let n=t+`px `+e,[r,i,a]=computeFontSize(n);this.fontName=e,this.fontSize=t,this.fontWidth=r,this.fontHeight=i,this.fontAscent=a,this.font=n}};function getLemEditorElement(){return document.getElementById(`lem-editor`)}function normalizeWheelDelta(e,t,n,r){switch(n){case 0:return{dx:e/r,dy:t/r};case 2:return{dx:e*20,dy:t*20};default:return{dx:e,dy:t}}}function extractWholeLines(e,t){let n=Math.trunc(e),r=Math.trunc(t);return{scrollX:n,scrollY:r,remainderX:e-n,remainderY:t-r}}function cursorPosition(e,t){let[n,r]=t.getDisplayRectangle(),i=e.clientX-n,a=e.clientY-r;return{pixelX:i,pixelY:a,x:Math.floor(i/t.option.fontWidth),y:Math.floor(a/t.option.fontHeight)}}function makeWheelHandler(e){let t={x:0,y:0},n=!1,r={pixelX:0,pixelY:0,x:0,y:0};return i=>{i.preventDefault(),r=cursorPosition(i,e);let{dx:a,dy:o}=normalizeWheelDelta(i.deltaX,i.deltaY,i.deltaMode,e.option.fontHeight);t={x:t.x+a,y:t.y+o},n||(n=!0,requestAnimationFrame(()=>{n=!1;let{scrollX:i,scrollY:a,remainderX:o,remainderY:s}=extractWholeLines(t.x,t.y);t={x:o,y:s},(i!==0||a!==0)&&e.jsonrpc.notify(`input`,{kind:`wheel`,value:{...r,wheelX:-i,wheelY:-a}})}))}}function addMouseEventListeners({dom:e,editor:t,isDraggable:n,draggableStyle:r}){e.addEventListener(`contextmenu`,e=>{e.preventDefault()});let i=(e,n)=>{e.preventDefault();let[r,i]=t.getDisplayRectangle(),a=e.clientX-r,o=e.clientY-i,s=Math.floor(a/t.option.fontWidth),c=Math.floor(o/t.option.fontHeight);t.jsonrpc.notify(`input`,{kind:n,value:{x:s,y:c,pixelX:a,pixelY:o,button:e.button,clicks:e.detail}})};e.addEventListener(`mousedown`,e=>{n&&(document.body.style.cursor=r),t.focusHiddenInput(),i(e,`mousedown`)}),e.addEventListener(`mouseup`,e=>{n&&(document.body.style.cursor=`default`),i(e,`mouseup`)});let a=0;e.addEventListener(`mousemove`,e=>{e.preventDefault();let n=Date.now();if(n-a>50){a=n;let[r,i]=t.getDisplayRectangle(),o=e.clientX-r,s=e.clientY-i,c=Math.floor(o/t.option.fontWidth),l=Math.floor(s/t.option.fontHeight);t.jsonrpc.notify(`input`,{kind:`mousemove`,value:{x:c,y:l,pixelX:o,pixelY:s,button:e.buttons===0?null:e.buttons-1}})}}),n&&(e.addEventListener(`mouseover`,()=>{document.body.style.cursor=r}),e.addEventListener(`mouseout`,e=>{e.buttons!==1&&(document.body.style.cursor=`default`)})),e.addEventListener(`wheel`,makeWheelHandler(t))}var zIndexTable={"floating-window":200,modeline:100,"vertical-border":100,"horizontal-border":101};function zindex(e){return zIndexTable[e]||0}var borderOffsetX=5,borderOffsetY=10,BaseSurface=class{constructor({editor:e}){this.editor=e,this.mainDOM=null,this.wrapper=null}delete(){this.wrapper?getLemEditorElement().removeChild(this.wrapper):getLemEditorElement().removeChild(this.mainDOM)}setupDOM({dom:e,isFloating:t,border:n,cssClassName:r}){this.mainDOM=e,t&&n?(this.wrapper=document.createElement(`div`),r&&(this.wrapper.className=r),this.wrapper.style.position=`absolute`,this.wrapper.style.backgroundColor=this.editor.option.background,this.wrapper.style.zIndex=zindex(`floating-window`),this.wrapper.appendChild(e),getLemEditorElement().appendChild(this.wrapper)):(r&&(e.className=r),getLemEditorElement().appendChild(e))}move(e,t){let[n,r]=this.editor.getDisplayRectangle(),i=Math.floor(n+e),a=Math.floor(r+t);this.wrapper?(this.wrapper.style.left=i-borderOffsetX+`px`,this.wrapper.style.top=a-borderOffsetY+`px`,this.mainDOM.style.left=borderOffsetX+`px`,this.mainDOM.style.top=borderOffsetY+`px`):(this.mainDOM.style.left=i+`px`,this.mainDOM.style.top=a+`px`)}_resize(e,t){let n=window.devicePixelRatio||1;this.mainDOM.width=e*n,this.mainDOM.height=t*n,this.mainDOM.style.width=e+`px`,this.mainDOM.style.height=t+`px`,this.wrapper&&(this.wrapper.style.width=e+borderOffsetX*2+`px`,this.wrapper.style.height=t+borderOffsetY*2+`px`)}drawBlock(e,t,n,r,i){}drawText(e,t,n,r,i,a,o){}drawImage(e,t,n,r,i){}clearImages(e,t){}clearAllImages(){}touch(){}evalIn(code){return eval(code)}},CanvasSurface=class extends BaseSurface{constructor({editor:e,view:t,pixelX:n,pixelY:r,pixelWidth:i,pixelHeight:a,styles:o,isFloating:s,border:c,cssClassName:l}){super({editor:e});let u=this.setupCanvas(o);this.setupDOM({dom:u,isFloating:s,border:c,cssClassName:l}),this.move(n,r),this.resize(i,a),this.drawingQueue=[],addMouseEventListeners({dom:u,editor:e})}setupCanvas(e){let t=document.createElement(`canvas`);if(t.style.position=`absolute`,e)for(let n in e)t.style[n]=e[n];return t}resize(e,t){this._resize(e,t);let n=window.devicePixelRatio||1;this.mainDOM.getContext(`2d`).scale(n,n)}move(e,t){if(super.move(e,t),this.imageEls)for(let[,e]of this.imageEls)this.positionImage(e)}delete(){this.clearAllImages(),super.delete()}drawBlock(e,t,n,r,i){this.drawingQueue.push(function(a){drawBlock({ctx:a,x:e,y:t,width:n,height:r,style:i})})}drawText(e,t,n,r,i,a,o){let s=this.editor.option,c=o||s.fontHeight;this.drawingQueue.push(function(o){if(a=a?`${s.fontSize}px ${a}`:s.font,!i)drawBlock({ctx:o,x:e,y:t,width:r,height:c,style:s.background}),drawText({ctx:o,x:e,y:t,text:n,style:s.foreground,font:a,option:s});else{let{foreground:l,background:u,bold:d,reverse:f,underline:p,cursor:m}=i;if(l||=s.foreground,u||=s.background,f){let e=u;u=l,l=e}m&&(u=s.background),drawBlock({ctx:o,x:e,y:t,width:r,height:c,style:u}),drawText({ctx:o,x:e,y:t,text:n,style:l,font:d?`bold `+a:a,option:s}),p&&drawHorizontalLine({ctx:o,x:e,y:t+s.fontHeight-2,width:r,style:typeof p==`string`?p:l,lineWidth:2})}})}imageBaseLeft(){return parseFloat(this.mainDOM.style.left)||0}imageBaseTop(){return parseFloat(this.mainDOM.style.top)||0}drawImage(e,t,n,r,i){this.imageEls||=new Map;let a=e+`,`+t,o=this.imageEls.get(a);if(o&&o.url!==i&&(o.el.remove(),this.imageEls.delete(a),o=null),!o){let e=document.createElement(`img`);e.style.position=`absolute`,e.style.pointerEvents=`none`,e.style.zIndex=`1`,e.src=i,this.mainDOM.parentNode.appendChild(e),o={el:e,url:i},this.imageEls.set(a,o)}o.x=e,o.y=t,o.width=n,o.height=r,this.positionImage(o)}positionImage(e){e.el.style.left=this.imageBaseLeft()+e.x+`px`,e.el.style.top=this.imageBaseTop()+e.y+`px`,e.el.style.width=e.width+`px`,e.el.style.height=e.height+`px`}clearImages(e,t){if(this.imageEls)for(let[n,r]of this.imageEls){let i=r.y+(r.height||0);r.ye&&(r.el.remove(),this.imageEls.delete(n))}}clearAllImages(){if(this.imageEls){for(let[,e]of this.imageEls)e.el.remove();this.imageEls.clear()}}touch(){let e=this.mainDOM.getContext(`2d`);for(let t of this.drawingQueue)t(e);this.drawingQueue=[]}activate(){this.mainDOM.dataset.store=`active`}deactivate(){this.mainDOM.dataset.store=`inactive`}},HTMLSurface=class extends BaseSurface{constructor({editor:e,pixelX:t,pixelY:n,pixelWidth:r,pixelHeight:i,styles:a,option:o,isFloating:s,border:c,html:l}){super({editor:e});let u=document.createElement(`iframe`);this.setupDOM({dom:u,isFloating:s,border:c}),u.style.position=`absolute`,u.style.backgroundColor=o.background,u.setAttribute(`sandbox`,`allow-scripts allow-same-origin`),u.srcdoc=l,u.addEventListener(`load`,()=>{let e=u.contentWindow;e.invokeLem=(e,t)=>parent.postMessage({type:`invoke-lem`,method:e,args:t})}),this.iframe=u,this.move(t,n),this.resize(r,i)}resize(e,t){this._resize(e,t)}update(e){let t=this.iframe.contentWindow.scrollY;this.iframe.srcdoc=e,this.iframe.onload=()=>{this.iframe.onload=null,this.iframe.contentWindow.scrollTo(0,t)}}evalIn(e){return this.iframe.contentWindow.eval(e)}},VerticalBorder=class{constructor({x:e,y:t,height:n,option:r,editor:i}){this.option=r,this.editor=i,this.line=document.createElement(`div`),this.line.className=`lem-editor__vertical-border`,this.line.style.height=n+`px`,this.line.style.position=`absolute`,this.line.style.zIndex=zindex(`vertical-border`),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:i,isDraggable:!0,draggableStyle:`col-resize`})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.line.style.left=Math.floor(n+e-this.option.fontWidth/2)+`px`,this.line.style.top=r+t+`px`}resize(e){this.line.style.height=e+`px`}},HorizontalBorder=class{constructor({x:e,y:t,width:n,option:r,editor:i}){this.option=r,this.editor=i,this.line=document.createElement(`div`),this.line.className=`lem-editor__horizontal-border`,this.line.style.width=n+`px`,this.line.style.position=`absolute`,this.line.style.zIndex=zindex(`horizontal-border`),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:i,isDraggable:!0,draggableStyle:`row-resize`})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.line.style.left=n+e+`px`,this.line.style.top=Math.floor(r+t-4)+`px`}resize(e){this.line.style.width=e+`px`}},viewStyles={header:()=>{},tile:()=>{},floating:e=>({boxSizing:`border-box`,borderColor:e.foreground,backgroundColor:e.background})};function getViewStyle(e,t){return viewStyles[e](t)||{}}var View=class{constructor({id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,useModeline:l,kind:u,type:d,content:f,border:p,borderShape:m,option:h,editor:g}){switch(this.option=h,this.id=e,this.x=t,this.y=n,this.width=r,this.height=i,this.pixelX=a,this.pixelY=o,this.pixelWidth=s,this.pixelHeight=c,this.useModeline=l,this.kind=u,this.type=d,this.border=p,this.borderShape=m,this.editor=g,this.bottomBar=null,this.leftsideBar=null,u){case`tile`:this.mainSurface=this.makeSurface(d,f),this.leftSideBar=new VerticalBorder({x:a,y:o,height:c+(l?h.fontHeight:0),option:h,editor:g}),l||(this.bottomBar=new HorizontalBorder({x:a,y:o+c-h.fontHeight,width:s,option:h,editor:g}));break;case`header`:this.mainSurface=this.makeSurface(d,f);break;case`floating`:this.mainSurface=this.makeSurface(d,f),m===`left-border`&&(this.leftSideBar=new VerticalBorder({x:a,y:o,height:c,option:h,editor:g}));break}this.modelineSurface=l?this.makeModelineSurface():null}delete(){this.mainSurface.delete(),this.modelineSurface&&this.modelineSurface.delete(),this.leftSideBar&&this.leftSideBar.delete(),this.bottomBar&&this.bottomBar.delete()}move(e,t,n,r){this.x=e,this.y=t,this.pixelX=n,this.pixelY=r,this.mainSurface.move(n,r),this.modelineSurface&&this.modelineSurface.move(n,r+this.pixelHeight),this.leftSideBar&&this.leftSideBar.move(n,r),this.bottomBar&&this.bottomBar.move(n,r+this.pixelHeight)}resize(e,t,n,r){this.width=e,this.height=t,this.pixelWidth=n,this.pixelHeight=r,this.mainSurface.resize(n,r),this.modelineSurface&&(this.modelineSurface.move(this.pixelX,this.pixelY+r),this.modelineSurface.resize(n,this.option.fontHeight)),this.leftSideBar&&this.leftSideBar.resize(r+(this.modelineSurface?this.option.fontHeight:0)),this.bottomBar&&this.bottomBar.resize(n)}clear(){this.mainSurface.drawBlock(0,0,this.pixelWidth,this.pixelHeight,this.option.background),this.mainSurface.clearImages(0,this.pixelHeight)}clearEol(e,t,n){n??=this.option.fontHeight,this.mainSurface.drawBlock(e,t,this.pixelWidth-e,n,this.option.background),this.mainSurface.clearImages(t,t+n)}clearEob(e,t){this.mainSurface.drawBlock(e,t,this.pixelWidth,this.pixelHeight-t,this.option.background),this.mainSurface.clearImages(t,this.pixelHeight)}print(e,t,n,r,i,a,o){this.mainSurface.drawText(e,t,n,r,i,a,o)}printImage(e,t,n,r,i){this.mainSurface.drawImage(e,t,n,r,i)}printToModeline(e,t,n,r,i,a){this.modelineSurface&&this.modelineSurface.drawText(e,t,n,r,i,null,a)}touch(e){this.mainSurface.touch(),this.modelineSurface&&(this.modelineSurface.touch(),e?this.modelineSurface.activate():this.modelineSurface.deactivate())}makeSurface(e,t){switch(e){case`html`:return this.makeHTMLSurface(t);case`editor`:return this.makeEditorSurface();default:console.error(`unknown type: ${e}`)}}makeHTMLSurface(e){return new HTMLSurface({editor:this.editor,pixelX:this.pixelX,pixelY:this.pixelY,pixelWidth:this.pixelWidth,pixelHeight:this.pixelHeight,styles:getViewStyle(this.kind,this.option),option:this.option,isFloating:this.kind===`floating`,border:this.border,html:e})}makeEditorSurface(){let e=this.borderShape===`left-border`?0:this.border,t=this.kind===`floating`;return new CanvasSurface({option:this.editor.option,pixelX:this.pixelX,pixelY:this.pixelY,pixelWidth:this.pixelWidth,pixelHeight:this.pixelHeight,styles:getViewStyle(this.kind,this.option),editor:this.editor,border:e,isFloating:t,view:this,cssClassName:t&&e?`lem-editor__floating-window--bordered`:null})}makeModelineSurface(){let e=new CanvasSurface({option:this.editor.option,pixelX:this.pixelX,pixelY:this.pixelY+this.pixelHeight,pixelWidth:this.pixelWidth,pixelHeight:this.option.fontHeight,editor:this.editor,view:this,styles:{zIndex:zindex(`modeline`)},cssClassName:`lem-editor__mode-line`});return addMouseEventListeners({dom:e.mainDOM,editor:this.editor,isDraggable:!0,draggableStyle:`row-resize`}),e}changeToHTMLContent(e){this.mainSurface.constructor.name===`HTMLSurface`?this.mainSurface.update(e):(this.mainSurface.delete(),this.mainSurface=this.makeHTMLSurface(e))}changeToEditorContent(){this.mainSurface.delete(),this.mainSurface=this.makeEditorSurface()}evalIn(e){return this.mainSurface.evalIn(e)}};function isPasteKeyEvent(e){return isMacOS()?e.metaKey&&e.key===`v`:e.ctrlKey&&e.shiftKey&&e.key===`V`}var Input=class{constructor(e){let t=e.option;this.editor=e,this.composition=!1,this.ignoreKeydownAfterCompositionend=!1,this.span=document.createElement(`span`),this.span.style.color=t.foreground,this.span.style.backgroundColor=t.background,this.span.style.position=`absolute`,this.span.style.zIndex=1e6,this.span.style.top=`0`,this.span.style.left=`0`,this.span.style.font=t.font,this.input=document.createElement(`input`),this.input.style.backgroundColor=`transparent`,this.input.style.color=`transparent`,this.input.style.width=`0`,this.input.style.padding=`0`,this.input.style.margin=`0`,this.input.style.border=`none`,this.input.style.position=`absolute`,this.input.style.zIndex=`-10`,this.input.style.top=`0`,this.input.style.left=`0`,this.input.style.font=t.font,this.input.addEventListener(`blur`,e=>{this.input.focus()}),this.input.addEventListener(`input`,e=>{this.composition===!1&&(this.input.value=``,this.span.innerHTML=``,this.input.style.width=`0`,isMacOS()||this.editor.emitInputString(e.data))}),this.input.addEventListener(`paste`,async e=>{e.preventDefault();let t=e.clipboardData||window.Clipboard.data,n=t?.getData(`text`)??t?.getData(`text/plain`);if(n&&n.length>0){this.editor.emitInputString(n);return}try{if(navigator.clipboard?.readText){let e=await navigator.clipboard.readText();if(e&&e.length>0){this.editor.emitInputString(e);return}}}catch(e){console.warn(`clipboard.readText() failed:`,e)}alert(`Paste failed (permission/environment restriction`)}),this.input.addEventListener(`keydown`,e=>{if(!isPasteKeyEvent(e)&&!(e.isComposing||this.composition)&&e.key!==`Process`){if(this.ignoreKeydownAfterCompositionend&&(isSafari||isMacOS())){e.preventDefault(),this.ignoreKeydownAfterCompositionend=!1;return}if(!(!isMacOS()&&!e.ctrlKey&&!e.altKey&&e.key.length===1)&&(e.preventDefault(),e.isComposing!==!0&&e.code!==``))return setTimeout(()=>{this.composition||(this.editor.emitInput(e),this.input.value=``)},0),!1}}),this.input.addEventListener(`compositionstart`,e=>{this.composition=!0,this.span.innerHTML=this.input.value,this.input.style.width=this.span.offsetWidth+`px`}),this.input.addEventListener(`compositionupdate`,e=>{this.span.innerHTML=e.data,this.input.style.width=this.span.offsetWidth+`px`}),this.input.addEventListener(`compositionend`,e=>{this.composition=!1,this.editor.emitInputString(this.input.value),this.input.value=``,this.span.innerHTML=this.input.value,this.input.style.width=`0`,this.ignoreKeydownAfterCompositionend=!0}),document.body.appendChild(this.input),document.body.appendChild(this.span),this.input.focus()}finalize(){document.body.removeChild(this.input),document.body.removeChild(this.span)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.span.style.top=r+t+`px`,this.span.style.left=n+e+`px`,this.input.style.top=this.span.offsetTop+`px`,this.input.style.left=this.span.offsetLeft+`px`}updateForeground(e){this.span.style.color=e}updateBackground(e){this.span.style.backgroundColor=e}},MessageTable=class{constructor(){this.map=new Map}register(e,t){for(let n in t){let r=t[n];this.map.set(n,r),e.on(n,r)}}get(e){return this.map.get(e)}};function getDisplayRectangleDefault(){return[0,0,window.innerWidth,window.innerHeight]}var Editor=class{constructor({getDisplayRectangle:e=getDisplayRectangleDefault,fontName:t,fontSize:n,url:r,onExit:i,onClosed:a}){this.getDisplayRectangle=e,this.option=new Option({fontName:t,fontSize:n}),this.onExit=i,this.input=new Input(this),this.cursors=new Map,this.cursorOverlay=document.createElement(`div`),this.cursorOverlay.className=`lem-cursor`,this.cursorOverlay.style.width=this.option.fontWidth+`px`,this.cursorOverlay.style.height=this.option.fontHeight+`px`,this.cursorOverlay.style.backgroundColor=`#ffffff`,this.cursorType=`box`,this.viewMap=new Map,this.jsonrpc=new JSONRPC(r,{onClosed:()=>{a()}}),this.messageTable=new MessageTable,this.messageTable.register(this.jsonrpc,{"update-foreground":this.updateForeground.bind(this),"update-background":this.updateBackground.bind(this),"make-view":this.makeView.bind(this),"delete-view":this.deleteView.bind(this),"resize-view":this.resize.bind(this),"move-view":this.move.bind(this),"redraw-view-after":this.redrawViewAfter.bind(this),clear:this.clear.bind(this),"clear-eol":this.clearEol.bind(this),"clear-eob":this.clearEob.bind(this),put:this.put.bind(this),"put-image":this.putImage.bind(this),"modeline-put":this.modelinePut.bind(this),"update-display":this.updateDisplay.bind(this),"move-cursor":this.moveCursor.bind(this),"change-view":this.changeView.bind(this),"resize-display":this.resizeDisplay.bind(this),bulk:this.bulk.bind(this),exit:this.exitEditor.bind(this),"get-clipboard-text":this.getClipboardText.bind(this),"set-clipboard-text":this.setClipboardText.bind(this),"js-eval":this.jsEval.bind(this),"set-font":this.setFont.bind(this),"get-font":this.getFont.bind(this),"get-display-size":this.getDisplaySize.bind(this),"load-css":this.loadCSS.bind(this),"update-cursor-shape":this.updateCursorShape.bind(this)}),this.login(),this.boundedHandleResize=this.handleResize.bind(this),this.focusHiddenInput=this.focusHiddenInput.bind(this)}init(){window.addEventListener(`resize`,this.boundedHandleResize),document.getElementsByTagName(`html`)[0].style[`background-color`]=`#333`,getLemEditorElement().appendChild(this.cursorOverlay)}finalize(){window.removeEventListener(`resize`,this.boundedHandleResize),this.input.finalize(),this.cursorOverlay.parentNode&&this.cursorOverlay.parentNode.removeChild(this.cursorOverlay)}closeConnection(){this.jsonrpc.close()}emitInput(e){let t=convertKeyEvent(e);if(t){if(t.key===`]`&&t.ctrl&&!t.meta&&!t.super&&!t.shift){this.jsonrpc.notify(`input`,{kind:`abort`});return}t.key!==`Unidentified`&&this.jsonrpc.notify(`input`,{kind:`key`,value:t})}}emitInputString(e){e?this.jsonrpc.notify(`input`,{kind:`input-string`,value:e}):console.error(`unexpected argument`,e)}redrawParams(){return{size:this.getDisplaySize(),fontWidth:this.option.fontWidth,fontHeight:this.option.fontHeight,fontAscent:this.option.fontAscent}}handleResize(e){this.jsonrpc.notify(`redraw`,this.redrawParams())}focusHiddenInput(){let e=this.input?.input;if(e){try{window.focus()}catch{}requestAnimationFrame(()=>{setTimeout(()=>{e.focus({preventScroll:!0})},0)})}}sendNotification(e,t){this.jsonrpc.notify(e,t)}request(e,t,n){this.jsonrpc.request(e,t,n)}getDisplaySize(){let[e,t,n,r]=this.getDisplayRectangle();return{width:Math.floor(n/this.option.fontWidth),height:Math.floor(r/this.option.fontHeight)}}callMessage(e,t){this.messageTable.get(e)(t)}findViewById(e){return this.viewMap.get(e)}login(){this.jsonrpc.request(`login`,{size:this.getDisplaySize(),foreground:this.option.foreground,background:this.option.background,fontWidth:this.option.fontWidth,fontHeight:this.option.fontHeight,fontAscent:this.option.fontAscent},e=>{if(this.updateForeground(e.foreground),this.updateBackground(e.background),e.views)for(let t of e.views)this.makeView(t);this.jsonrpc.notify(`redraw`,this.redrawParams())})}updateForeground(e){e!=null&&(this.option.foreground=e,this.input.updateForeground(e))}updateBackground(e){if(e==null)return;this.option.background=e,this.input.updateBackground(e);let t=getLemEditorElement();t.style.backgroundColor=e}makeView({id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,use_modeline:l,kind:u,type:d,content:f,border:p,border_shape:m}){let h=new View({option:this.option,id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,useModeline:l,kind:u,type:d,content:f,border:p,borderShape:m,editor:this});this.viewMap.set(e,h)}deleteView({viewInfo:{id:e}}){this.findViewById(e).delete(),this.viewMap.delete(e)}resize({viewInfo:{id:e},width:t,height:n,pixelWidth:r,pixelHeight:i}){let a=this.findViewById(e);a?a.resize(t,n,r,i):console.warn(`resize: view not found for id ${e}`)}move({viewInfo:{id:e},x:t,y:n,pixelX:r,pixelY:i}){let a=this.findViewById(e);a?a.move(t,n,r,i):console.warn(`move: view not found for id ${e}`)}redrawViewAfter({viewInfo:{id:e},isActive:t}){this.findViewById(e).touch(t)}clear({viewInfo:{id:e}}){this.findViewById(e).clear()}clearEol({viewInfo:{id:e},x:t,y:n,height:r}){this.findViewById(e).clearEol(t,n,r)}clearEob({viewInfo:{id:e},x:t,y:n}){this.findViewById(e).clearEob(t,n)}put({viewInfo:{id:e},x:t,y:n,text:r,textWidth:i,attribute:a,font:o,height:s}){this.findViewById(e).print(t,n,r,i,a,o,s)}putImage({viewInfo:{id:e},x:t,y:n,pixelWidth:r,pixelHeight:i,url:a}){this.findViewById(e).printImage(t,n,r,i,a)}modelinePut({viewInfo:{id:e},x:t,y:n,text:r,textWidth:i,attribute:a,height:o}){this.findViewById(e).printToModeline(t,n,r,i,a,o)}updateDisplay(){}moveCursor({viewInfo:{id:e},x:t,y:n,color:r,cursorText:i,cursorForeground:a}){let o=this.findViewById(e),[s,c]=this.getDisplayRectangle(),l=o.pixelX+t,u=o.pixelY+n;this.input.move(l,u);let d=r||this.option.foreground,f=a||this.option.background,p=this.cursorOverlay;switch(this.cursorType){case`bar`:p.style.left=s+l+`px`,p.style.top=c+u+`px`,p.style.width=`2px`,p.style.height=this.option.fontHeight+`px`,p.style.backgroundColor=d,p.textContent=``,p.style.color=``,p.style.font=``,p.style.paddingTop=``;break;case`underline`:p.style.left=s+l+`px`,p.style.top=c+u+this.option.fontHeight-2+`px`,p.style.width=this.option.fontWidth+`px`,p.style.height=`2px`,p.style.backgroundColor=d,p.textContent=``,p.style.color=``,p.style.font=``,p.style.paddingTop=``;break;default:p.style.left=s+l+`px`,p.style.top=c+u+`px`,p.style.width=this.option.fontWidth+`px`,p.style.height=this.option.fontHeight+`px`,p.style.backgroundColor=d,p.style.font=this.option.font,p.style.paddingTop=textOffsetY+`px`,p.textContent=i||``,p.style.color=f;break}p.style.animation=`none`,p.offsetHeight,p.style.animation=``}updateCursorShape({cursorType:e}){this.cursorType=e||`box`}changeView({viewInfo:{id:e},type:t,content:n}){let r=this.findViewById(e);switch(t){case`html`:r.changeToHTMLContent(n);break;case`editor`:r.changeToEditorContent();break}}resizeDisplay({width:e,height:t}){let n=getLemEditorElement();n.style.width=Math.floor(e*this.option.fontWidth)+`px`,n.style.height=Math.floor(t*this.option.fontHeight)+`px`}bulk(e){for(let{method:t,argument:n}of e)this.callMessage(t,n)}exitEditor(){this.onExit&&this.onExit()}getClipboardText(){navigator.clipboard?.readText().then(e=>{this.jsonrpc.notify(`got-clipboard-text`,{text:e})})}setClipboardText({text:e}){navigator.clipboard&&navigator.clipboard.writeText(e)}jsEval({viewInfo:{id:e},code:t}){let n=this.findViewById(e).evalIn(t);return n&&n.toString()}setFont({fontName:e,fontSize:t}){this.option.setFont(e||this.option.fontName,t||this.option.fontSize),this.cursorOverlay.style.width=this.option.fontWidth+`px`,this.cursorOverlay.style.height=this.option.fontHeight+`px`,this.jsonrpc.notify(`redraw`,this.redrawParams())}getFont(){return{name:this.option.fontName,size:this.option.fontSize}}loadCSS({content:e}){let t=document.createElement(`style`);t.textContent=e,document.head.appendChild(t)}notifyToServer(e,t){this.jsonrpc.notify(`invoke`,{method:e,args:t})}},canvas=document.querySelector(`#editor`);async function main(){await Promise.all([document.fonts.load(`19px file-icons`),document.fonts.load(`19px AllTheIcons`),document.fonts.load(`19px fontawesome`),document.fonts.load(`19px material-design-icons`),document.fonts.load(`19px octicons`)]),await document.fonts.ready;let e=new Editor({canvas,fontName:`Monospace`,fontSize:18,url:`${window.location.protocol===`https:`?`wss`:`ws`}://${window.location.hostname}:${window.location.port}`,onExit:null,onClosed:null});window.addEventListener(`message`,t=>{t.data.type===`invoke-lem`&&e.notifyToServer(t.data.method,t.data.args)}),e.init()}main(); \ No newline at end of file diff --git a/frontends/server/frontend/editor.js b/frontends/server/frontend/editor.js index c52afb54b..baa0e2af4 100644 --- a/frontends/server/frontend/editor.js +++ b/frontends/server/frontend/editor.js @@ -36,6 +36,7 @@ function computeFontSize(font) { return [ Math.floor(textMetrics.width), Math.round(textMetrics.fontBoundingBoxAscent + textOffsetY + (textMetrics.emHeightDescent || 0)), + Math.round(textMetrics.fontBoundingBoxAscent + textOffsetY), ]; } @@ -80,11 +81,12 @@ class Option { setFont(fontName, fontSize) { const font = fontSize + 'px ' + fontName; - const [width, height] = computeFontSize(font); + const [width, height, ascent] = computeFontSize(font); this.fontName = fontName; this.fontSize = fontSize; this.fontWidth = width; this.fontHeight = height; + this.fontAscent = ascent; this.font = font; } } @@ -313,11 +315,11 @@ class BaseSurface { } } - move(x, y, pixelX, pixelY) { + // every coordinate and size in this class and its subclasses use pixels as a unit (not cells) + move(x, y) { const [x0, y0] = this.editor.getDisplayRectangle(); - // Use pixel coordinates if provided, otherwise calculate from character coordinates - const left = (pixelX != null) ? Math.floor(x0 + pixelX) : Math.floor(x0 + x * this.editor.option.fontWidth); - const top = (pixelY != null) ? Math.floor(y0 + pixelY) : Math.floor(y0 + y * this.editor.option.fontHeight); + const left = Math.floor(x0 + x); + const top = Math.floor(y0 + y); if (this.wrapper) { this.wrapper.style.left = left - borderOffsetX + 'px'; this.wrapper.style.top = top - borderOffsetY + 'px'; @@ -329,24 +331,22 @@ class BaseSurface { } } - _resize(width, height, pixelWidth, pixelHeight) { + _resize(width, height) { const ratio = window.devicePixelRatio || 1; - // Use pixel dimensions if provided, otherwise calculate from character dimensions - const actualWidth = (pixelWidth != null) ? pixelWidth : width * this.editor.option.fontWidth; - const actualHeight = (pixelHeight != null) ? pixelHeight : height * this.editor.option.fontHeight; - this.mainDOM.width = actualWidth * ratio; - this.mainDOM.height = actualHeight * ratio; - this.mainDOM.style.width = actualWidth + 'px'; - this.mainDOM.style.height = actualHeight + 'px'; + this.mainDOM.width = width * ratio; + this.mainDOM.height = height * ratio; + this.mainDOM.style.width = width + 'px'; + this.mainDOM.style.height = height + 'px'; if (this.wrapper) { - this.wrapper.style.width = actualWidth + borderOffsetX * 2 + 'px'; - this.wrapper.style.height = actualHeight + borderOffsetY * 2 + 'px'; + this.wrapper.style.width = width + borderOffsetX * 2 + 'px'; + this.wrapper.style.height = height + borderOffsetY * 2 + 'px'; } } + // drawing coordinates are relative to the surface's own top-left corner. drawBlock(x, y, width, height, color) { } - drawText(x, y, text, textWidth, attribute) { } - drawImage(x, y, widthCells, heightCells, pixelWidth, pixelHeight, url) { } + drawText(x, y, text, textWidth, attribute, font, height) { } + drawImage(x, y, width, height, url) { } clearImages(yStart, yEnd) { } clearAllImages() { } @@ -361,13 +361,14 @@ class BaseSurface { } class CanvasSurface extends BaseSurface { - constructor({ editor, view, x, y, width, height, styles, isFloating, border, cssClassName }) { + constructor({ editor, view, pixelX, pixelY, pixelWidth, pixelHeight, + styles, isFloating, border, cssClassName }) { super({ editor }); const canvas = this.setupCanvas(styles); this.setupDOM({ dom: canvas, isFloating, border, cssClassName }); - this.move(x, y); - this.resize(width, height); + this.move(pixelX, pixelY); + this.resize(pixelWidth, pixelHeight); this.drawingQueue = []; @@ -385,15 +386,15 @@ class CanvasSurface extends BaseSurface { return canvas; } - resize(width, height, pixelWidth, pixelHeight) { - this._resize(width, height, pixelWidth, pixelHeight); + resize(width, height) { + this._resize(width, height); const ratio = window.devicePixelRatio || 1; const ctx = this.mainDOM.getContext('2d'); ctx.scale(ratio, ratio); } - move(x, y, pixelX, pixelY) { - super.move(x, y, pixelX, pixelY); + move(x, y) { + super.move(x, y); if (this.imageEls) { for (const [, entry] of this.imageEls) this.positionImage(entry); } @@ -405,36 +406,32 @@ class CanvasSurface extends BaseSurface { } drawBlock(x, y, width, height, color) { - const option = this.editor.option; this.drawingQueue.push(function(ctx) { - drawBlock({ - ctx, - x: x * option.fontWidth, - y: y * option.fontHeight, - width: width * option.fontWidth, - height: height * option.fontHeight, - style: color, - }) + drawBlock({ ctx, x, y, width, height, style: color }) }); } - drawText(x, y, text, textWidth, attribute, font) { + // the background is filled first, textWidth by blockHeight, then the text drawn over it, so a + // fill with no text is an empty string with a width. only those fills pass a height, to cover a + // row an image made taller than one line of text. text keeps its own cell height. + drawText(x, y, text, textWidth, attribute, font, height) { const option = this.editor.option; + const blockHeight = height || option.fontHeight; this.drawingQueue.push(function(ctx) { font = font ? `${option.fontSize}px ${font}` : option.font; if (!attribute) { drawBlock({ ctx, - x: x * option.fontWidth, - y: y * option.fontHeight, - width: textWidth * option.fontWidth, - height: option.fontHeight, + x: x, + y: y, + width: textWidth, + height: blockHeight, style: option.background, }); drawText({ ctx, - x: x * option.fontWidth, - y: y * option.fontHeight, + x: x, + y: y, text: text, style: option.foreground, font: font, @@ -458,20 +455,18 @@ class CanvasSurface extends BaseSurface { // when the cursor overlay blinks off. background = option.background; } - const gx = x * option.fontWidth; - const gy = y * option.fontHeight; drawBlock({ ctx, - x: gx, - y: gy, - width: textWidth * option.fontWidth, - height: option.fontHeight, + x: x, + y: y, + width: textWidth, + height: blockHeight, style: background, }); drawText({ ctx, - x: gx, - y: gy, + x: x, + y: y, text: text, style: foreground, font: bold ? ('bold ' + font) : font, @@ -480,9 +475,9 @@ class CanvasSurface extends BaseSurface { if (underline) { drawHorizontalLine({ ctx, - x: gx, - y: gy + option.fontHeight - 2, - width: textWidth * option.fontWidth, + x: x, + y: y + option.fontHeight - 2, + width: textWidth, style: typeof (underline) === 'string' ? underline : foreground, lineWidth: 2 }); @@ -495,9 +490,9 @@ class CanvasSurface extends BaseSurface { imageBaseLeft() { return parseFloat(this.mainDOM.style.left) || 0; } imageBaseTop() { return parseFloat(this.mainDOM.style.top) || 0; } - drawImage(x, y, widthCells, heightCells, pixelWidth, pixelHeight, url) { + drawImage(x, y, width, height, url) { if (!this.imageEls) - // mapping "x,y" to { el, url, x, y, widthCells, heightCells, pixelWidth, pixelHeight } + // mapping "x,y" to { el, url, x, y, width, height } this.imageEls = new Map(); const key = x + ',' + y; let entry = this.imageEls.get(key); @@ -519,29 +514,24 @@ class CanvasSurface extends BaseSurface { } entry.x = x; entry.y = y; - entry.widthCells = widthCells; - entry.heightCells = heightCells; - entry.pixelWidth = pixelWidth; - entry.pixelHeight = pixelHeight; + entry.width = width; + entry.height = height; this.positionImage(entry); } positionImage(entry) { - const option = this.editor.option; - // we reserved widthCells x heightCells cells for this image. - const boxWidth = entry.widthCells * option.fontWidth; - const boxHeight = entry.heightCells * option.fontHeight; - entry.el.style.left = (this.imageBaseLeft() + entry.x * option.fontWidth) + 'px'; - entry.el.style.top = (this.imageBaseTop() + entry.y * option.fontHeight) + 'px'; - entry.el.style.width = (entry.pixelWidth != null ? Math.min(entry.pixelWidth, boxWidth) : boxWidth) + 'px'; - entry.el.style.height = (entry.pixelHeight != null ? Math.min(entry.pixelHeight, boxHeight) : boxHeight) + 'px'; + entry.el.style.left = (this.imageBaseLeft() + entry.x) + 'px'; + entry.el.style.top = (this.imageBaseTop() + entry.y) + 'px'; + entry.el.style.width = entry.width + 'px'; + entry.el.style.height = entry.height + 'px'; } - // remove image elements whose row span intersects [yStart, yEnd). + // remove image elements whose vertical span intersects [yStart, yEnd). clearImages(yStart, yEnd) { if (!this.imageEls) return; for (const [key, entry] of this.imageEls) { - if (entry.y < yEnd && (entry.y + entry.heightCells) > yStart) { + const bottom = entry.y + (entry.height || 0); + if (entry.y < yEnd && bottom > yStart) { entry.el.remove(); this.imageEls.delete(key); } @@ -572,7 +562,8 @@ class CanvasSurface extends BaseSurface { } class HTMLSurface extends BaseSurface { - constructor({ editor, x, y, width, height, styles, option, isFloating, border, html }) { + constructor({ editor, pixelX, pixelY, pixelWidth, pixelHeight, + styles, option, isFloating, border, html }) { super({ editor }); const iframe = document.createElement('iframe'); @@ -590,12 +581,12 @@ class HTMLSurface extends BaseSurface { this.iframe = iframe; - this.move(x, y); - this.resize(width, height); + this.move(pixelX, pixelY); + this.resize(pixelWidth, pixelHeight); } - resize(width, height, pixelWidth, pixelHeight) { - this._resize(width, height, pixelWidth, pixelHeight); + resize(width, height) { + this._resize(width, height); } update(content) { @@ -612,13 +603,14 @@ class HTMLSurface extends BaseSurface { } } +// x, y and height are in pixels. class VerticalBorder { constructor({ x, y, height, option, editor }) { this.option = option; this.editor = editor; this.line = document.createElement('div'); this.line.className = 'lem-editor__vertical-border'; - this.line.style.height = height * option.fontHeight + 'px'; + this.line.style.height = height + 'px'; this.line.style.position = 'absolute'; this.line.style.zIndex = zindex('vertical-border'); @@ -640,22 +632,23 @@ class VerticalBorder { move(x, y) { const [x0, y0] = this.editor.getDisplayRectangle(); - this.line.style.left = Math.floor(x0 + x * this.option.fontWidth - this.option.fontWidth / 2) + 'px'; - this.line.style.top = (y0 + y * this.option.fontHeight) + 'px'; + this.line.style.left = Math.floor(x0 + x - this.option.fontWidth / 2) + 'px'; + this.line.style.top = (y0 + y) + 'px'; } resize(height) { - this.line.style.height = height * this.option.fontHeight + 'px'; + this.line.style.height = height + 'px'; } } +// x, y and width are in pixels. class HorizontalBorder { constructor({ x, y, width, option, editor }) { this.option = option; this.editor = editor; this.line = document.createElement('div'); this.line.className = 'lem-editor__horizontal-border'; - this.line.style.width = width * option.fontWidth + 'px'; + this.line.style.width = width + 'px'; this.line.style.position = 'absolute'; this.line.style.zIndex = zindex('horizontal-border'); @@ -677,12 +670,12 @@ class HorizontalBorder { move(x, y) { const [x0, y0] = this.editor.getDisplayRectangle(); - this.line.style.left = (x0 + x * this.option.fontWidth) + 'px'; - this.line.style.top = Math.floor(y0 + y * this.option.fontHeight - 4) + 'px'; + this.line.style.left = (x0 + x) + 'px'; + this.line.style.top = Math.floor(y0 + y - 4) + 'px'; } resize(width) { - this.line.style.width = (width * this.option.fontWidth) + 'px'; + this.line.style.width = width + 'px'; } } @@ -744,17 +737,18 @@ class View { case 'tile': this.mainSurface = this.makeSurface(type, content); this.leftSideBar = new VerticalBorder({ - x: x, - y: y, - height: height + (useModeline ? 1 : 0), + x: pixelX, + y: pixelY, + height: pixelHeight + (useModeline ? option.fontHeight : 0), option: option, editor: editor, }); if (!useModeline) { this.bottomBar = new HorizontalBorder({ - x: x, - y: y + height - 1, - width: width, + x: pixelX, + // along the last row of the view, not below it. + y: pixelY + pixelHeight - option.fontHeight, + width: pixelWidth, option: option, editor: editor, }); @@ -767,9 +761,9 @@ class View { this.mainSurface = this.makeSurface(type, content); if (borderShape === 'left-border') { this.leftSideBar = new VerticalBorder({ - x: x, - y: y, - height: height, + x: pixelX, + y: pixelY, + height: pixelHeight, option: option, editor: editor, }); @@ -778,11 +772,6 @@ class View { } this.modelineSurface = useModeline ? this.makeModelineSurface() : null; - - // For floating windows with pixel coordinates, reposition using pixel coordinates - if (kind === 'floating' && (pixelX != null || pixelY != null)) { - this.move(x, y, pixelX, pixelY); - } } delete() { @@ -804,19 +793,15 @@ class View { this.pixelX = pixelX; this.pixelY = pixelY; - this.mainSurface.move(x, y, pixelX, pixelY); + this.mainSurface.move(pixelX, pixelY); if (this.modelineSurface) { - // Calculate modeline pixel position if pixel coordinates are provided - const modelinePixelY = (pixelY != null && this.pixelHeight != null) - ? pixelY + this.pixelHeight - : null; - this.modelineSurface.move(x, y + this.height, pixelX, modelinePixelY); + this.modelineSurface.move(pixelX, pixelY + this.pixelHeight); } if (this.leftSideBar) { - this.leftSideBar.move(x, y); + this.leftSideBar.move(pixelX, pixelY); } if (this.bottomBar) { - this.bottomBar.move(x, y + this.height); + this.bottomBar.move(pixelX, pixelY + this.pixelHeight); } } @@ -825,25 +810,16 @@ class View { this.height = height; this.pixelWidth = pixelWidth; this.pixelHeight = pixelHeight; - this.mainSurface.resize(width, height, pixelWidth, pixelHeight); + this.mainSurface.resize(pixelWidth, pixelHeight); if (this.modelineSurface) { - // Calculate modeline pixel position if pixel coordinates are provided - const modelinePixelY = (this.pixelY != null && pixelHeight != null) - ? this.pixelY + pixelHeight - : null; - this.modelineSurface.move( - this.x, - this.y + this.height, - this.pixelX, - modelinePixelY, - ); - this.modelineSurface.resize(width, 1); + this.modelineSurface.move(this.pixelX, this.pixelY + pixelHeight); + this.modelineSurface.resize(pixelWidth, this.option.fontHeight); } if (this.leftSideBar) { - this.leftSideBar.resize(height + (this.modelineSurface ? 1 : 0)); + this.leftSideBar.resize(pixelHeight + (this.modelineSurface ? this.option.fontHeight : 0)); } if (this.bottomBar) { - this.bottomBar.resize(width); + this.bottomBar.resize(pixelWidth); } } @@ -851,17 +827,19 @@ class View { this.mainSurface.drawBlock( 0, 0, - this.width, - this.height, + this.pixelWidth, + this.pixelHeight, this.option.background, ); + this.mainSurface.clearImages(0, this.pixelHeight); } - clearEol(x, y, height=1) { + clearEol(x, y, height) { + if (height == null) height = this.option.fontHeight; this.mainSurface.drawBlock( x, y, - this.width - x, + this.pixelWidth - x, height, this.option.background, ); @@ -872,14 +850,14 @@ class View { this.mainSurface.drawBlock( x, // x === 0 y, - this.width, - this.height - y, + this.pixelWidth, + this.pixelHeight - y, this.option.background, ); - this.mainSurface.clearImages(y, this.height); + this.mainSurface.clearImages(y, this.pixelHeight); } - print(x, y, text, textWidth, attribute, font) { + print(x, y, text, textWidth, attribute, font, height) { this.mainSurface.drawText( x, y, @@ -887,14 +865,15 @@ class View { textWidth, attribute, font, + height, ); } - printImage(x, y, width, height, pixelWidth, pixelHeight, url) { - this.mainSurface.drawImage(x, y, width, height, pixelWidth, pixelHeight, url); + printImage(x, y, pixelWidth, pixelHeight, url) { + this.mainSurface.drawImage(x, y, pixelWidth, pixelHeight, url); } - printToModeline(x, y, text, textWidth, attribute) { + printToModeline(x, y, text, textWidth, attribute, height) { if (this.modelineSurface) { this.modelineSurface.drawText( x, @@ -902,6 +881,8 @@ class View { text, textWidth, attribute, + null, + height, ); } } @@ -932,10 +913,10 @@ class View { makeHTMLSurface(content) { return new HTMLSurface({ editor: this.editor, - x: this.x, - y: this.y, - width: this.width, - height: this.height, + pixelX: this.pixelX, + pixelY: this.pixelY, + pixelWidth: this.pixelWidth, + pixelHeight: this.pixelHeight, styles: getViewStyle(this.kind, this.option), option: this.option, isFloating: this.kind === 'floating', @@ -950,10 +931,10 @@ class View { return new CanvasSurface({ option: this.editor.option, - x: this.x, - y: this.y, - width: this.width, - height: this.height, + pixelX: this.pixelX, + pixelY: this.pixelY, + pixelWidth: this.pixelWidth, + pixelHeight: this.pixelHeight, styles: getViewStyle(this.kind, this.option), editor: this.editor, border, @@ -966,10 +947,10 @@ class View { makeModelineSurface() { const surface = new CanvasSurface({ option: this.editor.option, - x: this.x, - y: this.y + this.height, - width: this.width, - height: 1, + pixelX: this.pixelX, + pixelY: this.pixelY + this.pixelHeight, + pixelWidth: this.pixelWidth, + pixelHeight: this.option.fontHeight, editor: this.editor, view: this, styles: { zIndex: zindex('modeline') }, @@ -1316,13 +1297,19 @@ export class Editor { } } + // the server draws in pixels, so it needs our cell size. sent with every redraw, which is how a + // font change reaches it. + redrawParams() { + return { + size: this.getDisplaySize(), + fontWidth: this.option.fontWidth, + fontHeight: this.option.fontHeight, + fontAscent: this.option.fontAscent, + }; + } + handleResize(event) { - const canResize = true; - if (canResize) { - this.jsonrpc.notify('redraw', { size: this.getDisplaySize() }); - } else { - this.jsonrpc.notify('redraw'); - } + this.jsonrpc.notify('redraw', this.redrawParams()); } focusHiddenInput() { @@ -1368,6 +1355,7 @@ export class Editor { background: this.option.background, fontWidth: this.option.fontWidth, fontHeight: this.option.fontHeight, + fontAscent: this.option.fontAscent, }, (response) => { this.updateForeground(response.foreground); this.updateBackground(response.background); @@ -1377,7 +1365,7 @@ export class Editor { } } - this.jsonrpc.notify('redraw', { size: this.getDisplaySize() }); + this.jsonrpc.notify('redraw', this.redrawParams()); }); } @@ -1462,19 +1450,19 @@ export class Editor { view.clearEob(x, y); } - put({ viewInfo: { id }, x, y, text, textWidth, attribute, font }) { + put({ viewInfo: { id }, x, y, text, textWidth, attribute, font, height }) { const view = this.findViewById(id); - view.print(x, y, text, textWidth, attribute, font); + view.print(x, y, text, textWidth, attribute, font, height); } - putImage({ viewInfo: { id }, x, y, width, height, pixelWidth, pixelHeight, url }) { + putImage({ viewInfo: { id }, x, y, pixelWidth, pixelHeight, url }) { const view = this.findViewById(id); - view.printImage(x, y, width, height, pixelWidth, pixelHeight, url); + view.printImage(x, y, pixelWidth, pixelHeight, url); } - modelinePut({ viewInfo: { id }, x, y, text, textWidth, attribute }) { + modelinePut({ viewInfo: { id }, x, y, text, textWidth, attribute, height }) { const view = this.findViewById(id); - view.printToModeline(x, y, text, textWidth, attribute); + view.printToModeline(x, y, text, textWidth, attribute, height); } updateDisplay() { @@ -1483,8 +1471,9 @@ export class Editor { moveCursor({ viewInfo: { id }, x, y, color, cursorText, cursorForeground }) { const view = this.findViewById(id); const [x0, y0] = this.getDisplayRectangle(); - const left = view.x * this.option.fontWidth + x * this.option.fontWidth; - const top = view.y * this.option.fontHeight + y * this.option.fontHeight; + // x and y are pixels within the view. the view's own origin is in pixels too. + const left = view.pixelX + x; + const top = view.pixelY + y; this.input.move(left, top); const cursorColor = color || this.option.foreground; @@ -1596,6 +1585,11 @@ export class Editor { fontName || this.option.fontName, fontSize || this.option.fontSize, ); + this.cursorOverlay.style.width = this.option.fontWidth + 'px'; + this.cursorOverlay.style.height = this.option.fontHeight + 'px'; + // the cell size is the unit the server draws in, so nothing on screen is still correct, + // send the new params and let the server lay the display out again. + this.jsonrpc.notify('redraw', this.redrawParams()); } getFont() { diff --git a/frontends/server/main.lisp b/frontends/server/main.lisp index 7d74ca73b..848c35b18 100644 --- a/frontends/server/main.lisp +++ b/frontends/server/main.lisp @@ -102,11 +102,15 @@ :reader jsonrpc-message-queue) (editor-thread :initform nil :accessor jsonrpc-editor-thread) - ;; pixel size of one character cell, reported by the client. - (cell-width :initform nil + ;; pixel size of one character cell. multiplied into every coordinate we send, so never NIL: + ;; we guess, and the client corrects at login. + (cell-width :initform 8 :accessor jsonrpc-cell-width) - (cell-height :initform nil - :accessor jsonrpc-cell-height)) + (cell-height :initform 16 + :accessor jsonrpc-cell-height) + ;; how far below a cell's top the client puts the text baseline. + (cell-ascent :initform nil + :accessor jsonrpc-cell-ascent)) (:default-initargs :name :jsonrpc :redraw-after-modifying-floating-window t @@ -157,6 +161,21 @@ the same immutable instance for every subsequent message." 'vector))) (notify jsonrpc "bulk" argument))) +(defun update-cell-metrics (jsonrpc params) + "take the client's font metrics out of PARAMS, if it sent any. +returns true when one of them changed, since nothing already measured survives a new cell size." + (let ((changed)) + (flet ((update (key accessor) + (alexandria:when-let ((value (gethash key params))) + (when (and (realp value) (plusp value) + (not (eql value (funcall accessor jsonrpc)))) + (funcall (fdefinition `(setf ,accessor)) value jsonrpc) + (setf changed t))))) + (update "fontWidth" 'jsonrpc-cell-width) + (update "fontHeight" 'jsonrpc-cell-height) + (update "fontAscent" 'jsonrpc-cell-ascent)) + changed)) + (defun handle-login (jsonrpc logged-in-callback params) (with-error-handler () (let* ((size (gethash "size" params)) @@ -167,10 +186,7 @@ the same immutable instance for every subsequent message." (let ((width (gethash "width" size)) (height (gethash "height" size))) (resize-display jsonrpc width height))) - (alexandria:when-let ((fw (gethash "fontWidth" params))) - (when (plusp fw) (setf (jsonrpc-cell-width jsonrpc) fw))) - (alexandria:when-let ((fh (gethash "fontHeight" params))) - (when (plusp fh) (setf (jsonrpc-cell-height jsonrpc) fh))) + (update-cell-metrics jsonrpc params) (when background (alexandria:when-let (color (lem:parse-color background)) (setf (jsonrpc-background-color jsonrpc) color))) @@ -192,14 +208,22 @@ the same immutable instance for every subsequent message." (defun redraw (args) (with-error-handler () - (let ((size (and args (gethash "size" args)))) + (let ((size (and args (gethash "size" args))) + ;; the client re-sends its font metrics here, so a font change reaches us by the same + ;; path as a resize instead of needing one of its own. + (metrics-changed (and args (update-cell-metrics (lem:implementation) args)))) (when size (let ((width (gethash "width" size)) (height (gethash "height" size))) (resize-display (lem:implementation) width height) (notify (lem:implementation) "resize-display" size))) (lem:send-event (lambda () + (when metrics-changed + ;; the scroll position was recorded in the old cell size + (dolist (window (lem:window-list)) + (setf (lem-core::horizontal-scroll-start window) 0))) (lem-core::adjust-all-window-size) + ;; :force clears the caches, whose widths are stale after a cell-size change (lem:redraw-display :force t)))))) (defvar *invoke-method-table* (make-hash-table :test 'equal)) @@ -316,10 +340,10 @@ the same immutable instance for every subsequent message." view)) (defmethod lem-if:view-width ((jsonrpc jsonrpc) view) - (view-width view)) + (view-px-width view)) (defmethod lem-if:view-height ((jsonrpc jsonrpc) view) - (view-height view)) + (view-px-height view)) (defmethod lem-if:delete-view ((jsonrpc jsonrpc) view) (with-error-handler () @@ -336,7 +360,9 @@ the same immutable instance for every subsequent message." "resize-view" (hash "viewInfo" (view-id-hash view) "width" width - "height" height)))) + "height" height + "pixelWidth" (view-px-width view) + "pixelHeight" (view-px-height view))))) (defmethod lem-if:set-view-pos ((jsonrpc jsonrpc) view x y) (with-error-handler () @@ -345,7 +371,9 @@ the same immutable instance for every subsequent message." "move-view" (hash "viewInfo" (view-id-hash view) "x" x - "y" y)))) + "y" y + "pixelX" (view-px-x view) + "pixelY" (view-px-y view))))) (defmethod lem-if:make-view-with-pixels ((jsonrpc jsonrpc) window x y width height pixel-x pixel-y pixel-width pixel-height @@ -381,8 +409,8 @@ the same immutable instance for every subsequent message." (hash "viewInfo" (view-id-hash view) "x" x "y" y - "pixelX" pixel-x - "pixelY" pixel-y)))) + "pixelX" (view-px-x view) + "pixelY" (view-px-y view))))) (defmethod lem-if:set-view-size-pixels ((jsonrpc jsonrpc) view width height pixel-width pixel-height) (with-error-handler () @@ -392,8 +420,8 @@ the same immutable instance for every subsequent message." (hash "viewInfo" (view-id-hash view) "width" width "height" height - "pixelWidth" pixel-width - "pixelHeight" pixel-height)))) + "pixelWidth" (view-px-width view) + "pixelHeight" (view-px-height view))))) (defmethod lem-if:redraw-view-before ((jsonrpc jsonrpc) view) ) @@ -471,11 +499,15 @@ the same immutable instance for every subsequent message." (mouse:get-position)) (defmethod lem-if:cell-width ((jsonrpc jsonrpc)) - ;; TODO - 1) + (jsonrpc-cell-width jsonrpc)) + (defmethod lem-if:cell-height ((jsonrpc jsonrpc)) - ;; TODO - 1) + (jsonrpc-cell-height jsonrpc)) + +(defmethod lem-if:cell-pixel-size ((jsonrpc jsonrpc)) + (values (jsonrpc-cell-width jsonrpc) + (jsonrpc-cell-height jsonrpc) + (jsonrpc-cell-ascent jsonrpc))) (defun call (method params) (let ((mailbox (sb-concurrency:make-mailbox :name "lem-server-call-async"))) @@ -575,54 +607,11 @@ the same immutable instance for every subsequent message." ;;; drawing -(defgeneric object-width (drawing-object)) - -(defmethod object-width ((drawing-object display:void-object)) - 0) - -(defmethod object-width ((drawing-object display:text-object)) - (lem-core:string-width (display:text-object-string drawing-object))) - -(defmethod object-width ((drawing-object display:eol-cursor-object)) - 0) -(defmethod object-width ((drawing-object display:extend-to-eol-object)) - 0) - -(defmethod object-width ((drawing-object display:line-end-object)) - (lem-core:string-width (lem-core/display:text-object-string drawing-object))) - -(defmethod object-width ((drawing-object display:image-object)) - ;; width in character cells. when :pixel-width is given (and the client's cell size is known), - ;; round it up to whole cells so the column reserves enough grid space. otherwise use :width - ;; (a cell count) from the attribute. - (let ((pw (image-pixel-dimension drawing-object :pixel-width)) - (cw (jsonrpc-cell-width (lem-core:implementation)))) - (if (and pw cw) - (ceiling pw cw) - (or (display:image-object-width drawing-object) 1)))) - -(defgeneric object-height (drawing-object) - (:documentation "height of DRAWING-OBJECT in character cells. -`lem-core/display:layout-row' grows the row to fit everything on it, so returning more than 1 for -an image gives it the cells it needs.")) - -(defmethod object-height (drawing-object) - 1) - -(defmethod object-height ((drawing-object display:image-object)) - (let ((ph (image-pixel-dimension drawing-object :pixel-height)) - (ch (jsonrpc-cell-height (lem-core:implementation)))) - (if (and ph ch) - (ceiling ph ch) - (or (display:image-object-height drawing-object) 1)))) - -(defun image-pixel-dimension (object key) - "pixel value of KEY (:pixel-width / :pixel-height) on OBJECT's attribute, or NIL." - (let ((attribute (display:image-object-attribute object))) - (and attribute (lem:attribute-value attribute key)))) - -(defgeneric draw-object (jsonrpc object x y view)) +(defgeneric draw-object (jsonrpc object x y view) + (:documentation "draw OBJECT into VIEW with its top-left corner at pixel position X, Y. +`lem-core/display:layout-row' already chose Y as the row's baseline minus this object's ascent, so +a method never needs to know the row's own top or height.")) (defmethod draw-object (jsonrpc (object display:void-object) x y view) (values)) @@ -662,7 +651,9 @@ same hash." (setf attribute (lem:make-attribute :background lem-if:*background-color-of-drawing-window*))) (attribute-to-hash attribute))) -(defun put (jsonrpc view x y string attribute &key font text-width) +(defun put (jsonrpc view x y string attribute &key font text-width height) + "draw STRING at pixel position X, Y in VIEW, over a TEXT-WIDTH by HEIGHT pixel background. +HEIGHT defaults to one line of text." (with-error-handler () (notify* jsonrpc (ecase *put-target* @@ -672,7 +663,8 @@ same hash." "x" x "y" y "text" string - "textWidth" (or text-width (lem:string-width string)) + "textWidth" (or text-width (* (lem:string-width string) (jsonrpc-cell-width jsonrpc))) + "height" height "attribute" (ensure-attribute attribute) "font" font)))) @@ -680,7 +672,7 @@ same hash." (let* ((string (display:text-object-string object)) (attribute (display:text-object-attribute object)) (type (display:text-object-type object)) - (width (object-width object))) + (width (lem-if:object-width jsonrpc object))) (when (and attribute (lem-core:cursor-attribute-p attribute)) (lem-core:set-last-print-cursor (view-window view) x y)) (put jsonrpc @@ -695,7 +687,7 @@ same hash." (let* ((string (display:text-object-string object)) (attribute (display:text-object-attribute object)) (type (display:text-object-type object)) - (width (object-width object))) + (width (lem-if:object-width jsonrpc object))) (when (and attribute (lem-core:cursor-attribute-p attribute)) (lem-core:set-last-print-cursor (view-window view) x y)) (put jsonrpc @@ -714,15 +706,16 @@ same hash." :background (lem:color-to-hex-string (display:eol-cursor-object-color object))))) (lem-core:set-cursor-attribute attr) - (put jsonrpc view x y " " attr :text-width 1))) + (put jsonrpc view x y " " attr :text-width (jsonrpc-cell-width jsonrpc)))) (defmethod draw-object (jsonrpc (object display:line-end-object) x y view) (let ((string (display:text-object-string object)) (attribute (display:text-object-attribute object)) - (width (object-width object))) + (width (lem-if:object-width jsonrpc object))) (put jsonrpc view - (+ x (display:line-end-object-offset object)) + ;; the offset is a column count, unlike the x it is added to. + (+ x (* (display:line-end-object-offset object) (jsonrpc-cell-width jsonrpc))) y string attribute @@ -746,42 +739,35 @@ a string already carrying a data:/https: URL is passed through unchanged." (let ((url (image-object-url object))) (when url (with-error-handler () - ;; use the attribute's :pixel-width/:pixel-height if given, else the reserved cell box - ;; (cells * cell pixel size) when the cell size is known. - (let ((pw (or (image-pixel-dimension object :pixel-width) - (alexandria:when-let ((cw (jsonrpc-cell-width jsonrpc))) - (* (object-width object) cw)))) - (ph (or (image-pixel-dimension object :pixel-height) - (alexandria:when-let ((ch (jsonrpc-cell-height jsonrpc))) - (* (object-height object) ch))))) + (let ((pw (display:image-draw-width jsonrpc object)) + (ph (display:image-draw-height jsonrpc object))) (notify* jsonrpc "put-image" (hash "viewInfo" (view-id-hash view) "x" x "y" y - "width" (object-width object) - "height" (object-height object) "pixelWidth" pw "pixelHeight" ph "url" url))))))) (defun draw-row (jsonrpc view row) "draw ROW's background fill, then everything placed on it. -`clear-eol'/`clear-eob' only ever paint the editor's plain background, so a fill in an arbitrary -color (e.g. a highlighted row) goes out as spaces carrying that color via `put'." - (let ((width (view-width view))) +the client paints a put's background before its text, so the fill goes as an empty string sized +TEXT-WIDTH by HEIGHT, the row's full height, which may exceed a single text line's height when a +tall object (e.g. an image) sits on the row." + (let ((width (view-px-width view))) (when (and (display:row-fill-color row) (< (display:row-fill-x row) width)) - (let ((fill-width (- width (display:row-fill-x row)))) - (put jsonrpc - view - (display:row-fill-x row) - (display:row-top row) - (make-string fill-width :initial-element #\space) - (lem:make-attribute - :background - (lem:color-to-hex-string (display:row-fill-color row))) - :text-width fill-width)))) + (put jsonrpc + view + (display:row-fill-x row) + (display:row-top row) + "" + (lem:make-attribute + :background + (lem:color-to-hex-string (display:row-fill-color row))) + :text-width (- width (display:row-fill-x row)) + :height (display:row-height row)))) (loop :for placement :in (display:row-placements row) :do (draw-object jsonrpc (display:placement-object placement) @@ -791,8 +777,6 @@ color (e.g. a highlighted row) goes out as spaces carrying that color via `put'. (defmethod lem-if:render-row ((jsonrpc jsonrpc) view row) (with-error-handler () - ;; clear the row's full height (not just one line of text) since a tall object such as an image - ;; may occupy several. (notify* jsonrpc "clear-eol" (hash "viewInfo" (view-id-hash view) @@ -810,17 +794,13 @@ color (e.g. a highlighted row) goes out as spaces carrying that color via `put'. (hash "viewInfo" (view-id-hash view) "x" 0 "y" (display:row-top row) - "text" (make-string (view-width view) :initial-element #\space) - "textWidth" (view-width view) + ;; the modeline's own background: no text, just fill + "text" "" + "textWidth" (view-px-width view) + "height" (display:row-height row) "attribute" (attribute-to-hash default-attribute))) (draw-row jsonrpc view row)))) -(defmethod lem-if:object-width ((jsonrpc jsonrpc) drawing-object) - (object-width drawing-object)) - -(defmethod lem-if:object-height ((jsonrpc jsonrpc) drawing-object) - (object-height drawing-object)) - (defmethod lem-if:clear-to-end-of-window ((jsonrpc jsonrpc) view y) (notify* jsonrpc "clear-eob" diff --git a/frontends/server/view.lisp b/frontends/server/view.lisp index fe607b8e8..b5c26de81 100644 --- a/frontends/server/view.lisp +++ b/frontends/server/view.lisp @@ -13,6 +13,10 @@ :view-pixel-y :view-pixel-width :view-pixel-height + :view-px-x + :view-px-y + :view-px-width + :view-px-height :view-use-modeline :view-kind :move-view @@ -51,35 +55,63 @@ use-modeline kind border border-shape)) (apply #'%make-view args)) +(defun cell-pixel-size () + "The pixel size of one character cell, as (values WIDTH HEIGHT). +Never NIL here: this frontend starts from a guess and the client corrects it at login." + (lem-if:cell-pixel-size (lem:implementation))) + +(defun view-px-x (view) + "VIEW's left edge in pixels." + (or (view-pixel-x view) + (* (view-x view) (nth-value 0 (cell-pixel-size))))) + +(defun view-px-y (view) + "VIEW's top edge in pixels." + (or (view-pixel-y view) + (* (view-y view) (nth-value 1 (cell-pixel-size))))) + +(defun view-px-width (view) + "VIEW's width in pixels." + (or (view-pixel-width view) + (* (view-width view) (nth-value 0 (cell-pixel-size))))) + +(defun view-px-height (view) + "VIEW's height in pixels, the edit area only, since the modeline is a surface of its own." + (or (view-pixel-height view) + (* (view-height view) (nth-value 1 (cell-pixel-size))))) + (defun move-view (view x y &optional pixel-x pixel-y) - "Move view to new position. Pixel coordinates are optional." + "Move VIEW to cell position X, Y, or to PIXEL-X / PIXEL-Y for an axis given in pixels. +Passing no pixel position clears any earlier one, so the view follows the cell grid again." (setf (view-x view) x - (view-y view) y) - (when pixel-x (setf (view-pixel-x view) pixel-x)) - (when pixel-y (setf (view-pixel-y view) pixel-y))) + (view-y view) y + (view-pixel-x view) pixel-x + (view-pixel-y view) pixel-y) + (values)) (defun resize-view (view width height &optional pixel-width pixel-height) - "Resize view. Pixel dimensions are optional." + "Resize VIEW to WIDTH x HEIGHT cells, or to PIXEL-WIDTH / PIXEL-HEIGHT for a dimension given in +pixels. As in `move-view', passing no pixel size clears any earlier one." (setf (view-width view) width - (view-height view) height) - (when pixel-width (setf (view-pixel-width view) pixel-width)) - (when pixel-height (setf (view-pixel-height view) pixel-height)) + (view-height view) height + (view-pixel-width view) pixel-width + (view-pixel-height view) pixel-height) (values)) (defmethod yason:encode ((view view) &optional (stream *standard-output*)) (yason:with-output (stream) (yason:with-object () (yason:encode-object-element "id" (view-id view)) - ;; Character-unit coordinates (for backward compatibility) + ;; the cell geometry the core laid this view out on (yason:encode-object-element "x" (view-x view)) (yason:encode-object-element "y" (view-y view)) (yason:encode-object-element "width" (view-width view)) (yason:encode-object-element "height" (view-height view)) - ;; Pixel coordinates (new) - (yason:encode-object-element "pixelX" (view-pixel-x view)) - (yason:encode-object-element "pixelY" (view-pixel-y view)) - (yason:encode-object-element "pixelWidth" (view-pixel-width view)) - (yason:encode-object-element "pixelHeight" (view-pixel-height view)) + ;; and in pixels, always present, so the client never needs the cell size + (yason:encode-object-element "pixelX" (view-px-x view)) + (yason:encode-object-element "pixelY" (view-px-y view)) + (yason:encode-object-element "pixelWidth" (view-px-width view)) + (yason:encode-object-element "pixelHeight" (view-px-height view)) ;; Other existing fields (yason:encode-object-element "use_modeline" (view-use-modeline view)) (yason:encode-object-element "kind" (view-kind view)) From a9bfe67d2b964495a36e1e15b7141a0fcce742bd Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Sat, 1 Aug 2026 14:26:09 +0300 Subject: [PATCH 16/26] let an image sit on the baseline and be cropped at the edge images take an :ascent property, so one can be raised off the baseline. an image that overflows is cropped rather than wrapped; the crop narrows what is shown, not the drawn size. --- frontends/sdl2/drawing.lisp | 35 +++++++---- .../server/frontend/dist/assets/index.js | 2 +- frontends/server/frontend/editor.js | 25 +++++--- frontends/server/main.lisp | 14 ++++- src/display/physical-line.lisp | 59 +++++++++++++++++-- src/internal-packages.lisp | 2 + 6 files changed, 113 insertions(+), 24 deletions(-) diff --git a/frontends/sdl2/drawing.lisp b/frontends/sdl2/drawing.lisp index ec949b938..ddfb30837 100644 --- a/frontends/sdl2/drawing.lisp +++ b/frontends/sdl2/drawing.lisp @@ -291,18 +291,33 @@ width so the rasterizer's right-edge anti-aliasing tail is preserved." view)) (defmethod draw-object ((drawing-object image-object) x top display view) - (let* ((surface-width (object-width drawing-object)) + (let* ((draw-width (max 1 (image-draw-width (lem-core:implementation) drawing-object))) + (visible-width (object-width drawing-object)) (surface-height (object-height drawing-object)) - (texture (sdl2:create-texture-from-surface (display:display-renderer display) - (image-object-image drawing-object)))) - (display:with-scratch-rect (dest-rect display x top surface-width surface-height) - (sdl2:render-copy-ex (display:display-renderer display) - texture - :source-rect nil - :dest-rect dest-rect - :flip (list :none))) + (surface (image-object-image drawing-object)) + (texture (sdl2:create-texture-from-surface (display:display-renderer display) surface))) + (if (< visible-width draw-width) + ;; copy the leading fraction of the source into a dest that wide + (sdl2:with-rects ((dest-rect x top visible-width surface-height) + (source-rect 0 + 0 + (max 1 (round (* (sdl2:surface-width surface) + visible-width) + draw-width)) + (sdl2:surface-height surface))) + (sdl2:render-copy-ex (display:display-renderer display) + texture + :source-rect source-rect + :dest-rect dest-rect + :flip (list :none))) + (display:with-scratch-rect (dest-rect display x top visible-width surface-height) + (sdl2:render-copy-ex (display:display-renderer display) + texture + :source-rect nil + :dest-rect dest-rect + :flip (list :none)))) (sdl2:destroy-texture texture) - surface-width)) + visible-width)) (defun plain-text-object-p (object) "True when OBJECT is an instance of the base `text-object' class (and not diff --git a/frontends/server/frontend/dist/assets/index.js b/frontends/server/frontend/dist/assets/index.js index 136c84167..79fd24e7d 100644 --- a/frontends/server/frontend/dist/assets/index.js +++ b/frontends/server/frontend/dist/assets/index.js @@ -1 +1 @@ -var __defProp=Object.defineProperty,__commonJSMin=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),__exportAll=(e,t)=>{let n={};for(var r in e)__defProp(n,r,{get:e[r],enumerable:!0});return t||__defProp(n,Symbol.toStringTag,{value:`Module`}),n};(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var require_models=__commonJSMin((e=>{var t=e&&e.__extends||(function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};return function(t,n){if(typeof n!=`function`&&n!==null)throw TypeError(`Class extends value `+String(n)+` is not a constructor or null`);e(t,n);function r(){this.constructor=t}t.prototype=n===null?Object.create(n):(r.prototype=n.prototype,new r)}})();Object.defineProperty(e,"__esModule",{value:!0}),e.createJSONRPCNotification=e.createJSONRPCRequest=e.createJSONRPCSuccessResponse=e.createJSONRPCErrorResponse=e.JSONRPCErrorCode=e.JSONRPCErrorException=e.isJSONRPCResponses=e.isJSONRPCResponse=e.isJSONRPCRequests=e.isJSONRPCRequest=e.isJSONRPCID=e.JSONRPC=void 0,e.JSONRPC=`2.0`,e.isJSONRPCID=function(e){return typeof e==`string`||typeof e==`number`||e===null},e.isJSONRPCRequest=function(t){return t.jsonrpc===e.JSONRPC&&t.method!==void 0&&t.result===void 0&&t.error===void 0},e.isJSONRPCRequests=function(t){return Array.isArray(t)&&t.every(e.isJSONRPCRequest)},e.isJSONRPCResponse=function(t){return t.jsonrpc===e.JSONRPC&&t.id!==void 0&&(t.result!==void 0||t.error!==void 0)},e.isJSONRPCResponses=function(t){return Array.isArray(t)&&t.every(e.isJSONRPCResponse)};var n=function(e,t,n){var r={code:e,message:t};return n!=null&&(r.data=n),r};e.JSONRPCErrorException=function(e){t(r,e);function r(t,n,i){var a=e.call(this,t)||this;return Object.setPrototypeOf(a,r.prototype),a.code=n,a.data=i,a}return r.prototype.toObject=function(){return n(this.code,this.message,this.data)},r}(Error),(function(e){e[e.ParseError=-32700]=`ParseError`,e[e.InvalidRequest=-32600]=`InvalidRequest`,e[e.MethodNotFound=-32601]=`MethodNotFound`,e[e.InvalidParams=-32602]=`InvalidParams`,e[e.InternalError=-32603]=`InternalError`})(e.JSONRPCErrorCode||={}),e.createJSONRPCErrorResponse=function(t,r,i,a){return{jsonrpc:e.JSONRPC,id:t,error:n(r,i,a)}},e.createJSONRPCSuccessResponse=function(t,n){return{jsonrpc:e.JSONRPC,id:t,result:n??null}},e.createJSONRPCRequest=function(t,n,r){return{jsonrpc:e.JSONRPC,id:t,method:n,params:r}},e.createJSONRPCNotification=function(t,n){return{jsonrpc:e.JSONRPC,method:t,params:n}}})),require_internal=__commonJSMin((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.DefaultErrorCode=void 0,e.DefaultErrorCode=0})),require_client=__commonJSMin((e=>{var t=e&&e.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||=Promise)(function(n,a){function o(e){try{c(r.next(e))}catch(e){a(e)}}function s(e){try{c(r.throw(e))}catch(e){a(e)}}function c(e){e.done?n(e.value):i(e.value).then(o,s)}c((r=r.apply(e,t||[])).next())})},n=e&&e.__generator||function(e,t){var n={label:0,sent:function(){if(a[0]&1)throw a[1];return a[1]},trys:[],ops:[]},r,i,a,o;return o={next:s(0),throw:s(1),return:s(2)},typeof Symbol==`function`&&(o[Symbol.iterator]=function(){return this}),o;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw TypeError(`Generator is already executing.`);for(;o&&(o=0,s[0]&&(n=0)),n;)try{if(r=1,i&&(a=s[0]&2?i.return:s[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,s[1])).done)return a;switch(i=0,a&&(s=[s[0]&2,a.value]),s[0]){case 0:case 1:a=s;break;case 4:return n.label++,{value:s[1],done:!1};case 5:n.label++,i=s[1],s=[0];continue;case 7:s=n.ops.pop(),n.trys.pop();continue;default:if((a=n.trys,!(a=a.length>0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{Object.defineProperty(e,"__esModule",{value:!0})})),require_server=__commonJSMin((e=>{var t=e&&e.__assign||function(){return t=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{var t=e&&e.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||=Promise)(function(n,a){function o(e){try{c(r.next(e))}catch(e){a(e)}}function s(e){try{c(r.throw(e))}catch(e){a(e)}}function c(e){e.done?n(e.value):i(e.value).then(o,s)}c((r=r.apply(e,t||[])).next())})},n=e&&e.__generator||function(e,t){var n={label:0,sent:function(){if(a[0]&1)throw a[1];return a[1]},trys:[],ops:[]},r,i,a,o;return o={next:s(0),throw:s(1),return:s(2)},typeof Symbol==`function`&&(o[Symbol.iterator]=function(){return this}),o;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw TypeError(`Generator is already executing.`);for(;o&&(o=0,s[0]&&(n=0)),n;)try{if(r=1,i&&(a=s[0]&2?i.return:s[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,s[1])).done)return a;switch(i=0,a&&(s=[s[0]&2,a.value]),s[0]){case 0:case 1:a=s;break;case 4:return n.label++,{value:s[1],done:!1};case 5:n.label++,i=s[1],s=[0];continue;case 7:s=n.ops.pop(),n.trys.pop();continue;default:if((a=n.trys,!(a=a.length>0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{var t=e&&e.__createBinding||(Object.create?(function(e,t,n,r){r===void 0&&(r=n);var i=Object.getOwnPropertyDescriptor(t,n);(!i||(`get`in i?!t.__esModule:i.writable||i.configurable))&&(i={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,r,i)}):(function(e,t,n,r){r===void 0&&(r=n),e[r]=t[n]})),n=e&&e.__exportStar||function(e,n){for(var r in e)r!=="default"&&!Object.prototype.hasOwnProperty.call(n,r)&&t(n,e,r)};Object.defineProperty(e,"__esModule",{value:!0}),n(require_client(),e),n(require_interfaces(),e),n(require_models(),e),n(require_server(),e),n(require_server_and_client(),e)})),import_dist=require_dist(),JSONRPC=class{constructor(e,{onConnected:t,onClosed:n}){this.url=e,this.onConnected=t,this.onClosed=n,this.messageQueue=[],this.serverAndClient=null,this.connect(),this.connectionEstablished=!1,this.timerId=null,this.closed=!1}close(){this.timerId&&clearTimeout(this.timerId),this.webSocket.close(),this.closed=!0}on(e,t){this.serverAndClient.addMethod(e,t)}async requestInternal(e,t,n){let r=await this.serverAndClient.request(e,t);n&&n(r)}requestMessageQueue(){this.messageQueue.forEach(e=>{let[t,n,r]=e;this.requestInternal(t,n,r)}),this.messageQueue=[]}request(e,t,n){this.webSocket.readyState===WebSocket.OPEN?this.requestInternal(e,t,n):this.messageQueue.push([e,t,n])}notify(e,t){switch(this.webSocket.readyState){case WebSocket.OPEN:this.serverAndClient.notify(e,t);break;case WebSocket.CLOSED:break}}connect(e){this.closed||(console.log(`connect`,this.url),this.webSocket=new WebSocket(this.url),this.serverAndClient||=new import_dist.JSONRPCServerAndClient(new import_dist.JSONRPCServer,new import_dist.JSONRPCClient(e=>{try{return this.webSocket.send(JSON.stringify(e)),Promise.resolve()}catch(e){return Promise.reject(e)}})),this.webSocket.onmessage=e=>{this.serverAndClient.receiveAndSend(JSON.parse(e.data.toString()))},this.webSocket.onopen=()=>{console.log(`WebSocket connection established`),this.connectionEstablished=!0,this.onConnected&&this.onConnected(),this.requestMessageQueue()},this.webSocket.onclose=e=>{console.error(`WebScoket closed`,e),this.serverAndClient.rejectAllPendingRequests(`Connection is closed (${e.reason}).`),this.connectionEstablished&&this.onClosed(),this.timerId=setTimeout(()=>{this.connect()},3e3)},this.webSocket.onerror=e=>{console.error(`WebSocket error:`,e),this.webSocket.close()})}},keyevent_exports=__exportAll({convertKeyEvent:()=>convertKeyEvent}),modifierKeys=[`Shift`,`Control`,`Alt`,`Meta`,`CapsLock`],convertKeyTable={Enter:`Return`,ArrowRight:`Right`,ArrowLeft:`Left`,ArrowUp:`Up`,ArrowDown:`Down`,"¡":`1`,"™":`2`,"£":`3`,"¢":`4`,"∞":`5`,"§":`6`,"¶":`7`,"•":`8`,ª:`9`,º:`0`,"–":`-`,"≠":`=`,"“":`[`,"‘":`]`,"«":`\\`,"…":`;`,æ:`'`,"≤":`,`,"≥":`.`,"÷":`/`,"⁄":`!`,"€":`@`,"‹":`#`,"›":`$`,fi:`%`,fl:`^`,"‡":`&`,"°":`*`,"·":`(`,"‚":`)`,"—":`_`,"±":`+`,"”":`{`,"’":`}`,"»":`|`,Ú:`:`,Æ:`"`,"¯":`<`,"˘":`>`,"¿":`?`,œ:`q`,"∑":`w`,"´":`e`,"®":`r`,"†":`t`,"¥":`y`,"¨":`u`,ˆ:`i`,ø:`o`,π:`p`,å:`a`,ß:`s`,"∂":`d`,ƒ:`f`,"©":`g`,"˙":`h`,"∆":`j`,"˚":`k`,"¬":`l`,Ω:`z`,"≈":`x`,ç:`c`,"√":`v`,"∫":`b`,"˜":`n`,µ:`m`,Œ:`Q`,"„":`W`,"´":`E`,"‰":`R`,ˇ:`T`,Á:`Y`,"¨":`U`,ˆ:`I`,Ø:`O`,"∏":`P`,Å:`A`,Í:`S`,Î:`D`,Ï:`F`,"˝":`G`,Ó:`H`,Ô:`J`,"":`K`,Ò:`L`,"¸":`Z`,"˛":`X`,Ç:`C`,"◊":`V`,ı:`B`,"˜":`N`,Â:`M`};function getKey(e){return e.altKey?convertKeyTable[e.key]||(e.code.startsWith(`Key`)?e.code[3].toLowerCase():null)||e.key:convertKeyTable[e.key]||e.key}function convertKeyEvent(e){return modifierKeys.indexOf(e.key)===-1?{key:getKey(e),ctrl:e.ctrlKey,meta:e.altKey,super:e.metaKey,shift:e.shiftKey}:null}var lib_exports=__exportAll({computeWidth:()=>computeWidth,eawVersion:()=>version,getEAW:()=>getEAW}),defs=[[0,31,`N`],[32,126,`Na`],[127,160,`N`],[161,161,`A`],[162,163,`Na`],[164,164,`A`],[165,166,`Na`],[167,168,`A`],[169,169,`N`],[170,170,`A`],[171,171,`N`],[172,172,`Na`],[173,174,`A`],[175,175,`Na`],[176,180,`A`],[181,181,`N`],[182,186,`A`],[187,187,`N`],[188,191,`A`],[192,197,`N`],[198,198,`A`],[199,207,`N`],[208,208,`A`],[209,214,`N`],[215,216,`A`],[217,221,`N`],[222,225,`A`],[226,229,`N`],[230,230,`A`],[231,231,`N`],[232,234,`A`],[235,235,`N`],[236,237,`A`],[238,239,`N`],[240,240,`A`],[241,241,`N`],[242,243,`A`],[244,246,`N`],[247,250,`A`],[251,251,`N`],[252,252,`A`],[253,253,`N`],[254,254,`A`],[255,256,`N`],[257,257,`A`],[258,272,`N`],[273,273,`A`],[274,274,`N`],[275,275,`A`],[276,282,`N`],[283,283,`A`],[284,293,`N`],[294,295,`A`],[296,298,`N`],[299,299,`A`],[300,304,`N`],[305,307,`A`],[308,311,`N`],[312,312,`A`],[313,318,`N`],[319,322,`A`],[323,323,`N`],[324,324,`A`],[325,327,`N`],[328,331,`A`],[332,332,`N`],[333,333,`A`],[334,337,`N`],[338,339,`A`],[340,357,`N`],[358,359,`A`],[360,362,`N`],[363,363,`A`],[364,461,`N`],[462,462,`A`],[463,463,`N`],[464,464,`A`],[465,465,`N`],[466,466,`A`],[467,467,`N`],[468,468,`A`],[469,469,`N`],[470,470,`A`],[471,471,`N`],[472,472,`A`],[473,473,`N`],[474,474,`A`],[475,475,`N`],[476,476,`A`],[477,592,`N`],[593,593,`A`],[594,608,`N`],[609,609,`A`],[610,707,`N`],[708,708,`A`],[709,710,`N`],[711,711,`A`],[712,712,`N`],[713,715,`A`],[716,716,`N`],[717,717,`A`],[718,719,`N`],[720,720,`A`],[721,727,`N`],[728,731,`A`],[732,732,`N`],[733,733,`A`],[734,734,`N`],[735,735,`A`],[736,767,`N`],[768,879,`A`],[880,912,`N`],[913,929,`A`],[930,930,`N`],[931,937,`A`],[938,944,`N`],[945,961,`A`],[962,962,`N`],[963,969,`A`],[970,1024,`N`],[1025,1025,`A`],[1026,1039,`N`],[1040,1103,`A`],[1104,1104,`N`],[1105,1105,`A`],[1106,4351,`N`],[4352,4447,`W`],[4448,8207,`N`],[8208,8208,`A`],[8209,8210,`N`],[8211,8214,`A`],[8215,8215,`N`],[8216,8217,`A`],[8218,8219,`N`],[8220,8221,`A`],[8222,8223,`N`],[8224,8226,`A`],[8227,8227,`N`],[8228,8231,`A`],[8232,8239,`N`],[8240,8240,`A`],[8241,8241,`N`],[8242,8243,`A`],[8244,8244,`N`],[8245,8245,`A`],[8246,8250,`N`],[8251,8251,`A`],[8252,8253,`N`],[8254,8254,`A`],[8255,8307,`N`],[8308,8308,`A`],[8309,8318,`N`],[8319,8319,`A`],[8320,8320,`N`],[8321,8324,`A`],[8325,8360,`N`],[8361,8361,`H`],[8362,8363,`N`],[8364,8364,`A`],[8365,8450,`N`],[8451,8451,`A`],[8452,8452,`N`],[8453,8453,`A`],[8454,8456,`N`],[8457,8457,`A`],[8458,8466,`N`],[8467,8467,`A`],[8468,8469,`N`],[8470,8470,`A`],[8471,8480,`N`],[8481,8482,`A`],[8483,8485,`N`],[8486,8486,`A`],[8487,8490,`N`],[8491,8491,`A`],[8492,8530,`N`],[8531,8532,`A`],[8533,8538,`N`],[8539,8542,`A`],[8543,8543,`N`],[8544,8555,`A`],[8556,8559,`N`],[8560,8569,`A`],[8570,8584,`N`],[8585,8585,`A`],[8586,8591,`N`],[8592,8601,`A`],[8602,8631,`N`],[8632,8633,`A`],[8634,8657,`N`],[8658,8658,`A`],[8659,8659,`N`],[8660,8660,`A`],[8661,8678,`N`],[8679,8679,`A`],[8680,8703,`N`],[8704,8704,`A`],[8705,8705,`N`],[8706,8707,`A`],[8708,8710,`N`],[8711,8712,`A`],[8713,8714,`N`],[8715,8715,`A`],[8716,8718,`N`],[8719,8719,`A`],[8720,8720,`N`],[8721,8721,`A`],[8722,8724,`N`],[8725,8725,`A`],[8726,8729,`N`],[8730,8730,`A`],[8731,8732,`N`],[8733,8736,`A`],[8737,8738,`N`],[8739,8739,`A`],[8740,8740,`N`],[8741,8741,`A`],[8742,8742,`N`],[8743,8748,`A`],[8749,8749,`N`],[8750,8750,`A`],[8751,8755,`N`],[8756,8759,`A`],[8760,8763,`N`],[8764,8765,`A`],[8766,8775,`N`],[8776,8776,`A`],[8777,8779,`N`],[8780,8780,`A`],[8781,8785,`N`],[8786,8786,`A`],[8787,8799,`N`],[8800,8801,`A`],[8802,8803,`N`],[8804,8807,`A`],[8808,8809,`N`],[8810,8811,`A`],[8812,8813,`N`],[8814,8815,`A`],[8816,8833,`N`],[8834,8835,`A`],[8836,8837,`N`],[8838,8839,`A`],[8840,8852,`N`],[8853,8853,`A`],[8854,8856,`N`],[8857,8857,`A`],[8858,8868,`N`],[8869,8869,`A`],[8870,8894,`N`],[8895,8895,`A`],[8896,8977,`N`],[8978,8978,`A`],[8979,8985,`N`],[8986,8987,`W`],[8988,9e3,`N`],[9001,9002,`W`],[9003,9192,`N`],[9193,9196,`W`],[9197,9199,`N`],[9200,9200,`W`],[9201,9202,`N`],[9203,9203,`W`],[9204,9311,`N`],[9312,9449,`A`],[9450,9450,`N`],[9451,9547,`A`],[9548,9551,`N`],[9552,9587,`A`],[9588,9599,`N`],[9600,9615,`A`],[9616,9617,`N`],[9618,9621,`A`],[9622,9631,`N`],[9632,9633,`A`],[9634,9634,`N`],[9635,9641,`A`],[9642,9649,`N`],[9650,9651,`A`],[9652,9653,`N`],[9654,9655,`A`],[9656,9659,`N`],[9660,9661,`A`],[9662,9663,`N`],[9664,9665,`A`],[9666,9669,`N`],[9670,9672,`A`],[9673,9674,`N`],[9675,9675,`A`],[9676,9677,`N`],[9678,9681,`A`],[9682,9697,`N`],[9698,9701,`A`],[9702,9710,`N`],[9711,9711,`A`],[9712,9724,`N`],[9725,9726,`W`],[9727,9732,`N`],[9733,9734,`A`],[9735,9736,`N`],[9737,9737,`A`],[9738,9741,`N`],[9742,9743,`A`],[9744,9747,`N`],[9748,9749,`W`],[9750,9755,`N`],[9756,9756,`A`],[9757,9757,`N`],[9758,9758,`A`],[9759,9791,`N`],[9792,9792,`A`],[9793,9793,`N`],[9794,9794,`A`],[9795,9799,`N`],[9800,9811,`W`],[9812,9823,`N`],[9824,9825,`A`],[9826,9826,`N`],[9827,9829,`A`],[9830,9830,`N`],[9831,9834,`A`],[9835,9835,`N`],[9836,9837,`A`],[9838,9838,`N`],[9839,9839,`A`],[9840,9854,`N`],[9855,9855,`W`],[9856,9874,`N`],[9875,9875,`W`],[9876,9885,`N`],[9886,9887,`A`],[9888,9888,`N`],[9889,9889,`W`],[9890,9897,`N`],[9898,9899,`W`],[9900,9916,`N`],[9917,9918,`W`],[9919,9919,`A`],[9920,9923,`N`],[9924,9925,`W`],[9926,9933,`A`],[9934,9934,`W`],[9935,9939,`A`],[9940,9940,`W`],[9941,9953,`A`],[9954,9954,`N`],[9955,9955,`A`],[9956,9959,`N`],[9960,9961,`A`],[9962,9962,`W`],[9963,9969,`A`],[9970,9971,`W`],[9972,9972,`A`],[9973,9973,`W`],[9974,9977,`A`],[9978,9978,`W`],[9979,9980,`A`],[9981,9981,`W`],[9982,9983,`A`],[9984,9988,`N`],[9989,9989,`W`],[9990,9993,`N`],[9994,9995,`W`],[9996,10023,`N`],[10024,10024,`W`],[10025,10044,`N`],[10045,10045,`A`],[10046,10059,`N`],[10060,10060,`W`],[10061,10061,`N`],[10062,10062,`W`],[10063,10066,`N`],[10067,10069,`W`],[10070,10070,`N`],[10071,10071,`W`],[10072,10101,`N`],[10102,10111,`A`],[10112,10132,`N`],[10133,10135,`W`],[10136,10159,`N`],[10160,10160,`W`],[10161,10174,`N`],[10175,10175,`W`],[10176,10213,`N`],[10214,10221,`Na`],[10222,10628,`N`],[10629,10630,`Na`],[10631,11034,`N`],[11035,11036,`W`],[11037,11087,`N`],[11088,11088,`W`],[11089,11092,`N`],[11093,11093,`W`],[11094,11097,`A`],[11098,11903,`N`],[11904,11929,`W`],[11930,11930,`N`],[11931,12019,`W`],[12020,12031,`N`],[12032,12245,`W`],[12246,12271,`N`],[12272,12287,`W`],[12288,12288,`F`],[12289,12350,`W`],[12351,12352,`N`],[12353,12438,`W`],[12439,12440,`N`],[12441,12543,`W`],[12544,12548,`N`],[12549,12591,`W`],[12592,12592,`N`],[12593,12686,`W`],[12687,12687,`N`],[12688,12771,`W`],[12772,12782,`N`],[12783,12830,`W`],[12831,12831,`N`],[12832,12871,`W`],[12872,12879,`A`],[12880,19903,`W`],[19904,19967,`N`],[19968,42124,`W`],[42125,42127,`N`],[42128,42182,`W`],[42183,43359,`N`],[43360,43388,`W`],[43389,44031,`N`],[44032,55203,`W`],[55204,57343,`N`],[57344,63743,`A`],[63744,64255,`W`],[64256,65023,`N`],[65024,65039,`A`],[65040,65049,`W`],[65050,65071,`N`],[65072,65106,`W`],[65107,65107,`N`],[65108,65126,`W`],[65127,65127,`N`],[65128,65131,`W`],[65132,65280,`N`],[65281,65376,`F`],[65377,65470,`H`],[65471,65473,`N`],[65474,65479,`H`],[65480,65481,`N`],[65482,65487,`H`],[65488,65489,`N`],[65490,65495,`H`],[65496,65497,`N`],[65498,65500,`H`],[65501,65503,`N`],[65504,65510,`F`],[65511,65511,`N`],[65512,65518,`H`],[65519,65532,`N`],[65533,65533,`A`],[65534,94175,`N`],[94176,94180,`W`],[94181,94191,`N`],[94192,94193,`W`],[94194,94207,`N`],[94208,100343,`W`],[100344,100351,`N`],[100352,101589,`W`],[101590,101631,`N`],[101632,101640,`W`],[101641,110575,`N`],[110576,110579,`W`],[110580,110580,`N`],[110581,110587,`W`],[110588,110588,`N`],[110589,110590,`W`],[110591,110591,`N`],[110592,110882,`W`],[110883,110897,`N`],[110898,110898,`W`],[110899,110927,`N`],[110928,110930,`W`],[110931,110932,`N`],[110933,110933,`W`],[110934,110947,`N`],[110948,110951,`W`],[110952,110959,`N`],[110960,111355,`W`],[111356,126979,`N`],[126980,126980,`W`],[126981,127182,`N`],[127183,127183,`W`],[127184,127231,`N`],[127232,127242,`A`],[127243,127247,`N`],[127248,127277,`A`],[127278,127279,`N`],[127280,127337,`A`],[127338,127343,`N`],[127344,127373,`A`],[127374,127374,`W`],[127375,127376,`A`],[127377,127386,`W`],[127387,127404,`A`],[127405,127487,`N`],[127488,127490,`W`],[127491,127503,`N`],[127504,127547,`W`],[127548,127551,`N`],[127552,127560,`W`],[127561,127567,`N`],[127568,127569,`W`],[127570,127583,`N`],[127584,127589,`W`],[127590,127743,`N`],[127744,127776,`W`],[127777,127788,`N`],[127789,127797,`W`],[127798,127798,`N`],[127799,127868,`W`],[127869,127869,`N`],[127870,127891,`W`],[127892,127903,`N`],[127904,127946,`W`],[127947,127950,`N`],[127951,127955,`W`],[127956,127967,`N`],[127968,127984,`W`],[127985,127987,`N`],[127988,127988,`W`],[127989,127991,`N`],[127992,128062,`W`],[128063,128063,`N`],[128064,128064,`W`],[128065,128065,`N`],[128066,128252,`W`],[128253,128254,`N`],[128255,128317,`W`],[128318,128330,`N`],[128331,128334,`W`],[128335,128335,`N`],[128336,128359,`W`],[128360,128377,`N`],[128378,128378,`W`],[128379,128404,`N`],[128405,128406,`W`],[128407,128419,`N`],[128420,128420,`W`],[128421,128506,`N`],[128507,128591,`W`],[128592,128639,`N`],[128640,128709,`W`],[128710,128715,`N`],[128716,128716,`W`],[128717,128719,`N`],[128720,128722,`W`],[128723,128724,`N`],[128725,128727,`W`],[128728,128731,`N`],[128732,128735,`W`],[128736,128746,`N`],[128747,128748,`W`],[128749,128755,`N`],[128756,128764,`W`],[128765,128991,`N`],[128992,129003,`W`],[129004,129007,`N`],[129008,129008,`W`],[129009,129291,`N`],[129292,129338,`W`],[129339,129339,`N`],[129340,129349,`W`],[129350,129350,`N`],[129351,129535,`W`],[129536,129647,`N`],[129648,129660,`W`],[129661,129663,`N`],[129664,129672,`W`],[129673,129679,`N`],[129680,129725,`W`],[129726,129726,`N`],[129727,129733,`W`],[129734,129741,`N`],[129742,129755,`W`],[129756,129759,`N`],[129760,129768,`W`],[129769,129775,`N`],[129776,129784,`W`],[129785,131071,`N`],[131072,196605,`W`],[196606,196607,`N`],[196608,262141,`W`],[262142,917759,`N`],[917760,917999,`A`],[918e3,983039,`N`],[983040,1048573,`A`],[1048574,1048575,`N`],[1048576,1114109,`A`],[1114110,1114111,`N`]],version=`15.1.0`;function getEAWOfCodePoint(e){let t=0,n=defs.length-1;for(;t!==n;){let r=t+(n-t>>1),[i,a,o]=defs[r];if(ea)t=r+1;else return o}return defs[t][2]}function getEAW(e,t=0){let n=e.codePointAt(t);if(n!==void 0)return getEAWOfCodePoint(n)}var defaultWidths={N:1,Na:1,W:2,F:2,H:1,A:1};function computeWidth(e,t){let n=0;for(let r of e){let e=getEAW(r);n+=t&&t[e]||defaultWidths[e]}return n}var textOffsetY=5,isSafari=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);function isWideChar(e){switch(getEAW(e)){case`F`:case`W`:return!0;default:return!1}}function isMacOS(){return window.navigator.userAgent.indexOf(`Mac OS X`)!==-1}function computeFontSize(e){let t=document.createElement(`canvas`).getContext(`2d`);t.font=e;let n=t.measureText(`W`);return[Math.floor(n.width),Math.round(n.fontBoundingBoxAscent+textOffsetY+(n.emHeightDescent||0)),Math.round(n.fontBoundingBoxAscent+textOffsetY)]}function drawBlock({ctx:e,x:t,y:n,width:r,height:i,style:a}){e.fillStyle=a,e.fillRect(t,n,r,i)}function drawText({ctx:e,x:t,y:n,text:r,font:i,style:a,option:o}){n+=Math.round(textOffsetY),e.fillStyle=a,e.font=i,e.textBaseline=`top`;for(let i of r)isWideChar(i)?(e.fillText(i,t,n,o.fontWidth*2),t+=o.fontWidth*2):(e.fillText(i,t,n,o.fontWidth),t+=o.fontWidth)}function drawHorizontalLine({ctx:e,x:t,y:n,width:r,style:i,lineWidth:a=1}){e.strokeStyle=i,e.lineWidth=a,e.setLineDash=[],e.beginPath(),e.moveTo(t,n),e.lineTo(t+r,n),e.stroke()}var Option=class{constructor({fontName:e,fontSize:t}){this.setFont(e,t),this.foreground=`#cccccc`,this.background=`#2d2d2d`}setFont(e,t){let n=t+`px `+e,[r,i,a]=computeFontSize(n);this.fontName=e,this.fontSize=t,this.fontWidth=r,this.fontHeight=i,this.fontAscent=a,this.font=n}};function getLemEditorElement(){return document.getElementById(`lem-editor`)}function normalizeWheelDelta(e,t,n,r){switch(n){case 0:return{dx:e/r,dy:t/r};case 2:return{dx:e*20,dy:t*20};default:return{dx:e,dy:t}}}function extractWholeLines(e,t){let n=Math.trunc(e),r=Math.trunc(t);return{scrollX:n,scrollY:r,remainderX:e-n,remainderY:t-r}}function cursorPosition(e,t){let[n,r]=t.getDisplayRectangle(),i=e.clientX-n,a=e.clientY-r;return{pixelX:i,pixelY:a,x:Math.floor(i/t.option.fontWidth),y:Math.floor(a/t.option.fontHeight)}}function makeWheelHandler(e){let t={x:0,y:0},n=!1,r={pixelX:0,pixelY:0,x:0,y:0};return i=>{i.preventDefault(),r=cursorPosition(i,e);let{dx:a,dy:o}=normalizeWheelDelta(i.deltaX,i.deltaY,i.deltaMode,e.option.fontHeight);t={x:t.x+a,y:t.y+o},n||(n=!0,requestAnimationFrame(()=>{n=!1;let{scrollX:i,scrollY:a,remainderX:o,remainderY:s}=extractWholeLines(t.x,t.y);t={x:o,y:s},(i!==0||a!==0)&&e.jsonrpc.notify(`input`,{kind:`wheel`,value:{...r,wheelX:-i,wheelY:-a}})}))}}function addMouseEventListeners({dom:e,editor:t,isDraggable:n,draggableStyle:r}){e.addEventListener(`contextmenu`,e=>{e.preventDefault()});let i=(e,n)=>{e.preventDefault();let[r,i]=t.getDisplayRectangle(),a=e.clientX-r,o=e.clientY-i,s=Math.floor(a/t.option.fontWidth),c=Math.floor(o/t.option.fontHeight);t.jsonrpc.notify(`input`,{kind:n,value:{x:s,y:c,pixelX:a,pixelY:o,button:e.button,clicks:e.detail}})};e.addEventListener(`mousedown`,e=>{n&&(document.body.style.cursor=r),t.focusHiddenInput(),i(e,`mousedown`)}),e.addEventListener(`mouseup`,e=>{n&&(document.body.style.cursor=`default`),i(e,`mouseup`)});let a=0;e.addEventListener(`mousemove`,e=>{e.preventDefault();let n=Date.now();if(n-a>50){a=n;let[r,i]=t.getDisplayRectangle(),o=e.clientX-r,s=e.clientY-i,c=Math.floor(o/t.option.fontWidth),l=Math.floor(s/t.option.fontHeight);t.jsonrpc.notify(`input`,{kind:`mousemove`,value:{x:c,y:l,pixelX:o,pixelY:s,button:e.buttons===0?null:e.buttons-1}})}}),n&&(e.addEventListener(`mouseover`,()=>{document.body.style.cursor=r}),e.addEventListener(`mouseout`,e=>{e.buttons!==1&&(document.body.style.cursor=`default`)})),e.addEventListener(`wheel`,makeWheelHandler(t))}var zIndexTable={"floating-window":200,modeline:100,"vertical-border":100,"horizontal-border":101};function zindex(e){return zIndexTable[e]||0}var borderOffsetX=5,borderOffsetY=10,BaseSurface=class{constructor({editor:e}){this.editor=e,this.mainDOM=null,this.wrapper=null}delete(){this.wrapper?getLemEditorElement().removeChild(this.wrapper):getLemEditorElement().removeChild(this.mainDOM)}setupDOM({dom:e,isFloating:t,border:n,cssClassName:r}){this.mainDOM=e,t&&n?(this.wrapper=document.createElement(`div`),r&&(this.wrapper.className=r),this.wrapper.style.position=`absolute`,this.wrapper.style.backgroundColor=this.editor.option.background,this.wrapper.style.zIndex=zindex(`floating-window`),this.wrapper.appendChild(e),getLemEditorElement().appendChild(this.wrapper)):(r&&(e.className=r),getLemEditorElement().appendChild(e))}move(e,t){let[n,r]=this.editor.getDisplayRectangle(),i=Math.floor(n+e),a=Math.floor(r+t);this.wrapper?(this.wrapper.style.left=i-borderOffsetX+`px`,this.wrapper.style.top=a-borderOffsetY+`px`,this.mainDOM.style.left=borderOffsetX+`px`,this.mainDOM.style.top=borderOffsetY+`px`):(this.mainDOM.style.left=i+`px`,this.mainDOM.style.top=a+`px`)}_resize(e,t){let n=window.devicePixelRatio||1;this.mainDOM.width=e*n,this.mainDOM.height=t*n,this.mainDOM.style.width=e+`px`,this.mainDOM.style.height=t+`px`,this.wrapper&&(this.wrapper.style.width=e+borderOffsetX*2+`px`,this.wrapper.style.height=t+borderOffsetY*2+`px`)}drawBlock(e,t,n,r,i){}drawText(e,t,n,r,i,a,o){}drawImage(e,t,n,r,i){}clearImages(e,t){}clearAllImages(){}touch(){}evalIn(code){return eval(code)}},CanvasSurface=class extends BaseSurface{constructor({editor:e,view:t,pixelX:n,pixelY:r,pixelWidth:i,pixelHeight:a,styles:o,isFloating:s,border:c,cssClassName:l}){super({editor:e});let u=this.setupCanvas(o);this.setupDOM({dom:u,isFloating:s,border:c,cssClassName:l}),this.move(n,r),this.resize(i,a),this.drawingQueue=[],addMouseEventListeners({dom:u,editor:e})}setupCanvas(e){let t=document.createElement(`canvas`);if(t.style.position=`absolute`,e)for(let n in e)t.style[n]=e[n];return t}resize(e,t){this._resize(e,t);let n=window.devicePixelRatio||1;this.mainDOM.getContext(`2d`).scale(n,n)}move(e,t){if(super.move(e,t),this.imageEls)for(let[,e]of this.imageEls)this.positionImage(e)}delete(){this.clearAllImages(),super.delete()}drawBlock(e,t,n,r,i){this.drawingQueue.push(function(a){drawBlock({ctx:a,x:e,y:t,width:n,height:r,style:i})})}drawText(e,t,n,r,i,a,o){let s=this.editor.option,c=o||s.fontHeight;this.drawingQueue.push(function(o){if(a=a?`${s.fontSize}px ${a}`:s.font,!i)drawBlock({ctx:o,x:e,y:t,width:r,height:c,style:s.background}),drawText({ctx:o,x:e,y:t,text:n,style:s.foreground,font:a,option:s});else{let{foreground:l,background:u,bold:d,reverse:f,underline:p,cursor:m}=i;if(l||=s.foreground,u||=s.background,f){let e=u;u=l,l=e}m&&(u=s.background),drawBlock({ctx:o,x:e,y:t,width:r,height:c,style:u}),drawText({ctx:o,x:e,y:t,text:n,style:l,font:d?`bold `+a:a,option:s}),p&&drawHorizontalLine({ctx:o,x:e,y:t+s.fontHeight-2,width:r,style:typeof p==`string`?p:l,lineWidth:2})}})}imageBaseLeft(){return parseFloat(this.mainDOM.style.left)||0}imageBaseTop(){return parseFloat(this.mainDOM.style.top)||0}drawImage(e,t,n,r,i){this.imageEls||=new Map;let a=e+`,`+t,o=this.imageEls.get(a);if(o&&o.url!==i&&(o.el.remove(),this.imageEls.delete(a),o=null),!o){let e=document.createElement(`img`);e.style.position=`absolute`,e.style.pointerEvents=`none`,e.style.zIndex=`1`,e.src=i,this.mainDOM.parentNode.appendChild(e),o={el:e,url:i},this.imageEls.set(a,o)}o.x=e,o.y=t,o.width=n,o.height=r,this.positionImage(o)}positionImage(e){e.el.style.left=this.imageBaseLeft()+e.x+`px`,e.el.style.top=this.imageBaseTop()+e.y+`px`,e.el.style.width=e.width+`px`,e.el.style.height=e.height+`px`}clearImages(e,t){if(this.imageEls)for(let[n,r]of this.imageEls){let i=r.y+(r.height||0);r.ye&&(r.el.remove(),this.imageEls.delete(n))}}clearAllImages(){if(this.imageEls){for(let[,e]of this.imageEls)e.el.remove();this.imageEls.clear()}}touch(){let e=this.mainDOM.getContext(`2d`);for(let t of this.drawingQueue)t(e);this.drawingQueue=[]}activate(){this.mainDOM.dataset.store=`active`}deactivate(){this.mainDOM.dataset.store=`inactive`}},HTMLSurface=class extends BaseSurface{constructor({editor:e,pixelX:t,pixelY:n,pixelWidth:r,pixelHeight:i,styles:a,option:o,isFloating:s,border:c,html:l}){super({editor:e});let u=document.createElement(`iframe`);this.setupDOM({dom:u,isFloating:s,border:c}),u.style.position=`absolute`,u.style.backgroundColor=o.background,u.setAttribute(`sandbox`,`allow-scripts allow-same-origin`),u.srcdoc=l,u.addEventListener(`load`,()=>{let e=u.contentWindow;e.invokeLem=(e,t)=>parent.postMessage({type:`invoke-lem`,method:e,args:t})}),this.iframe=u,this.move(t,n),this.resize(r,i)}resize(e,t){this._resize(e,t)}update(e){let t=this.iframe.contentWindow.scrollY;this.iframe.srcdoc=e,this.iframe.onload=()=>{this.iframe.onload=null,this.iframe.contentWindow.scrollTo(0,t)}}evalIn(e){return this.iframe.contentWindow.eval(e)}},VerticalBorder=class{constructor({x:e,y:t,height:n,option:r,editor:i}){this.option=r,this.editor=i,this.line=document.createElement(`div`),this.line.className=`lem-editor__vertical-border`,this.line.style.height=n+`px`,this.line.style.position=`absolute`,this.line.style.zIndex=zindex(`vertical-border`),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:i,isDraggable:!0,draggableStyle:`col-resize`})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.line.style.left=Math.floor(n+e-this.option.fontWidth/2)+`px`,this.line.style.top=r+t+`px`}resize(e){this.line.style.height=e+`px`}},HorizontalBorder=class{constructor({x:e,y:t,width:n,option:r,editor:i}){this.option=r,this.editor=i,this.line=document.createElement(`div`),this.line.className=`lem-editor__horizontal-border`,this.line.style.width=n+`px`,this.line.style.position=`absolute`,this.line.style.zIndex=zindex(`horizontal-border`),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:i,isDraggable:!0,draggableStyle:`row-resize`})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.line.style.left=n+e+`px`,this.line.style.top=Math.floor(r+t-4)+`px`}resize(e){this.line.style.width=e+`px`}},viewStyles={header:()=>{},tile:()=>{},floating:e=>({boxSizing:`border-box`,borderColor:e.foreground,backgroundColor:e.background})};function getViewStyle(e,t){return viewStyles[e](t)||{}}var View=class{constructor({id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,useModeline:l,kind:u,type:d,content:f,border:p,borderShape:m,option:h,editor:g}){switch(this.option=h,this.id=e,this.x=t,this.y=n,this.width=r,this.height=i,this.pixelX=a,this.pixelY=o,this.pixelWidth=s,this.pixelHeight=c,this.useModeline=l,this.kind=u,this.type=d,this.border=p,this.borderShape=m,this.editor=g,this.bottomBar=null,this.leftsideBar=null,u){case`tile`:this.mainSurface=this.makeSurface(d,f),this.leftSideBar=new VerticalBorder({x:a,y:o,height:c+(l?h.fontHeight:0),option:h,editor:g}),l||(this.bottomBar=new HorizontalBorder({x:a,y:o+c-h.fontHeight,width:s,option:h,editor:g}));break;case`header`:this.mainSurface=this.makeSurface(d,f);break;case`floating`:this.mainSurface=this.makeSurface(d,f),m===`left-border`&&(this.leftSideBar=new VerticalBorder({x:a,y:o,height:c,option:h,editor:g}));break}this.modelineSurface=l?this.makeModelineSurface():null}delete(){this.mainSurface.delete(),this.modelineSurface&&this.modelineSurface.delete(),this.leftSideBar&&this.leftSideBar.delete(),this.bottomBar&&this.bottomBar.delete()}move(e,t,n,r){this.x=e,this.y=t,this.pixelX=n,this.pixelY=r,this.mainSurface.move(n,r),this.modelineSurface&&this.modelineSurface.move(n,r+this.pixelHeight),this.leftSideBar&&this.leftSideBar.move(n,r),this.bottomBar&&this.bottomBar.move(n,r+this.pixelHeight)}resize(e,t,n,r){this.width=e,this.height=t,this.pixelWidth=n,this.pixelHeight=r,this.mainSurface.resize(n,r),this.modelineSurface&&(this.modelineSurface.move(this.pixelX,this.pixelY+r),this.modelineSurface.resize(n,this.option.fontHeight)),this.leftSideBar&&this.leftSideBar.resize(r+(this.modelineSurface?this.option.fontHeight:0)),this.bottomBar&&this.bottomBar.resize(n)}clear(){this.mainSurface.drawBlock(0,0,this.pixelWidth,this.pixelHeight,this.option.background),this.mainSurface.clearImages(0,this.pixelHeight)}clearEol(e,t,n){n??=this.option.fontHeight,this.mainSurface.drawBlock(e,t,this.pixelWidth-e,n,this.option.background),this.mainSurface.clearImages(t,t+n)}clearEob(e,t){this.mainSurface.drawBlock(e,t,this.pixelWidth,this.pixelHeight-t,this.option.background),this.mainSurface.clearImages(t,this.pixelHeight)}print(e,t,n,r,i,a,o){this.mainSurface.drawText(e,t,n,r,i,a,o)}printImage(e,t,n,r,i){this.mainSurface.drawImage(e,t,n,r,i)}printToModeline(e,t,n,r,i,a){this.modelineSurface&&this.modelineSurface.drawText(e,t,n,r,i,null,a)}touch(e){this.mainSurface.touch(),this.modelineSurface&&(this.modelineSurface.touch(),e?this.modelineSurface.activate():this.modelineSurface.deactivate())}makeSurface(e,t){switch(e){case`html`:return this.makeHTMLSurface(t);case`editor`:return this.makeEditorSurface();default:console.error(`unknown type: ${e}`)}}makeHTMLSurface(e){return new HTMLSurface({editor:this.editor,pixelX:this.pixelX,pixelY:this.pixelY,pixelWidth:this.pixelWidth,pixelHeight:this.pixelHeight,styles:getViewStyle(this.kind,this.option),option:this.option,isFloating:this.kind===`floating`,border:this.border,html:e})}makeEditorSurface(){let e=this.borderShape===`left-border`?0:this.border,t=this.kind===`floating`;return new CanvasSurface({option:this.editor.option,pixelX:this.pixelX,pixelY:this.pixelY,pixelWidth:this.pixelWidth,pixelHeight:this.pixelHeight,styles:getViewStyle(this.kind,this.option),editor:this.editor,border:e,isFloating:t,view:this,cssClassName:t&&e?`lem-editor__floating-window--bordered`:null})}makeModelineSurface(){let e=new CanvasSurface({option:this.editor.option,pixelX:this.pixelX,pixelY:this.pixelY+this.pixelHeight,pixelWidth:this.pixelWidth,pixelHeight:this.option.fontHeight,editor:this.editor,view:this,styles:{zIndex:zindex(`modeline`)},cssClassName:`lem-editor__mode-line`});return addMouseEventListeners({dom:e.mainDOM,editor:this.editor,isDraggable:!0,draggableStyle:`row-resize`}),e}changeToHTMLContent(e){this.mainSurface.constructor.name===`HTMLSurface`?this.mainSurface.update(e):(this.mainSurface.delete(),this.mainSurface=this.makeHTMLSurface(e))}changeToEditorContent(){this.mainSurface.delete(),this.mainSurface=this.makeEditorSurface()}evalIn(e){return this.mainSurface.evalIn(e)}};function isPasteKeyEvent(e){return isMacOS()?e.metaKey&&e.key===`v`:e.ctrlKey&&e.shiftKey&&e.key===`V`}var Input=class{constructor(e){let t=e.option;this.editor=e,this.composition=!1,this.ignoreKeydownAfterCompositionend=!1,this.span=document.createElement(`span`),this.span.style.color=t.foreground,this.span.style.backgroundColor=t.background,this.span.style.position=`absolute`,this.span.style.zIndex=1e6,this.span.style.top=`0`,this.span.style.left=`0`,this.span.style.font=t.font,this.input=document.createElement(`input`),this.input.style.backgroundColor=`transparent`,this.input.style.color=`transparent`,this.input.style.width=`0`,this.input.style.padding=`0`,this.input.style.margin=`0`,this.input.style.border=`none`,this.input.style.position=`absolute`,this.input.style.zIndex=`-10`,this.input.style.top=`0`,this.input.style.left=`0`,this.input.style.font=t.font,this.input.addEventListener(`blur`,e=>{this.input.focus()}),this.input.addEventListener(`input`,e=>{this.composition===!1&&(this.input.value=``,this.span.innerHTML=``,this.input.style.width=`0`,isMacOS()||this.editor.emitInputString(e.data))}),this.input.addEventListener(`paste`,async e=>{e.preventDefault();let t=e.clipboardData||window.Clipboard.data,n=t?.getData(`text`)??t?.getData(`text/plain`);if(n&&n.length>0){this.editor.emitInputString(n);return}try{if(navigator.clipboard?.readText){let e=await navigator.clipboard.readText();if(e&&e.length>0){this.editor.emitInputString(e);return}}}catch(e){console.warn(`clipboard.readText() failed:`,e)}alert(`Paste failed (permission/environment restriction`)}),this.input.addEventListener(`keydown`,e=>{if(!isPasteKeyEvent(e)&&!(e.isComposing||this.composition)&&e.key!==`Process`){if(this.ignoreKeydownAfterCompositionend&&(isSafari||isMacOS())){e.preventDefault(),this.ignoreKeydownAfterCompositionend=!1;return}if(!(!isMacOS()&&!e.ctrlKey&&!e.altKey&&e.key.length===1)&&(e.preventDefault(),e.isComposing!==!0&&e.code!==``))return setTimeout(()=>{this.composition||(this.editor.emitInput(e),this.input.value=``)},0),!1}}),this.input.addEventListener(`compositionstart`,e=>{this.composition=!0,this.span.innerHTML=this.input.value,this.input.style.width=this.span.offsetWidth+`px`}),this.input.addEventListener(`compositionupdate`,e=>{this.span.innerHTML=e.data,this.input.style.width=this.span.offsetWidth+`px`}),this.input.addEventListener(`compositionend`,e=>{this.composition=!1,this.editor.emitInputString(this.input.value),this.input.value=``,this.span.innerHTML=this.input.value,this.input.style.width=`0`,this.ignoreKeydownAfterCompositionend=!0}),document.body.appendChild(this.input),document.body.appendChild(this.span),this.input.focus()}finalize(){document.body.removeChild(this.input),document.body.removeChild(this.span)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.span.style.top=r+t+`px`,this.span.style.left=n+e+`px`,this.input.style.top=this.span.offsetTop+`px`,this.input.style.left=this.span.offsetLeft+`px`}updateForeground(e){this.span.style.color=e}updateBackground(e){this.span.style.backgroundColor=e}},MessageTable=class{constructor(){this.map=new Map}register(e,t){for(let n in t){let r=t[n];this.map.set(n,r),e.on(n,r)}}get(e){return this.map.get(e)}};function getDisplayRectangleDefault(){return[0,0,window.innerWidth,window.innerHeight]}var Editor=class{constructor({getDisplayRectangle:e=getDisplayRectangleDefault,fontName:t,fontSize:n,url:r,onExit:i,onClosed:a}){this.getDisplayRectangle=e,this.option=new Option({fontName:t,fontSize:n}),this.onExit=i,this.input=new Input(this),this.cursors=new Map,this.cursorOverlay=document.createElement(`div`),this.cursorOverlay.className=`lem-cursor`,this.cursorOverlay.style.width=this.option.fontWidth+`px`,this.cursorOverlay.style.height=this.option.fontHeight+`px`,this.cursorOverlay.style.backgroundColor=`#ffffff`,this.cursorType=`box`,this.viewMap=new Map,this.jsonrpc=new JSONRPC(r,{onClosed:()=>{a()}}),this.messageTable=new MessageTable,this.messageTable.register(this.jsonrpc,{"update-foreground":this.updateForeground.bind(this),"update-background":this.updateBackground.bind(this),"make-view":this.makeView.bind(this),"delete-view":this.deleteView.bind(this),"resize-view":this.resize.bind(this),"move-view":this.move.bind(this),"redraw-view-after":this.redrawViewAfter.bind(this),clear:this.clear.bind(this),"clear-eol":this.clearEol.bind(this),"clear-eob":this.clearEob.bind(this),put:this.put.bind(this),"put-image":this.putImage.bind(this),"modeline-put":this.modelinePut.bind(this),"update-display":this.updateDisplay.bind(this),"move-cursor":this.moveCursor.bind(this),"change-view":this.changeView.bind(this),"resize-display":this.resizeDisplay.bind(this),bulk:this.bulk.bind(this),exit:this.exitEditor.bind(this),"get-clipboard-text":this.getClipboardText.bind(this),"set-clipboard-text":this.setClipboardText.bind(this),"js-eval":this.jsEval.bind(this),"set-font":this.setFont.bind(this),"get-font":this.getFont.bind(this),"get-display-size":this.getDisplaySize.bind(this),"load-css":this.loadCSS.bind(this),"update-cursor-shape":this.updateCursorShape.bind(this)}),this.login(),this.boundedHandleResize=this.handleResize.bind(this),this.focusHiddenInput=this.focusHiddenInput.bind(this)}init(){window.addEventListener(`resize`,this.boundedHandleResize),document.getElementsByTagName(`html`)[0].style[`background-color`]=`#333`,getLemEditorElement().appendChild(this.cursorOverlay)}finalize(){window.removeEventListener(`resize`,this.boundedHandleResize),this.input.finalize(),this.cursorOverlay.parentNode&&this.cursorOverlay.parentNode.removeChild(this.cursorOverlay)}closeConnection(){this.jsonrpc.close()}emitInput(e){let t=convertKeyEvent(e);if(t){if(t.key===`]`&&t.ctrl&&!t.meta&&!t.super&&!t.shift){this.jsonrpc.notify(`input`,{kind:`abort`});return}t.key!==`Unidentified`&&this.jsonrpc.notify(`input`,{kind:`key`,value:t})}}emitInputString(e){e?this.jsonrpc.notify(`input`,{kind:`input-string`,value:e}):console.error(`unexpected argument`,e)}redrawParams(){return{size:this.getDisplaySize(),fontWidth:this.option.fontWidth,fontHeight:this.option.fontHeight,fontAscent:this.option.fontAscent}}handleResize(e){this.jsonrpc.notify(`redraw`,this.redrawParams())}focusHiddenInput(){let e=this.input?.input;if(e){try{window.focus()}catch{}requestAnimationFrame(()=>{setTimeout(()=>{e.focus({preventScroll:!0})},0)})}}sendNotification(e,t){this.jsonrpc.notify(e,t)}request(e,t,n){this.jsonrpc.request(e,t,n)}getDisplaySize(){let[e,t,n,r]=this.getDisplayRectangle();return{width:Math.floor(n/this.option.fontWidth),height:Math.floor(r/this.option.fontHeight)}}callMessage(e,t){this.messageTable.get(e)(t)}findViewById(e){return this.viewMap.get(e)}login(){this.jsonrpc.request(`login`,{size:this.getDisplaySize(),foreground:this.option.foreground,background:this.option.background,fontWidth:this.option.fontWidth,fontHeight:this.option.fontHeight,fontAscent:this.option.fontAscent},e=>{if(this.updateForeground(e.foreground),this.updateBackground(e.background),e.views)for(let t of e.views)this.makeView(t);this.jsonrpc.notify(`redraw`,this.redrawParams())})}updateForeground(e){e!=null&&(this.option.foreground=e,this.input.updateForeground(e))}updateBackground(e){if(e==null)return;this.option.background=e,this.input.updateBackground(e);let t=getLemEditorElement();t.style.backgroundColor=e}makeView({id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,use_modeline:l,kind:u,type:d,content:f,border:p,border_shape:m}){let h=new View({option:this.option,id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,useModeline:l,kind:u,type:d,content:f,border:p,borderShape:m,editor:this});this.viewMap.set(e,h)}deleteView({viewInfo:{id:e}}){this.findViewById(e).delete(),this.viewMap.delete(e)}resize({viewInfo:{id:e},width:t,height:n,pixelWidth:r,pixelHeight:i}){let a=this.findViewById(e);a?a.resize(t,n,r,i):console.warn(`resize: view not found for id ${e}`)}move({viewInfo:{id:e},x:t,y:n,pixelX:r,pixelY:i}){let a=this.findViewById(e);a?a.move(t,n,r,i):console.warn(`move: view not found for id ${e}`)}redrawViewAfter({viewInfo:{id:e},isActive:t}){this.findViewById(e).touch(t)}clear({viewInfo:{id:e}}){this.findViewById(e).clear()}clearEol({viewInfo:{id:e},x:t,y:n,height:r}){this.findViewById(e).clearEol(t,n,r)}clearEob({viewInfo:{id:e},x:t,y:n}){this.findViewById(e).clearEob(t,n)}put({viewInfo:{id:e},x:t,y:n,text:r,textWidth:i,attribute:a,font:o,height:s}){this.findViewById(e).print(t,n,r,i,a,o,s)}putImage({viewInfo:{id:e},x:t,y:n,pixelWidth:r,pixelHeight:i,url:a}){this.findViewById(e).printImage(t,n,r,i,a)}modelinePut({viewInfo:{id:e},x:t,y:n,text:r,textWidth:i,attribute:a,height:o}){this.findViewById(e).printToModeline(t,n,r,i,a,o)}updateDisplay(){}moveCursor({viewInfo:{id:e},x:t,y:n,color:r,cursorText:i,cursorForeground:a}){let o=this.findViewById(e),[s,c]=this.getDisplayRectangle(),l=o.pixelX+t,u=o.pixelY+n;this.input.move(l,u);let d=r||this.option.foreground,f=a||this.option.background,p=this.cursorOverlay;switch(this.cursorType){case`bar`:p.style.left=s+l+`px`,p.style.top=c+u+`px`,p.style.width=`2px`,p.style.height=this.option.fontHeight+`px`,p.style.backgroundColor=d,p.textContent=``,p.style.color=``,p.style.font=``,p.style.paddingTop=``;break;case`underline`:p.style.left=s+l+`px`,p.style.top=c+u+this.option.fontHeight-2+`px`,p.style.width=this.option.fontWidth+`px`,p.style.height=`2px`,p.style.backgroundColor=d,p.textContent=``,p.style.color=``,p.style.font=``,p.style.paddingTop=``;break;default:p.style.left=s+l+`px`,p.style.top=c+u+`px`,p.style.width=this.option.fontWidth+`px`,p.style.height=this.option.fontHeight+`px`,p.style.backgroundColor=d,p.style.font=this.option.font,p.style.paddingTop=textOffsetY+`px`,p.textContent=i||``,p.style.color=f;break}p.style.animation=`none`,p.offsetHeight,p.style.animation=``}updateCursorShape({cursorType:e}){this.cursorType=e||`box`}changeView({viewInfo:{id:e},type:t,content:n}){let r=this.findViewById(e);switch(t){case`html`:r.changeToHTMLContent(n);break;case`editor`:r.changeToEditorContent();break}}resizeDisplay({width:e,height:t}){let n=getLemEditorElement();n.style.width=Math.floor(e*this.option.fontWidth)+`px`,n.style.height=Math.floor(t*this.option.fontHeight)+`px`}bulk(e){for(let{method:t,argument:n}of e)this.callMessage(t,n)}exitEditor(){this.onExit&&this.onExit()}getClipboardText(){navigator.clipboard?.readText().then(e=>{this.jsonrpc.notify(`got-clipboard-text`,{text:e})})}setClipboardText({text:e}){navigator.clipboard&&navigator.clipboard.writeText(e)}jsEval({viewInfo:{id:e},code:t}){let n=this.findViewById(e).evalIn(t);return n&&n.toString()}setFont({fontName:e,fontSize:t}){this.option.setFont(e||this.option.fontName,t||this.option.fontSize),this.cursorOverlay.style.width=this.option.fontWidth+`px`,this.cursorOverlay.style.height=this.option.fontHeight+`px`,this.jsonrpc.notify(`redraw`,this.redrawParams())}getFont(){return{name:this.option.fontName,size:this.option.fontSize}}loadCSS({content:e}){let t=document.createElement(`style`);t.textContent=e,document.head.appendChild(t)}notifyToServer(e,t){this.jsonrpc.notify(`invoke`,{method:e,args:t})}},canvas=document.querySelector(`#editor`);async function main(){await Promise.all([document.fonts.load(`19px file-icons`),document.fonts.load(`19px AllTheIcons`),document.fonts.load(`19px fontawesome`),document.fonts.load(`19px material-design-icons`),document.fonts.load(`19px octicons`)]),await document.fonts.ready;let e=new Editor({canvas,fontName:`Monospace`,fontSize:18,url:`${window.location.protocol===`https:`?`wss`:`ws`}://${window.location.hostname}:${window.location.port}`,onExit:null,onClosed:null});window.addEventListener(`message`,t=>{t.data.type===`invoke-lem`&&e.notifyToServer(t.data.method,t.data.args)}),e.init()}main(); \ No newline at end of file +var __defProp=Object.defineProperty,__commonJSMin=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),__exportAll=(e,t)=>{let n={};for(var r in e)__defProp(n,r,{get:e[r],enumerable:!0});return t||__defProp(n,Symbol.toStringTag,{value:`Module`}),n};(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var require_models=__commonJSMin((e=>{var t=e&&e.__extends||(function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};return function(t,n){if(typeof n!=`function`&&n!==null)throw TypeError(`Class extends value `+String(n)+` is not a constructor or null`);e(t,n);function r(){this.constructor=t}t.prototype=n===null?Object.create(n):(r.prototype=n.prototype,new r)}})();Object.defineProperty(e,"__esModule",{value:!0}),e.createJSONRPCNotification=e.createJSONRPCRequest=e.createJSONRPCSuccessResponse=e.createJSONRPCErrorResponse=e.JSONRPCErrorCode=e.JSONRPCErrorException=e.isJSONRPCResponses=e.isJSONRPCResponse=e.isJSONRPCRequests=e.isJSONRPCRequest=e.isJSONRPCID=e.JSONRPC=void 0,e.JSONRPC=`2.0`,e.isJSONRPCID=function(e){return typeof e==`string`||typeof e==`number`||e===null},e.isJSONRPCRequest=function(t){return t.jsonrpc===e.JSONRPC&&t.method!==void 0&&t.result===void 0&&t.error===void 0},e.isJSONRPCRequests=function(t){return Array.isArray(t)&&t.every(e.isJSONRPCRequest)},e.isJSONRPCResponse=function(t){return t.jsonrpc===e.JSONRPC&&t.id!==void 0&&(t.result!==void 0||t.error!==void 0)},e.isJSONRPCResponses=function(t){return Array.isArray(t)&&t.every(e.isJSONRPCResponse)};var n=function(e,t,n){var r={code:e,message:t};return n!=null&&(r.data=n),r};e.JSONRPCErrorException=function(e){t(r,e);function r(t,n,i){var a=e.call(this,t)||this;return Object.setPrototypeOf(a,r.prototype),a.code=n,a.data=i,a}return r.prototype.toObject=function(){return n(this.code,this.message,this.data)},r}(Error),(function(e){e[e.ParseError=-32700]=`ParseError`,e[e.InvalidRequest=-32600]=`InvalidRequest`,e[e.MethodNotFound=-32601]=`MethodNotFound`,e[e.InvalidParams=-32602]=`InvalidParams`,e[e.InternalError=-32603]=`InternalError`})(e.JSONRPCErrorCode||={}),e.createJSONRPCErrorResponse=function(t,r,i,a){return{jsonrpc:e.JSONRPC,id:t,error:n(r,i,a)}},e.createJSONRPCSuccessResponse=function(t,n){return{jsonrpc:e.JSONRPC,id:t,result:n??null}},e.createJSONRPCRequest=function(t,n,r){return{jsonrpc:e.JSONRPC,id:t,method:n,params:r}},e.createJSONRPCNotification=function(t,n){return{jsonrpc:e.JSONRPC,method:t,params:n}}})),require_internal=__commonJSMin((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.DefaultErrorCode=void 0,e.DefaultErrorCode=0})),require_client=__commonJSMin((e=>{var t=e&&e.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||=Promise)(function(n,a){function o(e){try{c(r.next(e))}catch(e){a(e)}}function s(e){try{c(r.throw(e))}catch(e){a(e)}}function c(e){e.done?n(e.value):i(e.value).then(o,s)}c((r=r.apply(e,t||[])).next())})},n=e&&e.__generator||function(e,t){var n={label:0,sent:function(){if(a[0]&1)throw a[1];return a[1]},trys:[],ops:[]},r,i,a,o;return o={next:s(0),throw:s(1),return:s(2)},typeof Symbol==`function`&&(o[Symbol.iterator]=function(){return this}),o;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw TypeError(`Generator is already executing.`);for(;o&&(o=0,s[0]&&(n=0)),n;)try{if(r=1,i&&(a=s[0]&2?i.return:s[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,s[1])).done)return a;switch(i=0,a&&(s=[s[0]&2,a.value]),s[0]){case 0:case 1:a=s;break;case 4:return n.label++,{value:s[1],done:!1};case 5:n.label++,i=s[1],s=[0];continue;case 7:s=n.ops.pop(),n.trys.pop();continue;default:if((a=n.trys,!(a=a.length>0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{Object.defineProperty(e,"__esModule",{value:!0})})),require_server=__commonJSMin((e=>{var t=e&&e.__assign||function(){return t=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{var t=e&&e.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||=Promise)(function(n,a){function o(e){try{c(r.next(e))}catch(e){a(e)}}function s(e){try{c(r.throw(e))}catch(e){a(e)}}function c(e){e.done?n(e.value):i(e.value).then(o,s)}c((r=r.apply(e,t||[])).next())})},n=e&&e.__generator||function(e,t){var n={label:0,sent:function(){if(a[0]&1)throw a[1];return a[1]},trys:[],ops:[]},r,i,a,o;return o={next:s(0),throw:s(1),return:s(2)},typeof Symbol==`function`&&(o[Symbol.iterator]=function(){return this}),o;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw TypeError(`Generator is already executing.`);for(;o&&(o=0,s[0]&&(n=0)),n;)try{if(r=1,i&&(a=s[0]&2?i.return:s[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,s[1])).done)return a;switch(i=0,a&&(s=[s[0]&2,a.value]),s[0]){case 0:case 1:a=s;break;case 4:return n.label++,{value:s[1],done:!1};case 5:n.label++,i=s[1],s=[0];continue;case 7:s=n.ops.pop(),n.trys.pop();continue;default:if((a=n.trys,!(a=a.length>0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{var t=e&&e.__createBinding||(Object.create?(function(e,t,n,r){r===void 0&&(r=n);var i=Object.getOwnPropertyDescriptor(t,n);(!i||(`get`in i?!t.__esModule:i.writable||i.configurable))&&(i={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,r,i)}):(function(e,t,n,r){r===void 0&&(r=n),e[r]=t[n]})),n=e&&e.__exportStar||function(e,n){for(var r in e)r!=="default"&&!Object.prototype.hasOwnProperty.call(n,r)&&t(n,e,r)};Object.defineProperty(e,"__esModule",{value:!0}),n(require_client(),e),n(require_interfaces(),e),n(require_models(),e),n(require_server(),e),n(require_server_and_client(),e)})),import_dist=require_dist(),JSONRPC=class{constructor(e,{onConnected:t,onClosed:n}){this.url=e,this.onConnected=t,this.onClosed=n,this.messageQueue=[],this.serverAndClient=null,this.connect(),this.connectionEstablished=!1,this.timerId=null,this.closed=!1}close(){this.timerId&&clearTimeout(this.timerId),this.webSocket.close(),this.closed=!0}on(e,t){this.serverAndClient.addMethod(e,t)}async requestInternal(e,t,n){let r=await this.serverAndClient.request(e,t);n&&n(r)}requestMessageQueue(){this.messageQueue.forEach(e=>{let[t,n,r]=e;this.requestInternal(t,n,r)}),this.messageQueue=[]}request(e,t,n){this.webSocket.readyState===WebSocket.OPEN?this.requestInternal(e,t,n):this.messageQueue.push([e,t,n])}notify(e,t){switch(this.webSocket.readyState){case WebSocket.OPEN:this.serverAndClient.notify(e,t);break;case WebSocket.CLOSED:break}}connect(e){this.closed||(console.log(`connect`,this.url),this.webSocket=new WebSocket(this.url),this.serverAndClient||=new import_dist.JSONRPCServerAndClient(new import_dist.JSONRPCServer,new import_dist.JSONRPCClient(e=>{try{return this.webSocket.send(JSON.stringify(e)),Promise.resolve()}catch(e){return Promise.reject(e)}})),this.webSocket.onmessage=e=>{this.serverAndClient.receiveAndSend(JSON.parse(e.data.toString()))},this.webSocket.onopen=()=>{console.log(`WebSocket connection established`),this.connectionEstablished=!0,this.onConnected&&this.onConnected(),this.requestMessageQueue()},this.webSocket.onclose=e=>{console.error(`WebScoket closed`,e),this.serverAndClient.rejectAllPendingRequests(`Connection is closed (${e.reason}).`),this.connectionEstablished&&this.onClosed(),this.timerId=setTimeout(()=>{this.connect()},3e3)},this.webSocket.onerror=e=>{console.error(`WebSocket error:`,e),this.webSocket.close()})}},keyevent_exports=__exportAll({convertKeyEvent:()=>convertKeyEvent}),modifierKeys=[`Shift`,`Control`,`Alt`,`Meta`,`CapsLock`],convertKeyTable={Enter:`Return`,ArrowRight:`Right`,ArrowLeft:`Left`,ArrowUp:`Up`,ArrowDown:`Down`,"¡":`1`,"™":`2`,"£":`3`,"¢":`4`,"∞":`5`,"§":`6`,"¶":`7`,"•":`8`,ª:`9`,º:`0`,"–":`-`,"≠":`=`,"“":`[`,"‘":`]`,"«":`\\`,"…":`;`,æ:`'`,"≤":`,`,"≥":`.`,"÷":`/`,"⁄":`!`,"€":`@`,"‹":`#`,"›":`$`,fi:`%`,fl:`^`,"‡":`&`,"°":`*`,"·":`(`,"‚":`)`,"—":`_`,"±":`+`,"”":`{`,"’":`}`,"»":`|`,Ú:`:`,Æ:`"`,"¯":`<`,"˘":`>`,"¿":`?`,œ:`q`,"∑":`w`,"´":`e`,"®":`r`,"†":`t`,"¥":`y`,"¨":`u`,ˆ:`i`,ø:`o`,π:`p`,å:`a`,ß:`s`,"∂":`d`,ƒ:`f`,"©":`g`,"˙":`h`,"∆":`j`,"˚":`k`,"¬":`l`,Ω:`z`,"≈":`x`,ç:`c`,"√":`v`,"∫":`b`,"˜":`n`,µ:`m`,Œ:`Q`,"„":`W`,"´":`E`,"‰":`R`,ˇ:`T`,Á:`Y`,"¨":`U`,ˆ:`I`,Ø:`O`,"∏":`P`,Å:`A`,Í:`S`,Î:`D`,Ï:`F`,"˝":`G`,Ó:`H`,Ô:`J`,"":`K`,Ò:`L`,"¸":`Z`,"˛":`X`,Ç:`C`,"◊":`V`,ı:`B`,"˜":`N`,Â:`M`};function getKey(e){return e.altKey?convertKeyTable[e.key]||(e.code.startsWith(`Key`)?e.code[3].toLowerCase():null)||e.key:convertKeyTable[e.key]||e.key}function convertKeyEvent(e){return modifierKeys.indexOf(e.key)===-1?{key:getKey(e),ctrl:e.ctrlKey,meta:e.altKey,super:e.metaKey,shift:e.shiftKey}:null}var lib_exports=__exportAll({computeWidth:()=>computeWidth,eawVersion:()=>version,getEAW:()=>getEAW}),defs=[[0,31,`N`],[32,126,`Na`],[127,160,`N`],[161,161,`A`],[162,163,`Na`],[164,164,`A`],[165,166,`Na`],[167,168,`A`],[169,169,`N`],[170,170,`A`],[171,171,`N`],[172,172,`Na`],[173,174,`A`],[175,175,`Na`],[176,180,`A`],[181,181,`N`],[182,186,`A`],[187,187,`N`],[188,191,`A`],[192,197,`N`],[198,198,`A`],[199,207,`N`],[208,208,`A`],[209,214,`N`],[215,216,`A`],[217,221,`N`],[222,225,`A`],[226,229,`N`],[230,230,`A`],[231,231,`N`],[232,234,`A`],[235,235,`N`],[236,237,`A`],[238,239,`N`],[240,240,`A`],[241,241,`N`],[242,243,`A`],[244,246,`N`],[247,250,`A`],[251,251,`N`],[252,252,`A`],[253,253,`N`],[254,254,`A`],[255,256,`N`],[257,257,`A`],[258,272,`N`],[273,273,`A`],[274,274,`N`],[275,275,`A`],[276,282,`N`],[283,283,`A`],[284,293,`N`],[294,295,`A`],[296,298,`N`],[299,299,`A`],[300,304,`N`],[305,307,`A`],[308,311,`N`],[312,312,`A`],[313,318,`N`],[319,322,`A`],[323,323,`N`],[324,324,`A`],[325,327,`N`],[328,331,`A`],[332,332,`N`],[333,333,`A`],[334,337,`N`],[338,339,`A`],[340,357,`N`],[358,359,`A`],[360,362,`N`],[363,363,`A`],[364,461,`N`],[462,462,`A`],[463,463,`N`],[464,464,`A`],[465,465,`N`],[466,466,`A`],[467,467,`N`],[468,468,`A`],[469,469,`N`],[470,470,`A`],[471,471,`N`],[472,472,`A`],[473,473,`N`],[474,474,`A`],[475,475,`N`],[476,476,`A`],[477,592,`N`],[593,593,`A`],[594,608,`N`],[609,609,`A`],[610,707,`N`],[708,708,`A`],[709,710,`N`],[711,711,`A`],[712,712,`N`],[713,715,`A`],[716,716,`N`],[717,717,`A`],[718,719,`N`],[720,720,`A`],[721,727,`N`],[728,731,`A`],[732,732,`N`],[733,733,`A`],[734,734,`N`],[735,735,`A`],[736,767,`N`],[768,879,`A`],[880,912,`N`],[913,929,`A`],[930,930,`N`],[931,937,`A`],[938,944,`N`],[945,961,`A`],[962,962,`N`],[963,969,`A`],[970,1024,`N`],[1025,1025,`A`],[1026,1039,`N`],[1040,1103,`A`],[1104,1104,`N`],[1105,1105,`A`],[1106,4351,`N`],[4352,4447,`W`],[4448,8207,`N`],[8208,8208,`A`],[8209,8210,`N`],[8211,8214,`A`],[8215,8215,`N`],[8216,8217,`A`],[8218,8219,`N`],[8220,8221,`A`],[8222,8223,`N`],[8224,8226,`A`],[8227,8227,`N`],[8228,8231,`A`],[8232,8239,`N`],[8240,8240,`A`],[8241,8241,`N`],[8242,8243,`A`],[8244,8244,`N`],[8245,8245,`A`],[8246,8250,`N`],[8251,8251,`A`],[8252,8253,`N`],[8254,8254,`A`],[8255,8307,`N`],[8308,8308,`A`],[8309,8318,`N`],[8319,8319,`A`],[8320,8320,`N`],[8321,8324,`A`],[8325,8360,`N`],[8361,8361,`H`],[8362,8363,`N`],[8364,8364,`A`],[8365,8450,`N`],[8451,8451,`A`],[8452,8452,`N`],[8453,8453,`A`],[8454,8456,`N`],[8457,8457,`A`],[8458,8466,`N`],[8467,8467,`A`],[8468,8469,`N`],[8470,8470,`A`],[8471,8480,`N`],[8481,8482,`A`],[8483,8485,`N`],[8486,8486,`A`],[8487,8490,`N`],[8491,8491,`A`],[8492,8530,`N`],[8531,8532,`A`],[8533,8538,`N`],[8539,8542,`A`],[8543,8543,`N`],[8544,8555,`A`],[8556,8559,`N`],[8560,8569,`A`],[8570,8584,`N`],[8585,8585,`A`],[8586,8591,`N`],[8592,8601,`A`],[8602,8631,`N`],[8632,8633,`A`],[8634,8657,`N`],[8658,8658,`A`],[8659,8659,`N`],[8660,8660,`A`],[8661,8678,`N`],[8679,8679,`A`],[8680,8703,`N`],[8704,8704,`A`],[8705,8705,`N`],[8706,8707,`A`],[8708,8710,`N`],[8711,8712,`A`],[8713,8714,`N`],[8715,8715,`A`],[8716,8718,`N`],[8719,8719,`A`],[8720,8720,`N`],[8721,8721,`A`],[8722,8724,`N`],[8725,8725,`A`],[8726,8729,`N`],[8730,8730,`A`],[8731,8732,`N`],[8733,8736,`A`],[8737,8738,`N`],[8739,8739,`A`],[8740,8740,`N`],[8741,8741,`A`],[8742,8742,`N`],[8743,8748,`A`],[8749,8749,`N`],[8750,8750,`A`],[8751,8755,`N`],[8756,8759,`A`],[8760,8763,`N`],[8764,8765,`A`],[8766,8775,`N`],[8776,8776,`A`],[8777,8779,`N`],[8780,8780,`A`],[8781,8785,`N`],[8786,8786,`A`],[8787,8799,`N`],[8800,8801,`A`],[8802,8803,`N`],[8804,8807,`A`],[8808,8809,`N`],[8810,8811,`A`],[8812,8813,`N`],[8814,8815,`A`],[8816,8833,`N`],[8834,8835,`A`],[8836,8837,`N`],[8838,8839,`A`],[8840,8852,`N`],[8853,8853,`A`],[8854,8856,`N`],[8857,8857,`A`],[8858,8868,`N`],[8869,8869,`A`],[8870,8894,`N`],[8895,8895,`A`],[8896,8977,`N`],[8978,8978,`A`],[8979,8985,`N`],[8986,8987,`W`],[8988,9e3,`N`],[9001,9002,`W`],[9003,9192,`N`],[9193,9196,`W`],[9197,9199,`N`],[9200,9200,`W`],[9201,9202,`N`],[9203,9203,`W`],[9204,9311,`N`],[9312,9449,`A`],[9450,9450,`N`],[9451,9547,`A`],[9548,9551,`N`],[9552,9587,`A`],[9588,9599,`N`],[9600,9615,`A`],[9616,9617,`N`],[9618,9621,`A`],[9622,9631,`N`],[9632,9633,`A`],[9634,9634,`N`],[9635,9641,`A`],[9642,9649,`N`],[9650,9651,`A`],[9652,9653,`N`],[9654,9655,`A`],[9656,9659,`N`],[9660,9661,`A`],[9662,9663,`N`],[9664,9665,`A`],[9666,9669,`N`],[9670,9672,`A`],[9673,9674,`N`],[9675,9675,`A`],[9676,9677,`N`],[9678,9681,`A`],[9682,9697,`N`],[9698,9701,`A`],[9702,9710,`N`],[9711,9711,`A`],[9712,9724,`N`],[9725,9726,`W`],[9727,9732,`N`],[9733,9734,`A`],[9735,9736,`N`],[9737,9737,`A`],[9738,9741,`N`],[9742,9743,`A`],[9744,9747,`N`],[9748,9749,`W`],[9750,9755,`N`],[9756,9756,`A`],[9757,9757,`N`],[9758,9758,`A`],[9759,9791,`N`],[9792,9792,`A`],[9793,9793,`N`],[9794,9794,`A`],[9795,9799,`N`],[9800,9811,`W`],[9812,9823,`N`],[9824,9825,`A`],[9826,9826,`N`],[9827,9829,`A`],[9830,9830,`N`],[9831,9834,`A`],[9835,9835,`N`],[9836,9837,`A`],[9838,9838,`N`],[9839,9839,`A`],[9840,9854,`N`],[9855,9855,`W`],[9856,9874,`N`],[9875,9875,`W`],[9876,9885,`N`],[9886,9887,`A`],[9888,9888,`N`],[9889,9889,`W`],[9890,9897,`N`],[9898,9899,`W`],[9900,9916,`N`],[9917,9918,`W`],[9919,9919,`A`],[9920,9923,`N`],[9924,9925,`W`],[9926,9933,`A`],[9934,9934,`W`],[9935,9939,`A`],[9940,9940,`W`],[9941,9953,`A`],[9954,9954,`N`],[9955,9955,`A`],[9956,9959,`N`],[9960,9961,`A`],[9962,9962,`W`],[9963,9969,`A`],[9970,9971,`W`],[9972,9972,`A`],[9973,9973,`W`],[9974,9977,`A`],[9978,9978,`W`],[9979,9980,`A`],[9981,9981,`W`],[9982,9983,`A`],[9984,9988,`N`],[9989,9989,`W`],[9990,9993,`N`],[9994,9995,`W`],[9996,10023,`N`],[10024,10024,`W`],[10025,10044,`N`],[10045,10045,`A`],[10046,10059,`N`],[10060,10060,`W`],[10061,10061,`N`],[10062,10062,`W`],[10063,10066,`N`],[10067,10069,`W`],[10070,10070,`N`],[10071,10071,`W`],[10072,10101,`N`],[10102,10111,`A`],[10112,10132,`N`],[10133,10135,`W`],[10136,10159,`N`],[10160,10160,`W`],[10161,10174,`N`],[10175,10175,`W`],[10176,10213,`N`],[10214,10221,`Na`],[10222,10628,`N`],[10629,10630,`Na`],[10631,11034,`N`],[11035,11036,`W`],[11037,11087,`N`],[11088,11088,`W`],[11089,11092,`N`],[11093,11093,`W`],[11094,11097,`A`],[11098,11903,`N`],[11904,11929,`W`],[11930,11930,`N`],[11931,12019,`W`],[12020,12031,`N`],[12032,12245,`W`],[12246,12271,`N`],[12272,12287,`W`],[12288,12288,`F`],[12289,12350,`W`],[12351,12352,`N`],[12353,12438,`W`],[12439,12440,`N`],[12441,12543,`W`],[12544,12548,`N`],[12549,12591,`W`],[12592,12592,`N`],[12593,12686,`W`],[12687,12687,`N`],[12688,12771,`W`],[12772,12782,`N`],[12783,12830,`W`],[12831,12831,`N`],[12832,12871,`W`],[12872,12879,`A`],[12880,19903,`W`],[19904,19967,`N`],[19968,42124,`W`],[42125,42127,`N`],[42128,42182,`W`],[42183,43359,`N`],[43360,43388,`W`],[43389,44031,`N`],[44032,55203,`W`],[55204,57343,`N`],[57344,63743,`A`],[63744,64255,`W`],[64256,65023,`N`],[65024,65039,`A`],[65040,65049,`W`],[65050,65071,`N`],[65072,65106,`W`],[65107,65107,`N`],[65108,65126,`W`],[65127,65127,`N`],[65128,65131,`W`],[65132,65280,`N`],[65281,65376,`F`],[65377,65470,`H`],[65471,65473,`N`],[65474,65479,`H`],[65480,65481,`N`],[65482,65487,`H`],[65488,65489,`N`],[65490,65495,`H`],[65496,65497,`N`],[65498,65500,`H`],[65501,65503,`N`],[65504,65510,`F`],[65511,65511,`N`],[65512,65518,`H`],[65519,65532,`N`],[65533,65533,`A`],[65534,94175,`N`],[94176,94180,`W`],[94181,94191,`N`],[94192,94193,`W`],[94194,94207,`N`],[94208,100343,`W`],[100344,100351,`N`],[100352,101589,`W`],[101590,101631,`N`],[101632,101640,`W`],[101641,110575,`N`],[110576,110579,`W`],[110580,110580,`N`],[110581,110587,`W`],[110588,110588,`N`],[110589,110590,`W`],[110591,110591,`N`],[110592,110882,`W`],[110883,110897,`N`],[110898,110898,`W`],[110899,110927,`N`],[110928,110930,`W`],[110931,110932,`N`],[110933,110933,`W`],[110934,110947,`N`],[110948,110951,`W`],[110952,110959,`N`],[110960,111355,`W`],[111356,126979,`N`],[126980,126980,`W`],[126981,127182,`N`],[127183,127183,`W`],[127184,127231,`N`],[127232,127242,`A`],[127243,127247,`N`],[127248,127277,`A`],[127278,127279,`N`],[127280,127337,`A`],[127338,127343,`N`],[127344,127373,`A`],[127374,127374,`W`],[127375,127376,`A`],[127377,127386,`W`],[127387,127404,`A`],[127405,127487,`N`],[127488,127490,`W`],[127491,127503,`N`],[127504,127547,`W`],[127548,127551,`N`],[127552,127560,`W`],[127561,127567,`N`],[127568,127569,`W`],[127570,127583,`N`],[127584,127589,`W`],[127590,127743,`N`],[127744,127776,`W`],[127777,127788,`N`],[127789,127797,`W`],[127798,127798,`N`],[127799,127868,`W`],[127869,127869,`N`],[127870,127891,`W`],[127892,127903,`N`],[127904,127946,`W`],[127947,127950,`N`],[127951,127955,`W`],[127956,127967,`N`],[127968,127984,`W`],[127985,127987,`N`],[127988,127988,`W`],[127989,127991,`N`],[127992,128062,`W`],[128063,128063,`N`],[128064,128064,`W`],[128065,128065,`N`],[128066,128252,`W`],[128253,128254,`N`],[128255,128317,`W`],[128318,128330,`N`],[128331,128334,`W`],[128335,128335,`N`],[128336,128359,`W`],[128360,128377,`N`],[128378,128378,`W`],[128379,128404,`N`],[128405,128406,`W`],[128407,128419,`N`],[128420,128420,`W`],[128421,128506,`N`],[128507,128591,`W`],[128592,128639,`N`],[128640,128709,`W`],[128710,128715,`N`],[128716,128716,`W`],[128717,128719,`N`],[128720,128722,`W`],[128723,128724,`N`],[128725,128727,`W`],[128728,128731,`N`],[128732,128735,`W`],[128736,128746,`N`],[128747,128748,`W`],[128749,128755,`N`],[128756,128764,`W`],[128765,128991,`N`],[128992,129003,`W`],[129004,129007,`N`],[129008,129008,`W`],[129009,129291,`N`],[129292,129338,`W`],[129339,129339,`N`],[129340,129349,`W`],[129350,129350,`N`],[129351,129535,`W`],[129536,129647,`N`],[129648,129660,`W`],[129661,129663,`N`],[129664,129672,`W`],[129673,129679,`N`],[129680,129725,`W`],[129726,129726,`N`],[129727,129733,`W`],[129734,129741,`N`],[129742,129755,`W`],[129756,129759,`N`],[129760,129768,`W`],[129769,129775,`N`],[129776,129784,`W`],[129785,131071,`N`],[131072,196605,`W`],[196606,196607,`N`],[196608,262141,`W`],[262142,917759,`N`],[917760,917999,`A`],[918e3,983039,`N`],[983040,1048573,`A`],[1048574,1048575,`N`],[1048576,1114109,`A`],[1114110,1114111,`N`]],version=`15.1.0`;function getEAWOfCodePoint(e){let t=0,n=defs.length-1;for(;t!==n;){let r=t+(n-t>>1),[i,a,o]=defs[r];if(ea)t=r+1;else return o}return defs[t][2]}function getEAW(e,t=0){let n=e.codePointAt(t);if(n!==void 0)return getEAWOfCodePoint(n)}var defaultWidths={N:1,Na:1,W:2,F:2,H:1,A:1};function computeWidth(e,t){let n=0;for(let r of e){let e=getEAW(r);n+=t&&t[e]||defaultWidths[e]}return n}var textOffsetY=5,isSafari=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);function isWideChar(e){switch(getEAW(e)){case`F`:case`W`:return!0;default:return!1}}function isMacOS(){return window.navigator.userAgent.indexOf(`Mac OS X`)!==-1}function computeFontSize(e){let t=document.createElement(`canvas`).getContext(`2d`);t.font=e;let n=t.measureText(`W`);return[Math.floor(n.width),Math.round(n.fontBoundingBoxAscent+textOffsetY+(n.emHeightDescent||0)),Math.round(n.fontBoundingBoxAscent+textOffsetY)]}function drawBlock({ctx:e,x:t,y:n,width:r,height:i,style:a}){e.fillStyle=a,e.fillRect(t,n,r,i)}function drawText({ctx:e,x:t,y:n,text:r,font:i,style:a,option:o}){n+=Math.round(textOffsetY),e.fillStyle=a,e.font=i,e.textBaseline=`top`;for(let i of r)isWideChar(i)?(e.fillText(i,t,n,o.fontWidth*2),t+=o.fontWidth*2):(e.fillText(i,t,n,o.fontWidth),t+=o.fontWidth)}function drawHorizontalLine({ctx:e,x:t,y:n,width:r,style:i,lineWidth:a=1}){e.strokeStyle=i,e.lineWidth=a,e.setLineDash=[],e.beginPath(),e.moveTo(t,n),e.lineTo(t+r,n),e.stroke()}var Option=class{constructor({fontName:e,fontSize:t}){this.setFont(e,t),this.foreground=`#cccccc`,this.background=`#2d2d2d`}setFont(e,t){let n=t+`px `+e,[r,i,a]=computeFontSize(n);this.fontName=e,this.fontSize=t,this.fontWidth=r,this.fontHeight=i,this.fontAscent=a,this.font=n}};function getLemEditorElement(){return document.getElementById(`lem-editor`)}function normalizeWheelDelta(e,t,n,r){switch(n){case 0:return{dx:e/r,dy:t/r};case 2:return{dx:e*20,dy:t*20};default:return{dx:e,dy:t}}}function extractWholeLines(e,t){let n=Math.trunc(e),r=Math.trunc(t);return{scrollX:n,scrollY:r,remainderX:e-n,remainderY:t-r}}function cursorPosition(e,t){let[n,r]=t.getDisplayRectangle(),i=e.clientX-n,a=e.clientY-r;return{pixelX:i,pixelY:a,x:Math.floor(i/t.option.fontWidth),y:Math.floor(a/t.option.fontHeight)}}function makeWheelHandler(e){let t={x:0,y:0},n=!1,r={pixelX:0,pixelY:0,x:0,y:0};return i=>{i.preventDefault(),r=cursorPosition(i,e);let{dx:a,dy:o}=normalizeWheelDelta(i.deltaX,i.deltaY,i.deltaMode,e.option.fontHeight);t={x:t.x+a,y:t.y+o},n||(n=!0,requestAnimationFrame(()=>{n=!1;let{scrollX:i,scrollY:a,remainderX:o,remainderY:s}=extractWholeLines(t.x,t.y);t={x:o,y:s},(i!==0||a!==0)&&e.jsonrpc.notify(`input`,{kind:`wheel`,value:{...r,wheelX:-i,wheelY:-a}})}))}}function addMouseEventListeners({dom:e,editor:t,isDraggable:n,draggableStyle:r}){e.addEventListener(`contextmenu`,e=>{e.preventDefault()});let i=(e,n)=>{e.preventDefault();let[r,i]=t.getDisplayRectangle(),a=e.clientX-r,o=e.clientY-i,s=Math.floor(a/t.option.fontWidth),c=Math.floor(o/t.option.fontHeight);t.jsonrpc.notify(`input`,{kind:n,value:{x:s,y:c,pixelX:a,pixelY:o,button:e.button,clicks:e.detail}})};e.addEventListener(`mousedown`,e=>{n&&(document.body.style.cursor=r),t.focusHiddenInput(),i(e,`mousedown`)}),e.addEventListener(`mouseup`,e=>{n&&(document.body.style.cursor=`default`),i(e,`mouseup`)});let a=0;e.addEventListener(`mousemove`,e=>{e.preventDefault();let n=Date.now();if(n-a>50){a=n;let[r,i]=t.getDisplayRectangle(),o=e.clientX-r,s=e.clientY-i,c=Math.floor(o/t.option.fontWidth),l=Math.floor(s/t.option.fontHeight);t.jsonrpc.notify(`input`,{kind:`mousemove`,value:{x:c,y:l,pixelX:o,pixelY:s,button:e.buttons===0?null:e.buttons-1}})}}),n&&(e.addEventListener(`mouseover`,()=>{document.body.style.cursor=r}),e.addEventListener(`mouseout`,e=>{e.buttons!==1&&(document.body.style.cursor=`default`)})),e.addEventListener(`wheel`,makeWheelHandler(t))}var zIndexTable={"floating-window":200,modeline:100,"vertical-border":100,"horizontal-border":101};function zindex(e){return zIndexTable[e]||0}var borderOffsetX=5,borderOffsetY=10,BaseSurface=class{constructor({editor:e}){this.editor=e,this.mainDOM=null,this.wrapper=null}delete(){this.wrapper?getLemEditorElement().removeChild(this.wrapper):getLemEditorElement().removeChild(this.mainDOM)}setupDOM({dom:e,isFloating:t,border:n,cssClassName:r}){this.mainDOM=e,t&&n?(this.wrapper=document.createElement(`div`),r&&(this.wrapper.className=r),this.wrapper.style.position=`absolute`,this.wrapper.style.backgroundColor=this.editor.option.background,this.wrapper.style.zIndex=zindex(`floating-window`),this.wrapper.appendChild(e),getLemEditorElement().appendChild(this.wrapper)):(r&&(e.className=r),getLemEditorElement().appendChild(e))}move(e,t){let[n,r]=this.editor.getDisplayRectangle(),i=Math.floor(n+e),a=Math.floor(r+t);this.wrapper?(this.wrapper.style.left=i-borderOffsetX+`px`,this.wrapper.style.top=a-borderOffsetY+`px`,this.mainDOM.style.left=borderOffsetX+`px`,this.mainDOM.style.top=borderOffsetY+`px`):(this.mainDOM.style.left=i+`px`,this.mainDOM.style.top=a+`px`)}_resize(e,t){let n=window.devicePixelRatio||1;this.mainDOM.width=e*n,this.mainDOM.height=t*n,this.mainDOM.style.width=e+`px`,this.mainDOM.style.height=t+`px`,this.wrapper&&(this.wrapper.style.width=e+borderOffsetX*2+`px`,this.wrapper.style.height=t+borderOffsetY*2+`px`)}drawBlock(e,t,n,r,i){}drawText(e,t,n,r,i,a,o){}drawImage(e,t,n,r,i,a,o){}clearImages(e,t){}clearAllImages(){}touch(){}evalIn(code){return eval(code)}},CanvasSurface=class extends BaseSurface{constructor({editor:e,view:t,pixelX:n,pixelY:r,pixelWidth:i,pixelHeight:a,styles:o,isFloating:s,border:c,cssClassName:l}){super({editor:e});let u=this.setupCanvas(o);this.setupDOM({dom:u,isFloating:s,border:c,cssClassName:l}),this.move(n,r),this.resize(i,a),this.drawingQueue=[],addMouseEventListeners({dom:u,editor:e})}setupCanvas(e){let t=document.createElement(`canvas`);if(t.style.position=`absolute`,e)for(let n in e)t.style[n]=e[n];return t}resize(e,t){this._resize(e,t);let n=window.devicePixelRatio||1;this.mainDOM.getContext(`2d`).scale(n,n)}move(e,t){if(super.move(e,t),this.imageEls)for(let[,e]of this.imageEls)this.positionImage(e)}delete(){this.clearAllImages(),super.delete()}drawBlock(e,t,n,r,i){this.drawingQueue.push(function(a){drawBlock({ctx:a,x:e,y:t,width:n,height:r,style:i})})}drawText(e,t,n,r,i,a,o){let s=this.editor.option,c=o||s.fontHeight;this.drawingQueue.push(function(o){if(a=a?`${s.fontSize}px ${a}`:s.font,!i)drawBlock({ctx:o,x:e,y:t,width:r,height:c,style:s.background}),drawText({ctx:o,x:e,y:t,text:n,style:s.foreground,font:a,option:s});else{let{foreground:l,background:u,bold:d,reverse:f,underline:p,cursor:m}=i;if(l||=s.foreground,u||=s.background,f){let e=u;u=l,l=e}m&&(u=s.background),drawBlock({ctx:o,x:e,y:t,width:r,height:c,style:u}),drawText({ctx:o,x:e,y:t,text:n,style:l,font:d?`bold `+a:a,option:s}),p&&drawHorizontalLine({ctx:o,x:e,y:t+s.fontHeight-2,width:r,style:typeof p==`string`?p:l,lineWidth:2})}})}imageBaseLeft(){return parseFloat(this.mainDOM.style.left)||0}imageBaseTop(){return parseFloat(this.mainDOM.style.top)||0}drawImage(e,t,n,r,i,a,o){this.imageEls||=new Map;let s=e+`,`+t,c=this.imageEls.get(s);if(c&&c.url!==o&&(c.el.remove(),this.imageEls.delete(s),c=null),!c){let e=document.createElement(`img`);e.style.position=`absolute`,e.style.pointerEvents=`none`,e.style.zIndex=`1`,e.src=o,this.mainDOM.parentNode.appendChild(e),c={el:e,url:o},this.imageEls.set(s,c)}c.x=e,c.y=t,c.width=n,c.height=r,c.clipWidth=i,c.clipHeight=a,this.positionImage(c)}positionImage(e){e.el.style.left=this.imageBaseLeft()+e.x+`px`,e.el.style.top=this.imageBaseTop()+e.y+`px`,e.el.style.width=e.width+`px`,e.el.style.height=e.height+`px`;let t=e.clipWidth==null?0:Math.max(0,e.width-e.clipWidth),n=e.clipHeight==null?0:Math.max(0,e.height-e.clipHeight);e.el.style.clipPath=t>0||n>0?`inset(0px ${t}px ${n}px 0px)`:``}clearImages(e,t){if(this.imageEls)for(let[n,r]of this.imageEls){let i=r.y+(r.height||0);r.ye&&(r.el.remove(),this.imageEls.delete(n))}}clearAllImages(){if(this.imageEls){for(let[,e]of this.imageEls)e.el.remove();this.imageEls.clear()}}touch(){let e=this.mainDOM.getContext(`2d`);for(let t of this.drawingQueue)t(e);this.drawingQueue=[]}activate(){this.mainDOM.dataset.store=`active`}deactivate(){this.mainDOM.dataset.store=`inactive`}},HTMLSurface=class extends BaseSurface{constructor({editor:e,pixelX:t,pixelY:n,pixelWidth:r,pixelHeight:i,styles:a,option:o,isFloating:s,border:c,html:l}){super({editor:e});let u=document.createElement(`iframe`);this.setupDOM({dom:u,isFloating:s,border:c}),u.style.position=`absolute`,u.style.backgroundColor=o.background,u.setAttribute(`sandbox`,`allow-scripts allow-same-origin`),u.srcdoc=l,u.addEventListener(`load`,()=>{let e=u.contentWindow;e.invokeLem=(e,t)=>parent.postMessage({type:`invoke-lem`,method:e,args:t})}),this.iframe=u,this.move(t,n),this.resize(r,i)}resize(e,t){this._resize(e,t)}update(e){let t=this.iframe.contentWindow.scrollY;this.iframe.srcdoc=e,this.iframe.onload=()=>{this.iframe.onload=null,this.iframe.contentWindow.scrollTo(0,t)}}evalIn(e){return this.iframe.contentWindow.eval(e)}},VerticalBorder=class{constructor({x:e,y:t,height:n,option:r,editor:i}){this.option=r,this.editor=i,this.line=document.createElement(`div`),this.line.className=`lem-editor__vertical-border`,this.line.style.height=n+`px`,this.line.style.position=`absolute`,this.line.style.zIndex=zindex(`vertical-border`),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:i,isDraggable:!0,draggableStyle:`col-resize`})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.line.style.left=Math.floor(n+e-this.option.fontWidth/2)+`px`,this.line.style.top=r+t+`px`}resize(e){this.line.style.height=e+`px`}},HorizontalBorder=class{constructor({x:e,y:t,width:n,option:r,editor:i}){this.option=r,this.editor=i,this.line=document.createElement(`div`),this.line.className=`lem-editor__horizontal-border`,this.line.style.width=n+`px`,this.line.style.position=`absolute`,this.line.style.zIndex=zindex(`horizontal-border`),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:i,isDraggable:!0,draggableStyle:`row-resize`})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.line.style.left=n+e+`px`,this.line.style.top=Math.floor(r+t-4)+`px`}resize(e){this.line.style.width=e+`px`}},viewStyles={header:()=>{},tile:()=>{},floating:e=>({boxSizing:`border-box`,borderColor:e.foreground,backgroundColor:e.background})};function getViewStyle(e,t){return viewStyles[e](t)||{}}var View=class{constructor({id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,useModeline:l,kind:u,type:d,content:f,border:p,borderShape:m,option:h,editor:g}){switch(this.option=h,this.id=e,this.x=t,this.y=n,this.width=r,this.height=i,this.pixelX=a,this.pixelY=o,this.pixelWidth=s,this.pixelHeight=c,this.useModeline=l,this.kind=u,this.type=d,this.border=p,this.borderShape=m,this.editor=g,this.bottomBar=null,this.leftsideBar=null,u){case`tile`:this.mainSurface=this.makeSurface(d,f),this.leftSideBar=new VerticalBorder({x:a,y:o,height:c+(l?h.fontHeight:0),option:h,editor:g}),l||(this.bottomBar=new HorizontalBorder({x:a,y:o+c-h.fontHeight,width:s,option:h,editor:g}));break;case`header`:this.mainSurface=this.makeSurface(d,f);break;case`floating`:this.mainSurface=this.makeSurface(d,f),m===`left-border`&&(this.leftSideBar=new VerticalBorder({x:a,y:o,height:c,option:h,editor:g}));break}this.modelineSurface=l?this.makeModelineSurface():null}delete(){this.mainSurface.delete(),this.modelineSurface&&this.modelineSurface.delete(),this.leftSideBar&&this.leftSideBar.delete(),this.bottomBar&&this.bottomBar.delete()}move(e,t,n,r){this.x=e,this.y=t,this.pixelX=n,this.pixelY=r,this.mainSurface.move(n,r),this.modelineSurface&&this.modelineSurface.move(n,r+this.pixelHeight),this.leftSideBar&&this.leftSideBar.move(n,r),this.bottomBar&&this.bottomBar.move(n,r+this.pixelHeight)}resize(e,t,n,r){this.width=e,this.height=t,this.pixelWidth=n,this.pixelHeight=r,this.mainSurface.resize(n,r),this.modelineSurface&&(this.modelineSurface.move(this.pixelX,this.pixelY+r),this.modelineSurface.resize(n,this.option.fontHeight)),this.leftSideBar&&this.leftSideBar.resize(r+(this.modelineSurface?this.option.fontHeight:0)),this.bottomBar&&this.bottomBar.resize(n)}clear(){this.mainSurface.drawBlock(0,0,this.pixelWidth,this.pixelHeight,this.option.background),this.mainSurface.clearImages(0,this.pixelHeight)}clearEol(e,t,n){n??=this.option.fontHeight,this.mainSurface.drawBlock(e,t,this.pixelWidth-e,n,this.option.background),this.mainSurface.clearImages(t,t+n)}clearEob(e,t){this.mainSurface.drawBlock(e,t,this.pixelWidth,this.pixelHeight-t,this.option.background),this.mainSurface.clearImages(t,this.pixelHeight)}print(e,t,n,r,i,a,o){this.mainSurface.drawText(e,t,n,r,i,a,o)}printImage(e,t,n,r,i,a,o){this.mainSurface.drawImage(e,t,n,r,i,a,o)}printToModeline(e,t,n,r,i,a){this.modelineSurface&&this.modelineSurface.drawText(e,t,n,r,i,null,a)}touch(e){this.mainSurface.touch(),this.modelineSurface&&(this.modelineSurface.touch(),e?this.modelineSurface.activate():this.modelineSurface.deactivate())}makeSurface(e,t){switch(e){case`html`:return this.makeHTMLSurface(t);case`editor`:return this.makeEditorSurface();default:console.error(`unknown type: ${e}`)}}makeHTMLSurface(e){return new HTMLSurface({editor:this.editor,pixelX:this.pixelX,pixelY:this.pixelY,pixelWidth:this.pixelWidth,pixelHeight:this.pixelHeight,styles:getViewStyle(this.kind,this.option),option:this.option,isFloating:this.kind===`floating`,border:this.border,html:e})}makeEditorSurface(){let e=this.borderShape===`left-border`?0:this.border,t=this.kind===`floating`;return new CanvasSurface({option:this.editor.option,pixelX:this.pixelX,pixelY:this.pixelY,pixelWidth:this.pixelWidth,pixelHeight:this.pixelHeight,styles:getViewStyle(this.kind,this.option),editor:this.editor,border:e,isFloating:t,view:this,cssClassName:t&&e?`lem-editor__floating-window--bordered`:null})}makeModelineSurface(){let e=new CanvasSurface({option:this.editor.option,pixelX:this.pixelX,pixelY:this.pixelY+this.pixelHeight,pixelWidth:this.pixelWidth,pixelHeight:this.option.fontHeight,editor:this.editor,view:this,styles:{zIndex:zindex(`modeline`)},cssClassName:`lem-editor__mode-line`});return addMouseEventListeners({dom:e.mainDOM,editor:this.editor,isDraggable:!0,draggableStyle:`row-resize`}),e}changeToHTMLContent(e){this.mainSurface.constructor.name===`HTMLSurface`?this.mainSurface.update(e):(this.mainSurface.delete(),this.mainSurface=this.makeHTMLSurface(e))}changeToEditorContent(){this.mainSurface.delete(),this.mainSurface=this.makeEditorSurface()}evalIn(e){return this.mainSurface.evalIn(e)}};function isPasteKeyEvent(e){return isMacOS()?e.metaKey&&e.key===`v`:e.ctrlKey&&e.shiftKey&&e.key===`V`}var Input=class{constructor(e){let t=e.option;this.editor=e,this.composition=!1,this.ignoreKeydownAfterCompositionend=!1,this.span=document.createElement(`span`),this.span.style.color=t.foreground,this.span.style.backgroundColor=t.background,this.span.style.position=`absolute`,this.span.style.zIndex=1e6,this.span.style.top=`0`,this.span.style.left=`0`,this.span.style.font=t.font,this.input=document.createElement(`input`),this.input.style.backgroundColor=`transparent`,this.input.style.color=`transparent`,this.input.style.width=`0`,this.input.style.padding=`0`,this.input.style.margin=`0`,this.input.style.border=`none`,this.input.style.position=`absolute`,this.input.style.zIndex=`-10`,this.input.style.top=`0`,this.input.style.left=`0`,this.input.style.font=t.font,this.input.addEventListener(`blur`,e=>{this.input.focus()}),this.input.addEventListener(`input`,e=>{this.composition===!1&&(this.input.value=``,this.span.innerHTML=``,this.input.style.width=`0`,isMacOS()||this.editor.emitInputString(e.data))}),this.input.addEventListener(`paste`,async e=>{e.preventDefault();let t=e.clipboardData||window.Clipboard.data,n=t?.getData(`text`)??t?.getData(`text/plain`);if(n&&n.length>0){this.editor.emitInputString(n);return}try{if(navigator.clipboard?.readText){let e=await navigator.clipboard.readText();if(e&&e.length>0){this.editor.emitInputString(e);return}}}catch(e){console.warn(`clipboard.readText() failed:`,e)}alert(`Paste failed (permission/environment restriction`)}),this.input.addEventListener(`keydown`,e=>{if(!isPasteKeyEvent(e)&&!(e.isComposing||this.composition)&&e.key!==`Process`){if(this.ignoreKeydownAfterCompositionend&&(isSafari||isMacOS())){e.preventDefault(),this.ignoreKeydownAfterCompositionend=!1;return}if(!(!isMacOS()&&!e.ctrlKey&&!e.altKey&&e.key.length===1)&&(e.preventDefault(),e.isComposing!==!0&&e.code!==``))return setTimeout(()=>{this.composition||(this.editor.emitInput(e),this.input.value=``)},0),!1}}),this.input.addEventListener(`compositionstart`,e=>{this.composition=!0,this.span.innerHTML=this.input.value,this.input.style.width=this.span.offsetWidth+`px`}),this.input.addEventListener(`compositionupdate`,e=>{this.span.innerHTML=e.data,this.input.style.width=this.span.offsetWidth+`px`}),this.input.addEventListener(`compositionend`,e=>{this.composition=!1,this.editor.emitInputString(this.input.value),this.input.value=``,this.span.innerHTML=this.input.value,this.input.style.width=`0`,this.ignoreKeydownAfterCompositionend=!0}),document.body.appendChild(this.input),document.body.appendChild(this.span),this.input.focus()}finalize(){document.body.removeChild(this.input),document.body.removeChild(this.span)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.span.style.top=r+t+`px`,this.span.style.left=n+e+`px`,this.input.style.top=this.span.offsetTop+`px`,this.input.style.left=this.span.offsetLeft+`px`}updateForeground(e){this.span.style.color=e}updateBackground(e){this.span.style.backgroundColor=e}},MessageTable=class{constructor(){this.map=new Map}register(e,t){for(let n in t){let r=t[n];this.map.set(n,r),e.on(n,r)}}get(e){return this.map.get(e)}};function getDisplayRectangleDefault(){return[0,0,window.innerWidth,window.innerHeight]}var Editor=class{constructor({getDisplayRectangle:e=getDisplayRectangleDefault,fontName:t,fontSize:n,url:r,onExit:i,onClosed:a}){this.getDisplayRectangle=e,this.option=new Option({fontName:t,fontSize:n}),this.onExit=i,this.input=new Input(this),this.cursors=new Map,this.cursorOverlay=document.createElement(`div`),this.cursorOverlay.className=`lem-cursor`,this.cursorOverlay.style.width=this.option.fontWidth+`px`,this.cursorOverlay.style.height=this.option.fontHeight+`px`,this.cursorOverlay.style.backgroundColor=`#ffffff`,this.cursorType=`box`,this.viewMap=new Map,this.jsonrpc=new JSONRPC(r,{onClosed:()=>{a()}}),this.messageTable=new MessageTable,this.messageTable.register(this.jsonrpc,{"update-foreground":this.updateForeground.bind(this),"update-background":this.updateBackground.bind(this),"make-view":this.makeView.bind(this),"delete-view":this.deleteView.bind(this),"resize-view":this.resize.bind(this),"move-view":this.move.bind(this),"redraw-view-after":this.redrawViewAfter.bind(this),clear:this.clear.bind(this),"clear-eol":this.clearEol.bind(this),"clear-eob":this.clearEob.bind(this),put:this.put.bind(this),"put-image":this.putImage.bind(this),"modeline-put":this.modelinePut.bind(this),"update-display":this.updateDisplay.bind(this),"move-cursor":this.moveCursor.bind(this),"change-view":this.changeView.bind(this),"resize-display":this.resizeDisplay.bind(this),bulk:this.bulk.bind(this),exit:this.exitEditor.bind(this),"get-clipboard-text":this.getClipboardText.bind(this),"set-clipboard-text":this.setClipboardText.bind(this),"js-eval":this.jsEval.bind(this),"set-font":this.setFont.bind(this),"get-font":this.getFont.bind(this),"get-display-size":this.getDisplaySize.bind(this),"load-css":this.loadCSS.bind(this),"update-cursor-shape":this.updateCursorShape.bind(this)}),this.login(),this.boundedHandleResize=this.handleResize.bind(this),this.focusHiddenInput=this.focusHiddenInput.bind(this)}init(){window.addEventListener(`resize`,this.boundedHandleResize),document.getElementsByTagName(`html`)[0].style[`background-color`]=`#333`,getLemEditorElement().appendChild(this.cursorOverlay)}finalize(){window.removeEventListener(`resize`,this.boundedHandleResize),this.input.finalize(),this.cursorOverlay.parentNode&&this.cursorOverlay.parentNode.removeChild(this.cursorOverlay)}closeConnection(){this.jsonrpc.close()}emitInput(e){let t=convertKeyEvent(e);if(t){if(t.key===`]`&&t.ctrl&&!t.meta&&!t.super&&!t.shift){this.jsonrpc.notify(`input`,{kind:`abort`});return}t.key!==`Unidentified`&&this.jsonrpc.notify(`input`,{kind:`key`,value:t})}}emitInputString(e){e?this.jsonrpc.notify(`input`,{kind:`input-string`,value:e}):console.error(`unexpected argument`,e)}redrawParams(){return{size:this.getDisplaySize(),fontWidth:this.option.fontWidth,fontHeight:this.option.fontHeight,fontAscent:this.option.fontAscent}}handleResize(e){this.jsonrpc.notify(`redraw`,this.redrawParams())}focusHiddenInput(){let e=this.input?.input;if(e){try{window.focus()}catch{}requestAnimationFrame(()=>{setTimeout(()=>{e.focus({preventScroll:!0})},0)})}}sendNotification(e,t){this.jsonrpc.notify(e,t)}request(e,t,n){this.jsonrpc.request(e,t,n)}getDisplaySize(){let[e,t,n,r]=this.getDisplayRectangle();return{width:Math.floor(n/this.option.fontWidth),height:Math.floor(r/this.option.fontHeight)}}callMessage(e,t){this.messageTable.get(e)(t)}findViewById(e){return this.viewMap.get(e)}login(){this.jsonrpc.request(`login`,{size:this.getDisplaySize(),foreground:this.option.foreground,background:this.option.background,fontWidth:this.option.fontWidth,fontHeight:this.option.fontHeight,fontAscent:this.option.fontAscent},e=>{if(this.updateForeground(e.foreground),this.updateBackground(e.background),e.views)for(let t of e.views)this.makeView(t);this.jsonrpc.notify(`redraw`,this.redrawParams())})}updateForeground(e){e!=null&&(this.option.foreground=e,this.input.updateForeground(e))}updateBackground(e){if(e==null)return;this.option.background=e,this.input.updateBackground(e);let t=getLemEditorElement();t.style.backgroundColor=e}makeView({id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,use_modeline:l,kind:u,type:d,content:f,border:p,border_shape:m}){let h=new View({option:this.option,id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,useModeline:l,kind:u,type:d,content:f,border:p,borderShape:m,editor:this});this.viewMap.set(e,h)}deleteView({viewInfo:{id:e}}){this.findViewById(e).delete(),this.viewMap.delete(e)}resize({viewInfo:{id:e},width:t,height:n,pixelWidth:r,pixelHeight:i}){let a=this.findViewById(e);a?a.resize(t,n,r,i):console.warn(`resize: view not found for id ${e}`)}move({viewInfo:{id:e},x:t,y:n,pixelX:r,pixelY:i}){let a=this.findViewById(e);a?a.move(t,n,r,i):console.warn(`move: view not found for id ${e}`)}redrawViewAfter({viewInfo:{id:e},isActive:t}){this.findViewById(e).touch(t)}clear({viewInfo:{id:e}}){this.findViewById(e).clear()}clearEol({viewInfo:{id:e},x:t,y:n,height:r}){this.findViewById(e).clearEol(t,n,r)}clearEob({viewInfo:{id:e},x:t,y:n}){this.findViewById(e).clearEob(t,n)}put({viewInfo:{id:e},x:t,y:n,text:r,textWidth:i,attribute:a,font:o,height:s}){this.findViewById(e).print(t,n,r,i,a,o,s)}putImage({viewInfo:{id:e},x:t,y:n,pixelWidth:r,pixelHeight:i,clipWidth:a,clipHeight:o,url:s}){this.findViewById(e).printImage(t,n,r,i,a,o,s)}modelinePut({viewInfo:{id:e},x:t,y:n,text:r,textWidth:i,attribute:a,height:o}){this.findViewById(e).printToModeline(t,n,r,i,a,o)}updateDisplay(){}moveCursor({viewInfo:{id:e},x:t,y:n,color:r,cursorText:i,cursorForeground:a}){let o=this.findViewById(e),[s,c]=this.getDisplayRectangle(),l=o.pixelX+t,u=o.pixelY+n;this.input.move(l,u);let d=r||this.option.foreground,f=a||this.option.background,p=this.cursorOverlay;switch(this.cursorType){case`bar`:p.style.left=s+l+`px`,p.style.top=c+u+`px`,p.style.width=`2px`,p.style.height=this.option.fontHeight+`px`,p.style.backgroundColor=d,p.textContent=``,p.style.color=``,p.style.font=``,p.style.paddingTop=``;break;case`underline`:p.style.left=s+l+`px`,p.style.top=c+u+this.option.fontHeight-2+`px`,p.style.width=this.option.fontWidth+`px`,p.style.height=`2px`,p.style.backgroundColor=d,p.textContent=``,p.style.color=``,p.style.font=``,p.style.paddingTop=``;break;default:p.style.left=s+l+`px`,p.style.top=c+u+`px`,p.style.width=this.option.fontWidth+`px`,p.style.height=this.option.fontHeight+`px`,p.style.backgroundColor=d,p.style.font=this.option.font,p.style.paddingTop=textOffsetY+`px`,p.textContent=i||``,p.style.color=f;break}p.style.animation=`none`,p.offsetHeight,p.style.animation=``}updateCursorShape({cursorType:e}){this.cursorType=e||`box`}changeView({viewInfo:{id:e},type:t,content:n}){let r=this.findViewById(e);switch(t){case`html`:r.changeToHTMLContent(n);break;case`editor`:r.changeToEditorContent();break}}resizeDisplay({width:e,height:t}){let n=getLemEditorElement();n.style.width=Math.floor(e*this.option.fontWidth)+`px`,n.style.height=Math.floor(t*this.option.fontHeight)+`px`}bulk(e){for(let{method:t,argument:n}of e)this.callMessage(t,n)}exitEditor(){this.onExit&&this.onExit()}getClipboardText(){navigator.clipboard?.readText().then(e=>{this.jsonrpc.notify(`got-clipboard-text`,{text:e})})}setClipboardText({text:e}){navigator.clipboard&&navigator.clipboard.writeText(e)}jsEval({viewInfo:{id:e},code:t}){let n=this.findViewById(e).evalIn(t);return n&&n.toString()}setFont({fontName:e,fontSize:t}){this.option.setFont(e||this.option.fontName,t||this.option.fontSize),this.cursorOverlay.style.width=this.option.fontWidth+`px`,this.cursorOverlay.style.height=this.option.fontHeight+`px`,this.jsonrpc.notify(`redraw`,this.redrawParams())}getFont(){return{name:this.option.fontName,size:this.option.fontSize}}loadCSS({content:e}){let t=document.createElement(`style`);t.textContent=e,document.head.appendChild(t)}notifyToServer(e,t){this.jsonrpc.notify(`invoke`,{method:e,args:t})}},canvas=document.querySelector(`#editor`);async function main(){await Promise.all([document.fonts.load(`19px file-icons`),document.fonts.load(`19px AllTheIcons`),document.fonts.load(`19px fontawesome`),document.fonts.load(`19px material-design-icons`),document.fonts.load(`19px octicons`)]),await document.fonts.ready;let e=new Editor({canvas,fontName:`Monospace`,fontSize:18,url:`${window.location.protocol===`https:`?`wss`:`ws`}://${window.location.hostname}:${window.location.port}`,onExit:null,onClosed:null});window.addEventListener(`message`,t=>{t.data.type===`invoke-lem`&&e.notifyToServer(t.data.method,t.data.args)}),e.init()}main(); \ No newline at end of file diff --git a/frontends/server/frontend/editor.js b/frontends/server/frontend/editor.js index baa0e2af4..3ad4569fe 100644 --- a/frontends/server/frontend/editor.js +++ b/frontends/server/frontend/editor.js @@ -346,7 +346,7 @@ class BaseSurface { // drawing coordinates are relative to the surface's own top-left corner. drawBlock(x, y, width, height, color) { } drawText(x, y, text, textWidth, attribute, font, height) { } - drawImage(x, y, width, height, url) { } + drawImage(x, y, width, height, clipWidth, clipHeight, url) { } clearImages(yStart, yEnd) { } clearAllImages() { } @@ -490,9 +490,9 @@ class CanvasSurface extends BaseSurface { imageBaseLeft() { return parseFloat(this.mainDOM.style.left) || 0; } imageBaseTop() { return parseFloat(this.mainDOM.style.top) || 0; } - drawImage(x, y, width, height, url) { + drawImage(x, y, width, height, clipWidth, clipHeight, url) { if (!this.imageEls) - // mapping "x,y" to { el, url, x, y, width, height } + // mapping "x,y" to { el, url, x, y, width, height, clipWidth, clipHeight } this.imageEls = new Map(); const key = x + ',' + y; let entry = this.imageEls.get(key); @@ -516,6 +516,8 @@ class CanvasSurface extends BaseSurface { entry.y = y; entry.width = width; entry.height = height; + entry.clipWidth = clipWidth; + entry.clipHeight = clipHeight; this.positionImage(entry); } @@ -524,6 +526,15 @@ class CanvasSurface extends BaseSurface { entry.el.style.top = (this.imageBaseTop() + entry.y) + 'px'; entry.el.style.width = entry.width + 'px'; entry.el.style.height = entry.height + 'px'; + // show only the part the server said is visible. the element keeps its full size and + // clip-path hides the rest, since shrinking it would squash the picture. + const clipRight = entry.clipWidth == null + ? 0 : Math.max(0, entry.width - entry.clipWidth); + const clipBottom = entry.clipHeight == null + ? 0 : Math.max(0, entry.height - entry.clipHeight); + entry.el.style.clipPath = (clipRight > 0 || clipBottom > 0) + ? `inset(0px ${clipRight}px ${clipBottom}px 0px)` + : ''; } // remove image elements whose vertical span intersects [yStart, yEnd). @@ -869,8 +880,8 @@ class View { ); } - printImage(x, y, pixelWidth, pixelHeight, url) { - this.mainSurface.drawImage(x, y, pixelWidth, pixelHeight, url); + printImage(x, y, pixelWidth, pixelHeight, clipWidth, clipHeight, url) { + this.mainSurface.drawImage(x, y, pixelWidth, pixelHeight, clipWidth, clipHeight, url); } printToModeline(x, y, text, textWidth, attribute, height) { @@ -1455,9 +1466,9 @@ export class Editor { view.print(x, y, text, textWidth, attribute, font, height); } - putImage({ viewInfo: { id }, x, y, pixelWidth, pixelHeight, url }) { + putImage({ viewInfo: { id }, x, y, pixelWidth, pixelHeight, clipWidth, clipHeight, url }) { const view = this.findViewById(id); - view.printImage(x, y, pixelWidth, pixelHeight, url); + view.printImage(x, y, pixelWidth, pixelHeight, clipWidth, clipHeight, url); } modelinePut({ viewInfo: { id }, x, y, text, textWidth, attribute, height }) { diff --git a/frontends/server/main.lisp b/frontends/server/main.lisp index 848c35b18..8ee0c365c 100644 --- a/frontends/server/main.lisp +++ b/frontends/server/main.lisp @@ -739,8 +739,15 @@ a string already carrying a data:/https: URL is passed through unchanged." (let ((url (image-object-url object))) (when url (with-error-handler () - (let ((pw (display:image-draw-width jsonrpc object)) - (ph (display:image-draw-height jsonrpc object))) + (let* ((pw (display:image-draw-width jsonrpc object)) + (ph (display:image-draw-height jsonrpc object)) + ;; how much may appear: the crop the layout applied, and the room left in the view. + ;; an image is a DOM element over the view, not pixels in it, so nothing clips it + ;; for us. + (clip-width (min pw + (max 0 (- (view-px-width view) x)) + (or (display:image-object-visible-width object) pw))) + (clip-height (min ph (max 0 (- (view-px-height view) y))))) (notify* jsonrpc "put-image" (hash "viewInfo" (view-id-hash view) @@ -748,6 +755,9 @@ a string already carrying a data:/https: URL is passed through unchanged." "y" y "pixelWidth" pw "pixelHeight" ph + ;; the visible part, from the image's top-left + "clipWidth" clip-width + "clipHeight" clip-height "url" url))))))) (defun draw-row (jsonrpc view row) diff --git a/src/display/physical-line.lisp b/src/display/physical-line.lisp index 252d0bd9d..ddbc9ee5f 100644 --- a/src/display/physical-line.lisp +++ b/src/display/physical-line.lisp @@ -60,7 +60,24 @@ ((image :initarg :image :reader image-object-image) (width :initarg :width :reader image-object-width) (height :initarg :height :reader image-object-height) - (attribute :initarg :attribute :reader image-object-attribute))) + (attribute :initarg :attribute :reader image-object-attribute) + ;; how much of the width may be shown, or NIL for all of it. see `crop-image-object'. + (visible-width :initarg :visible-width + :initform nil + :reader image-object-visible-width))) + +(defun image-object-ascent (object height) + "How much of OBJECT's image, drawn HEIGHT tall, sits above the text baseline. +Taken from the object's `:ascent' attribute: a percentage of HEIGHT, 50 by default. `:center' +instead puts the middle of the image on the middle of a line of text." + ;; attribute-value* rather than attribute-value: an object's attribute may be a name, as + ;; `attribute-image' above it allows. + (let ((ascent (or (attribute-value* (image-object-attribute object) :ascent) + 50))) + (if (eq ascent :center) + (multiple-value-bind (text-ascent text-height) (text-row-metrics) + (round (+ (/ height 2) (- text-ascent (/ text-height 2))))) + (round (* height (/ (max 0 (min 100 ascent)) 100)))))) (defun image-draw-width (implementation object) "Pixel width OBJECT's image is drawn at. @@ -90,7 +107,11 @@ frontend can report one (`lem-if:image-natural-size'), otherwise one cell wide." 0) (defmethod lem-if:object-width (implementation (drawing-object image-object)) - (image-draw-width implementation drawing-object)) + ;; a cropped image occupies only what it was cropped to, see `crop-image-object'. + (let ((width (image-draw-width implementation drawing-object))) + (alexandria:if-let ((visible (image-object-visible-width drawing-object))) + (min width visible) + width))) (defmethod lem-if:object-height (implementation (drawing-object drawing-object)) (lem-if:cell-height implementation)) @@ -106,6 +127,20 @@ frontend can report one (`lem-if:image-natural-size'), otherwise one cell wide." (declare (ignore cell-width cell-height)) (or cell-ascent (lem-if:object-height implementation drawing-object)))) +(defmethod lem-if:object-ascent (implementation (drawing-object image-object)) + (image-object-ascent drawing-object (lem-if:object-height implementation drawing-object))) + +(defun crop-image-object (object width) + "A copy of OBJECT allowed to occupy only WIDTH, in the units `object-width' counts in." + (make-instance 'image-object + :image (image-object-image object) + :width (image-object-width object) + :height (image-object-height object) + :attribute (image-object-attribute object) + :visible-width (alexandria:if-let ((visible (image-object-visible-width object))) + (min width visible) + width))) + (defmethod cursor-object-p (drawing-object) nil) @@ -149,7 +184,10 @@ frontend can report one (`lem-if:image-natural-size'), otherwise one cell wide." (defmethod drawing-object-equal ((drawing-object-1 image-object) (drawing-object-2 image-object)) (and (eq (image-object-image drawing-object-1) (image-object-image drawing-object-2)) (equal (image-object-width drawing-object-1) (image-object-width drawing-object-2)) - (equal (image-object-height drawing-object-1) (image-object-height drawing-object-2)))) + (equal (image-object-height drawing-object-1) (image-object-height drawing-object-2)) + ;; a differently cropped image draws differently, so the cached row must not be reused + (equal (image-object-visible-width drawing-object-1) + (image-object-visible-width drawing-object-2)))) (defgeneric drawing-object-mergable-p (drawing-object-1 drawing-object-2)) @@ -357,7 +395,16 @@ frontend can report one (`lem-if:image-natural-size'), otherwise one cell wide." :and physical-line-objects := '() :for object := (pop objects) :while object - :do (cond ((and (typep object 'text-object) + :do (cond ((and (typep object 'image-object) + (< (- view-width total-width) (object-width object))) + ;; an image cannot be broken in half the way a text run is, so it moves whole + ;; to the next row. one that does not fit even a row of its own is cropped. + (if (null physical-line-objects) + (push (crop-image-object object (- view-width total-width)) + physical-line-objects) + (push object objects)) + (return (values (nreverse physical-line-objects) objects))) + ((and (typep object 'text-object) (<= view-width (+ total-width (object-width object)))) (cond ((< 1 (length (text-object-string object))) (setf objects (nconc (explode-object object) objects))) @@ -807,6 +854,10 @@ creating zero temporary letter-objects." (text-object-attribute object) (text-object-type object)) result)))) + ;; an image crossing the right edge is cut down to what fits. the left edge is not, since + ;; that needs an offset into the image and an image-object carries only a visible width. + ((and (typep object 'image-object) (< x end-x) (< end-x obj-end)) + (push (crop-image-object object (- end-x x)) result)) ;; Non-text objects straddling boundary - include (t (push object result))) (incf x w))) diff --git a/src/internal-packages.lisp b/src/internal-packages.lisp index 2c21009cc..2f33ea867 100644 --- a/src/internal-packages.lisp +++ b/src/internal-packages.lisp @@ -18,6 +18,8 @@ :image-object-height :image-object-image :image-object-width + :image-object-visible-width + :image-object-ascent :image-draw-width :image-draw-height :object-ascent From 40db435d54684d22183ac77a549f18ee9e6e3054 Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Sat, 1 Aug 2026 16:41:53 +0300 Subject: [PATCH 17/26] let virtual text contain newlines also fixes a pre-existing bound in the wrapping path that compared y, in the frontend's units, against window-height, a row count. --- src/display/logical-line.lisp | 21 +++-- src/display/physical-line.lisp | 160 +++++++++++++++++++++++---------- src/internal-packages.lisp | 2 +- 3 files changed, 129 insertions(+), 54 deletions(-) diff --git a/src/display/logical-line.lisp b/src/display/logical-line.lisp index b6f4fbffc..148eebb50 100644 --- a/src/display/logical-line.lisp +++ b/src/display/logical-line.lisp @@ -440,6 +440,10 @@ several folds that each hide arbitrary character ranges across multiple buffer l attribute offset) +;; a newline inside virtual text (an overlay's :before-string / :after-string): ends the screen row +;; without touching the buffer line. +(defstruct line-break-item) + (defmethod item-string ((item string-with-attribute-item)) (string-with-attribute-item-string item)) @@ -519,11 +523,18 @@ VIRTUAL-ITEMS arrive in draw order (from `create-logical-line')." (flet ((add-virtuals-at (pos) (loop :while (and pending (= (virtual-item-charpos (first pending)) pos)) :do (let ((vi (pop pending))) - (setf items (add-or-merge-item - (make-string-with-attribute-item - :string (virtual-item-string vi) - :attribute (virtual-item-attribute vi)) - items)))))) + ;; a newline ends the screen row rather than being drawn, so the segments + ;; around it become items with a break between them. + (loop :for segment :in (uiop:split-string (virtual-item-string vi) + :separator '(#\newline)) + :for firstp := t :then nil + :do (unless firstp + (setf items (cons (make-line-break-item) items))) + (setf items (add-or-merge-item + (make-string-with-attribute-item + :string segment + :attribute (virtual-item-attribute vi)) + items))))))) ;; walk segments between break positions, injecting virtual items at each boundary (loop :for (pos . rest) :on positions :while rest diff --git a/src/display/physical-line.lisp b/src/display/physical-line.lisp index ddbc9ee5f..488cb60ba 100644 --- a/src/display/physical-line.lisp +++ b/src/display/physical-line.lisp @@ -19,6 +19,10 @@ (defclass void-object (drawing-object) ()) +;; from a `line-break-item', consumed while splitting a line into rows, so it never reaches a +;; frontend. +(defclass line-break-object (void-object) ()) + (defclass text-object (drawing-object) ((surface :initarg :surface :initform nil :accessor text-object-surface) (string :initarg :string :reader text-object-string) @@ -339,6 +343,8 @@ frontend can report one (`lem-if:image-natural-size'), otherwise one cell wide." :true-cursor-p (eol-cursor-item-true-cursor-p item)))) ((typep item 'extend-to-eol-item) (list (make-instance 'extend-to-eol-object :color (extend-to-eol-item-color item)))) + ((typep item 'line-break-item) + (list (make-instance 'line-break-object))) ((typep item 'line-end-item) (let ((string (line-end-item-text item)) (attribute (line-end-item-attribute item))) @@ -378,6 +384,11 @@ frontend can report one (`lem-if:image-natural-size'), otherwise one cell wide." (char-type character))) (defun separate-objects-by-width (objects view-width buffer) + "Take one screen row's worth of OBJECTS, at most VIEW-WIDTH wide. +Returns (values ROW REST WHY): the row's objects, those left for the rows after it, and why the row +ended. :WRAPPED for running out of width, :LINE-BREAK for a newline inside virtual text, :END for +the end of the line. Only after :WRAPPED does the next row show more of the buffer's text, which is +what turning a row back into a buffer position needs to know." (flet ((explode-object (text-object) (check-type text-object text-object) (let* ((string (text-object-string text-object)) @@ -395,7 +406,11 @@ frontend can report one (`lem-if:image-natural-size'), otherwise one cell wide." :and physical-line-objects := '() :for object := (pop objects) :while object - :do (cond ((and (typep object 'image-object) + :do (cond ((typep object 'line-break-object) + ;; a newline in virtual text, not a row that ran out of width, so no wrap + ;; marker and not :wrapped. + (return (values (nreverse physical-line-objects) objects :line-break))) + ((and (typep object 'image-object) (< (- view-width total-width) (object-width object))) ;; an image cannot be broken in half the way a text run is, so it moves whole ;; to the next row. one that does not fit even a row of its own is cropped. @@ -403,7 +418,7 @@ frontend can report one (`lem-if:image-natural-size'), otherwise one cell wide." (push (crop-image-object object (- view-width total-width)) physical-line-objects) (push object objects)) - (return (values (nreverse physical-line-objects) objects))) + (return (values (nreverse physical-line-objects) objects :wrapped))) ((and (typep object 'text-object) (<= view-width (+ total-width (object-width object)))) (cond ((< 1 (length (text-object-string object))) @@ -413,11 +428,26 @@ frontend can report one (`lem-if:image-natural-size'), otherwise one cell wide." (push (make-letter-object wrap-line-character wrap-line-attribute) physical-line-objects) - (return (values (nreverse physical-line-objects) objects))))) + (return (values (nreverse physical-line-objects) + objects + :wrapped))))) (t (incf total-width (object-width object)) (push object physical-line-objects))) - :finally (return (nreverse physical-line-objects)))))) + :finally (return (values (nreverse physical-line-objects) nil :end)))))) + +(defun split-objects-at-line-breaks (objects) + "Split OBJECTS into one list per screen row, consuming each `line-break-object'. +Returns a list of lists, never empty: a line with no breaks in it gives one row." + (if (notany (lambda (object) (typep object 'line-break-object)) objects) + (list objects) + (let (rows row) + (dolist (object objects) + (if (typep object 'line-break-object) + (progn (push (nreverse row) rows) + (setf row nil)) + (push object row))) + (nreverse (cons (nreverse row) rows))))) (defun render-row (view row) (lem-if:render-row (implementation) view row)) @@ -738,31 +768,43 @@ over the top-level spine and tolerant of improper (dotted) lists." left-side-width)) (defun check-line-fingerprint (window y fingerprint) - "Check if the fingerprint for line at Y matches. Returns cached height or NIL." + "Check if the fingerprint for line at Y matches. Returns the cached list of row heights, or NIL. +One entry per row, since a line can draw several without wrapping." (let ((cache (line-fingerprint-cache window))) (multiple-value-bind (entry found) (gethash y cache) (when (and found (eql (car entry) fingerprint)) (cdr entry))))) (defun evict-line-fingerprint-shadow (cache y height) - "Remove entries in CACHE for the rows a HEIGHT-tall line at Y covers." - (loop :for row :from (1+ y) :below (+ y height) - :do (remhash row cache))) - -(defun update-line-fingerprint (window y fingerprint height) - "Store the fingerprint and height for line at Y, and drop the rows it covers." + "Remove entries in CACHE for the rows a HEIGHT-tall line at Y covers. +Loops over the cache's keys, not over every Y in the range: on a pixel frontend that range is one +iteration per pixel, against a cache holding one entry per line drawn." + (let ((end (+ y height)) + (stale)) + (loop :for row :being :the :hash-keys :of cache + :when (and (< y row) (< row end)) + :do (push row stale)) + (dolist (row stale) + (remhash row cache)))) + +(defun update-line-fingerprint (window y fingerprint heights) + "Store the fingerprint and HEIGHTS for line at Y, and drop the rows it covers. +HEIGHTS is one entry per screen row the line drew, as the redraw functions collect them." (let ((cache (line-fingerprint-cache window))) - (setf (gethash y cache) (cons fingerprint height)) - (evict-line-fingerprint-shadow cache y height))) + (setf (gethash y cache) (cons fingerprint heights)) + (evict-line-fingerprint-shadow cache y (reduce #'+ heights :initial-value 0)))) + +(defun left-side-character-count (left-side-objects) + (loop :for obj :in left-side-objects + :when (typep obj 'text-object) + :sum (length (text-object-string obj)))) (defun redraw-logical-line-when-line-wrapping (window y logical-line left-side-objects left-side-width) - (let* ((left-side-characters (loop :for obj :in left-side-objects - :when (typep obj 'text-object) - :sum (length (text-object-string obj))))) + (let* ((left-side-characters (left-side-character-count left-side-objects))) (multiple-value-bind (first-line-objects rest-line-objects) (separate-objects-by-width (create-drawing-objects logical-line) (- (window-view-width window) left-side-width) @@ -773,22 +815,25 @@ over the top-level spine and tolerant of improper (dotted) lists." *active-modes* left-side-width left-side-characters))))) - (let ((total-height 0) + (let ((heights '()) (objects first-line-objects)) (loop - (unless objects (return)) + ;; an empty row is still a row when more of the line follows, which is what a break at + ;; the very start of the virtual text asks for. + (unless (or objects rest-line-objects) (return)) (let* ((all-objects (append left-side-objects objects)) (height (render-row-with-caching window y all-objects))) (incf y height) (setq left-side-objects wrapped-left-side-objects) - (incf total-height height) - (unless (< y (window-height window)) + (push height heights) + ;; y is in the frontend's units, so the bound must be too, not the row count. + (unless (< y (window-view-height window)) (return))) (setf (values objects rest-line-objects) (separate-objects-by-width rest-line-objects (- (window-view-width window) left-side-width) (window-buffer window)))) - total-height))))) + (nreverse heights)))))) (defun find-cursor-object (objects) (loop :for object :in objects @@ -873,30 +918,47 @@ creating zero temporary letter-objects." scroll-before left-side-width))) ;; Early exit if line content unchanged - (alexandria:when-let ((cached-height (check-line-fingerprint window y fingerprint))) - (return-from redraw-logical-line-when-horizontal-scroll cached-height)) - (let ((objects (create-drawing-objects logical-line)) - (height 0)) - (multiple-value-bind (cursor-object cursor-x) - (find-cursor-object objects) - (when cursor-object - (let ((width (- (window-view-width window) left-side-width))) - (cond ((< cursor-x (horizontal-scroll-start window)) - (setf (horizontal-scroll-start window) cursor-x)) - ((< (+ (horizontal-scroll-start window) - width) - (+ cursor-x (object-width cursor-object))) - (setf (horizontal-scroll-start window) - (+ (- cursor-x width) - (object-width cursor-object))))))) - (setf objects - (clip-objects-to-display-range - objects - (horizontal-scroll-start window) - (+ (horizontal-scroll-start window) - (window-view-width window)))) - (setf height - (render-row-with-caching window y (append left-side-objects objects)))) + (alexandria:when-let ((cached-heights (check-line-fingerprint window y fingerprint))) + (return-from redraw-logical-line-when-horizontal-scroll cached-heights)) + (let* ((rows (split-objects-at-line-breaks (create-drawing-objects logical-line))) + (left-side-characters (left-side-character-count left-side-objects)) + (heights '()) + (total-height 0)) + ;; the cursor is on one of the rows, scrolling follows it there. + (dolist (row-objects rows) + (multiple-value-bind (cursor-object cursor-x) + (find-cursor-object row-objects) + (when cursor-object + (let ((width (- (window-view-width window) left-side-width))) + (cond ((< cursor-x (horizontal-scroll-start window)) + (setf (horizontal-scroll-start window) cursor-x)) + ((< (+ (horizontal-scroll-start window) + width) + (+ cursor-x (object-width cursor-object))) + (setf (horizontal-scroll-start window) + (+ (- cursor-x width) + (object-width cursor-object))))))))) + (let ((wrapped-left-side-objects + (when (rest rows) + (copy-list (compute-wrap-left-area-content *active-modes* + left-side-width + left-side-characters))))) + (loop :for row-objects :in rows + ;; only the first row carries the real left area, the rest get the wrap padding. + :for side := left-side-objects :then wrapped-left-side-objects + :do (let* ((clipped (clip-objects-to-display-range + row-objects + (horizontal-scroll-start window) + (+ (horizontal-scroll-start window) + (window-view-width window)))) + (height (render-row-with-caching window (+ y total-height) + (append side clipped)))) + (incf total-height height) + (push height heights)) + ;; y is in the frontend's units, as is the bound + (when (<= (window-view-height window) (+ y total-height)) + (return)))) + (setf heights (nreverse heights)) ;; Reuse fingerprint if scroll position didn't change; avoids redundant sxhash (update-line-fingerprint window y @@ -905,8 +967,8 @@ creating zero temporary letter-objects." (compute-line-fingerprint logical-line (horizontal-scroll-start window) left-side-width)) - height) - height))) + heights) + heights))) (defun redraw-lines (window) (let* ((*line-wrap* (variable-value 'line-wrap @@ -928,7 +990,9 @@ creating zero temporary letter-objects." (setf left-side-width (loop :for object :in left-side-objects :sum (object-width object))) - (incf y (funcall redraw-fn window y logical-line left-side-objects left-side-width)) + (dolist (row-height + (funcall redraw-fn window y logical-line left-side-objects left-side-width)) + (incf y row-height)) (unless (< y height) (return-from outer))))) (when (< y height) diff --git a/src/internal-packages.lisp b/src/internal-packages.lisp index 2f33ea867..c463751a4 100644 --- a/src/internal-packages.lisp +++ b/src/internal-packages.lisp @@ -842,9 +842,9 @@ :get-mouse-position :cell-width :cell-height + :cell-pixel-size :clear-to-end-of-window :js-eval - :cell-pixel-size :render-row :render-modeline-row :object-width From a136112baca9f56882c2080f84aadd127d445539 Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Sat, 1 Aug 2026 19:58:14 +0300 Subject: [PATCH 18/26] hit test the mouse against the rows actually drawn --- src/display/logical-line.lisp | 13 +++-- src/display/physical-line.lisp | 94 +++++++++++++++++++++++++--------- src/mouse.lisp | 38 +++++++++++--- 3 files changed, 113 insertions(+), 32 deletions(-) diff --git a/src/display/logical-line.lisp b/src/display/logical-line.lisp index 148eebb50..5c93079c1 100644 --- a/src/display/logical-line.lisp +++ b/src/display/logical-line.lisp @@ -629,12 +629,19 @@ VIRTUAL-ITEMS arrive in draw order (from `create-logical-line')." (*active-modes* active-modes)) (loop :for logical-line := (create-logical-line point overlays active-modes) :do (when logical-line - (funcall function logical-line)) + (funcall function logical-line point)) (loop (unless (line-offset point 1) (return-from call-do-logical-line)) (unless (line-continuation-p point) (return))))))) -(defmacro do-logical-line ((logical-line window) &body body) - `(call-do-logical-line ,window (lambda (,logical-line) ,@body))) +(defmacro do-logical-line ((logical-line window &optional point) &body body) + "Run BODY for each logical line of WINDOW, in draw order. +POINT, when named, is bound to the start of the line. It is one point reused for every line and +moved on to the next once BODY returns, so BODY must `copy-point' it to hold on to it." + (let ((point-var (or point (gensym "POINT")))) + `(call-do-logical-line ,window + (lambda (,logical-line ,point-var) + (declare (ignorable ,point-var)) + ,@body)))) diff --git a/src/display/physical-line.lisp b/src/display/physical-line.lisp index 488cb60ba..bc02215e2 100644 --- a/src/display/physical-line.lisp +++ b/src/display/physical-line.lisp @@ -767,9 +767,41 @@ over the top-level spine and tolerant of improper (dotted) lists." scroll-start left-side-width)) +(defstruct screen-row + "One drawn row of a window, recorded as it was drawn." + height + ;; which buffer line this row's logical line starts on. + line-number + ;; this row's index within its line. a break in virtual text starts a row without advancing it. + wrap-index) + +(defun window-screen-rows (window) + "Every screen row of WINDOW, top to bottom, as recorded while it was drawn." + (window-parameter window 'screen-rows)) + +(defun (setf window-screen-rows) (rows window) + (setf (window-parameter window 'screen-rows) rows)) + +(defun window-screen-row-index-at-y (window y) + "Index of the screen row Y falls in, counted from the top of WINDOW's view, or NIL when Y is past +the last row drawn or the window has not been drawn yet. Y is in the frontend's units. +Walks the rows because they are not all one height, so there is nothing to divide by." + (loop :with top := 0 + :for row :in (window-screen-rows window) + :for index :from 0 + :do (when (< y (+ top (screen-row-height row))) + (return index)) + (incf top (screen-row-height row)))) + +(defun window-screen-row-at-index (window index) + "WINDOW's screen row at INDEX, counted from the top of its view, or NIL if no row was drawn +there." + (nth index (window-screen-rows window))) + (defun check-line-fingerprint (window y fingerprint) - "Check if the fingerprint for line at Y matches. Returns the cached list of row heights, or NIL. -One entry per row, since a line can draw several without wrapping." + "Check if the fingerprint for line at Y matches. Returns the cached list of rows, or NIL. +One entry per row, so a line taken from the cache still contributes its rows to +`window-screen-rows'." (let ((cache (line-fingerprint-cache window))) (multiple-value-bind (entry found) (gethash y cache) (when (and found (eql (car entry) fingerprint)) @@ -787,12 +819,13 @@ iteration per pixel, against a cache holding one entry per line drawn." (dolist (row stale) (remhash row cache)))) -(defun update-line-fingerprint (window y fingerprint heights) - "Store the fingerprint and HEIGHTS for line at Y, and drop the rows it covers. -HEIGHTS is one entry per screen row the line drew, as the redraw functions collect them." +(defun update-line-fingerprint (window y fingerprint rows) + "Store the fingerprint and ROWS for line at Y, and drop the rows it covers. +ROWS is one (HEIGHT . WRAP-INDEX) per screen row the line drew, as the redraw functions collect +them." (let ((cache (line-fingerprint-cache window))) - (setf (gethash y cache) (cons fingerprint heights)) - (evict-line-fingerprint-shadow cache y (reduce #'+ heights :initial-value 0)))) + (setf (gethash y cache) (cons fingerprint rows)) + (evict-line-fingerprint-shadow cache y (reduce #'+ rows :key #'car :initial-value 0)))) (defun left-side-character-count (left-side-objects) (loop :for obj :in left-side-objects @@ -805,7 +838,7 @@ HEIGHTS is one entry per screen row the line drew, as the redraw functions colle left-side-objects left-side-width) (let* ((left-side-characters (left-side-character-count left-side-objects))) - (multiple-value-bind (first-line-objects rest-line-objects) + (multiple-value-bind (first-line-objects rest-line-objects why) (separate-objects-by-width (create-drawing-objects logical-line) (- (window-view-width window) left-side-width) (window-buffer window)) @@ -815,7 +848,8 @@ HEIGHTS is one entry per screen row the line drew, as the redraw functions colle *active-modes* left-side-width left-side-characters))))) - (let ((heights '()) + (let ((rows) + (wrap-index 0) (objects first-line-objects)) (loop ;; an empty row is still a row when more of the line follows, which is what a break at @@ -825,15 +859,18 @@ HEIGHTS is one entry per screen row the line drew, as the redraw functions colle (height (render-row-with-caching window y all-objects))) (incf y height) (setq left-side-objects wrapped-left-side-objects) - (push height heights) + (push (cons height wrap-index) rows) + ;; only running out of width advances the position, a virtual-text break does not. + (when (eq why :wrapped) + (incf wrap-index)) ;; y is in the frontend's units, so the bound must be too, not the row count. (unless (< y (window-view-height window)) (return))) - (setf (values objects rest-line-objects) + (setf (values objects rest-line-objects why) (separate-objects-by-width rest-line-objects (- (window-view-width window) left-side-width) (window-buffer window)))) - (nreverse heights)))))) + (nreverse rows)))))) (defun find-cursor-object (objects) (loop :for object :in objects @@ -918,11 +955,11 @@ creating zero temporary letter-objects." scroll-before left-side-width))) ;; Early exit if line content unchanged - (alexandria:when-let ((cached-heights (check-line-fingerprint window y fingerprint))) - (return-from redraw-logical-line-when-horizontal-scroll cached-heights)) + (alexandria:when-let ((cached-rows (check-line-fingerprint window y fingerprint))) + (return-from redraw-logical-line-when-horizontal-scroll cached-rows)) (let* ((rows (split-objects-at-line-breaks (create-drawing-objects logical-line))) (left-side-characters (left-side-character-count left-side-objects)) - (heights '()) + (row-heights) (total-height 0)) ;; the cursor is on one of the rows, scrolling follows it there. (dolist (row-objects rows) @@ -954,11 +991,12 @@ creating zero temporary letter-objects." (height (render-row-with-caching window (+ y total-height) (append side clipped)))) (incf total-height height) - (push height heights)) + ;; wrapping is off here, so every row begins where the line does, index 0 + (push (cons height 0) row-heights)) ;; y is in the frontend's units, as is the bound (when (<= (window-view-height window) (+ y total-height)) (return)))) - (setf heights (nreverse heights)) + (setf row-heights (nreverse row-heights)) ;; Reuse fingerprint if scroll position didn't change; avoids redundant sxhash (update-line-fingerprint window y @@ -967,8 +1005,8 @@ creating zero temporary letter-objects." (compute-line-fingerprint logical-line (horizontal-scroll-start window) left-side-width)) - heights) - heights))) + row-heights) + row-heights))) (defun redraw-lines (window) (let* ((*line-wrap* (variable-value 'line-wrap @@ -978,9 +1016,11 @@ creating zero temporary letter-objects." #'redraw-logical-line-when-horizontal-scroll))) (let ((y 0) (height (window-view-height window)) + ;; every row drawn, in reverse. see `window-screen-rows' + (rows) left-side-width) (block outer - (do-logical-line (logical-line window) + (do-logical-line (logical-line window line-point) (let* ((left-side-objects (alexandria:when-let (content (logical-line-left-content logical-line)) (mapcan #'create-drawing-object @@ -990,11 +1030,19 @@ creating zero temporary letter-objects." (setf left-side-width (loop :for object :in left-side-objects :sum (object-width object))) - (dolist (row-height - (funcall redraw-fn window y logical-line left-side-objects left-side-width)) - (incf y row-height)) + (let ((line-rows + (funcall redraw-fn window y logical-line left-side-objects left-side-width)) + ;; read once, shared by the line's rows + (line-number (line-number-at-point line-point))) + (loop :for (row-height . wrap-index) :in line-rows + :do (push (make-screen-row :height row-height + :line-number line-number + :wrap-index wrap-index) + rows) + (incf y row-height))) (unless (< y height) (return-from outer))))) + (setf (window-screen-rows window) (nreverse rows)) (when (< y height) (clear-line-fingerprint-cache-from window y) (invalidate-drawing-cache-from window y) diff --git a/src/mouse.lisp b/src/mouse.lisp index 7faa8548d..429bb5e0b 100644 --- a/src/mouse.lisp +++ b/src/mouse.lisp @@ -69,18 +69,44 @@ (* (window-y window) (lem-if:cell-height (implementation))))))) +(defun mouse-event-screen-row (mouse-event window fallback-row) + "The screen row of WINDOW that MOUSE-EVENT points at, counted from the top of its view. +Walks the heights recorded when the window was drawn, since rows are not all one height. +FALLBACK-ROW, what dividing by a row height gives, is used when there is no pixel position to walk +with or the window is undrawn." + (if (and (mouse-event-pixel-x mouse-event) + (mouse-event-pixel-y mouse-event)) + (multiple-value-bind (relative-x relative-y) + (get-relative-mouse-coordinates-pixels mouse-event window) + (declare (ignore relative-x)) + (or (window-screen-row-index-at-y window relative-y) + fallback-row)) + fallback-row)) + +(defun move-point-to-screen-row (point window row) + "Move POINT to the start of screen ROW of WINDOW, counting rows from the top of its view. +Uses the line recorded when the row was drawn, since counting virtual lines down from the view top +would miscount every row a newline inside virtual text added. Falls back to that count for a row +that was not drawn, or whose line the buffer no longer has." + (flet ((count-from-view-top () + (move-point point (window-view-point window)) + (move-to-next-virtual-line point row window))) + (alexandria:if-let ((screen-row (window-screen-row-at-index window row))) + (if (move-to-line point (screen-row-line-number screen-row)) + (move-to-next-virtual-line point (screen-row-wrap-index screen-row) window) + (count-from-view-top)) + (count-from-view-top)))) + (defun get-point-from-window-with-coordinates (window x y &optional (allow-overflow-column t)) (with-point ((point (buffer-point (window-buffer window)))) - (move-point point (window-view-point window)) - (move-to-next-virtual-line point y window) + (move-point-to-screen-row point window y) (let ((moved (move-to-virtual-line-column point x window))) (when (or moved allow-overflow-column) point)))) (defun move-current-point-to-x-y-position (window x y) (switch-to-window window) - (move-point (current-point) (window-view-point window)) - (move-to-next-virtual-line (current-point) y) + (move-point-to-screen-row (current-point) window y) (move-to-virtual-line-column (current-point) x)) (defvar *last-mouse-event*) @@ -194,7 +220,7 @@ mouse-event :window window :x x - :y y))))))) + :y (mouse-event-screen-row mouse-event window y)))))))) (defmethod handle-mouse-event ((mouse-event mouse-button-up)) (setf *last-dragged-separator* nil) @@ -260,7 +286,7 @@ mouse-event :window window :x x - :y y)))) + :y (mouse-event-screen-row mouse-event window y))))) ((typep *last-dragged-separator* 'window-vertical-separator) (let ((x (mouse-event-x mouse-event)) (button (mouse-event-button mouse-event))) From eb9778a6cfbd219b0aaf3c5da5650596d6a3a4eb Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Sun, 2 Aug 2026 14:15:46 +0300 Subject: [PATCH 19/26] fill a row with draw-block instead of a textless put row fills and the modeline background were sent as an empty put with a width and height, since put was the only notification that could paint a color. put now draws one line of text; draw-block fills a rect. --- .../server/frontend/dist/assets/index.js | 2 +- frontends/server/frontend/editor.js | 49 ++++++++++---- frontends/server/main.lisp | 65 ++++++++++--------- 3 files changed, 72 insertions(+), 44 deletions(-) diff --git a/frontends/server/frontend/dist/assets/index.js b/frontends/server/frontend/dist/assets/index.js index 79fd24e7d..572821549 100644 --- a/frontends/server/frontend/dist/assets/index.js +++ b/frontends/server/frontend/dist/assets/index.js @@ -1 +1 @@ -var __defProp=Object.defineProperty,__commonJSMin=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),__exportAll=(e,t)=>{let n={};for(var r in e)__defProp(n,r,{get:e[r],enumerable:!0});return t||__defProp(n,Symbol.toStringTag,{value:`Module`}),n};(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var require_models=__commonJSMin((e=>{var t=e&&e.__extends||(function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};return function(t,n){if(typeof n!=`function`&&n!==null)throw TypeError(`Class extends value `+String(n)+` is not a constructor or null`);e(t,n);function r(){this.constructor=t}t.prototype=n===null?Object.create(n):(r.prototype=n.prototype,new r)}})();Object.defineProperty(e,"__esModule",{value:!0}),e.createJSONRPCNotification=e.createJSONRPCRequest=e.createJSONRPCSuccessResponse=e.createJSONRPCErrorResponse=e.JSONRPCErrorCode=e.JSONRPCErrorException=e.isJSONRPCResponses=e.isJSONRPCResponse=e.isJSONRPCRequests=e.isJSONRPCRequest=e.isJSONRPCID=e.JSONRPC=void 0,e.JSONRPC=`2.0`,e.isJSONRPCID=function(e){return typeof e==`string`||typeof e==`number`||e===null},e.isJSONRPCRequest=function(t){return t.jsonrpc===e.JSONRPC&&t.method!==void 0&&t.result===void 0&&t.error===void 0},e.isJSONRPCRequests=function(t){return Array.isArray(t)&&t.every(e.isJSONRPCRequest)},e.isJSONRPCResponse=function(t){return t.jsonrpc===e.JSONRPC&&t.id!==void 0&&(t.result!==void 0||t.error!==void 0)},e.isJSONRPCResponses=function(t){return Array.isArray(t)&&t.every(e.isJSONRPCResponse)};var n=function(e,t,n){var r={code:e,message:t};return n!=null&&(r.data=n),r};e.JSONRPCErrorException=function(e){t(r,e);function r(t,n,i){var a=e.call(this,t)||this;return Object.setPrototypeOf(a,r.prototype),a.code=n,a.data=i,a}return r.prototype.toObject=function(){return n(this.code,this.message,this.data)},r}(Error),(function(e){e[e.ParseError=-32700]=`ParseError`,e[e.InvalidRequest=-32600]=`InvalidRequest`,e[e.MethodNotFound=-32601]=`MethodNotFound`,e[e.InvalidParams=-32602]=`InvalidParams`,e[e.InternalError=-32603]=`InternalError`})(e.JSONRPCErrorCode||={}),e.createJSONRPCErrorResponse=function(t,r,i,a){return{jsonrpc:e.JSONRPC,id:t,error:n(r,i,a)}},e.createJSONRPCSuccessResponse=function(t,n){return{jsonrpc:e.JSONRPC,id:t,result:n??null}},e.createJSONRPCRequest=function(t,n,r){return{jsonrpc:e.JSONRPC,id:t,method:n,params:r}},e.createJSONRPCNotification=function(t,n){return{jsonrpc:e.JSONRPC,method:t,params:n}}})),require_internal=__commonJSMin((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.DefaultErrorCode=void 0,e.DefaultErrorCode=0})),require_client=__commonJSMin((e=>{var t=e&&e.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||=Promise)(function(n,a){function o(e){try{c(r.next(e))}catch(e){a(e)}}function s(e){try{c(r.throw(e))}catch(e){a(e)}}function c(e){e.done?n(e.value):i(e.value).then(o,s)}c((r=r.apply(e,t||[])).next())})},n=e&&e.__generator||function(e,t){var n={label:0,sent:function(){if(a[0]&1)throw a[1];return a[1]},trys:[],ops:[]},r,i,a,o;return o={next:s(0),throw:s(1),return:s(2)},typeof Symbol==`function`&&(o[Symbol.iterator]=function(){return this}),o;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw TypeError(`Generator is already executing.`);for(;o&&(o=0,s[0]&&(n=0)),n;)try{if(r=1,i&&(a=s[0]&2?i.return:s[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,s[1])).done)return a;switch(i=0,a&&(s=[s[0]&2,a.value]),s[0]){case 0:case 1:a=s;break;case 4:return n.label++,{value:s[1],done:!1};case 5:n.label++,i=s[1],s=[0];continue;case 7:s=n.ops.pop(),n.trys.pop();continue;default:if((a=n.trys,!(a=a.length>0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{Object.defineProperty(e,"__esModule",{value:!0})})),require_server=__commonJSMin((e=>{var t=e&&e.__assign||function(){return t=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{var t=e&&e.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||=Promise)(function(n,a){function o(e){try{c(r.next(e))}catch(e){a(e)}}function s(e){try{c(r.throw(e))}catch(e){a(e)}}function c(e){e.done?n(e.value):i(e.value).then(o,s)}c((r=r.apply(e,t||[])).next())})},n=e&&e.__generator||function(e,t){var n={label:0,sent:function(){if(a[0]&1)throw a[1];return a[1]},trys:[],ops:[]},r,i,a,o;return o={next:s(0),throw:s(1),return:s(2)},typeof Symbol==`function`&&(o[Symbol.iterator]=function(){return this}),o;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw TypeError(`Generator is already executing.`);for(;o&&(o=0,s[0]&&(n=0)),n;)try{if(r=1,i&&(a=s[0]&2?i.return:s[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,s[1])).done)return a;switch(i=0,a&&(s=[s[0]&2,a.value]),s[0]){case 0:case 1:a=s;break;case 4:return n.label++,{value:s[1],done:!1};case 5:n.label++,i=s[1],s=[0];continue;case 7:s=n.ops.pop(),n.trys.pop();continue;default:if((a=n.trys,!(a=a.length>0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{var t=e&&e.__createBinding||(Object.create?(function(e,t,n,r){r===void 0&&(r=n);var i=Object.getOwnPropertyDescriptor(t,n);(!i||(`get`in i?!t.__esModule:i.writable||i.configurable))&&(i={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,r,i)}):(function(e,t,n,r){r===void 0&&(r=n),e[r]=t[n]})),n=e&&e.__exportStar||function(e,n){for(var r in e)r!=="default"&&!Object.prototype.hasOwnProperty.call(n,r)&&t(n,e,r)};Object.defineProperty(e,"__esModule",{value:!0}),n(require_client(),e),n(require_interfaces(),e),n(require_models(),e),n(require_server(),e),n(require_server_and_client(),e)})),import_dist=require_dist(),JSONRPC=class{constructor(e,{onConnected:t,onClosed:n}){this.url=e,this.onConnected=t,this.onClosed=n,this.messageQueue=[],this.serverAndClient=null,this.connect(),this.connectionEstablished=!1,this.timerId=null,this.closed=!1}close(){this.timerId&&clearTimeout(this.timerId),this.webSocket.close(),this.closed=!0}on(e,t){this.serverAndClient.addMethod(e,t)}async requestInternal(e,t,n){let r=await this.serverAndClient.request(e,t);n&&n(r)}requestMessageQueue(){this.messageQueue.forEach(e=>{let[t,n,r]=e;this.requestInternal(t,n,r)}),this.messageQueue=[]}request(e,t,n){this.webSocket.readyState===WebSocket.OPEN?this.requestInternal(e,t,n):this.messageQueue.push([e,t,n])}notify(e,t){switch(this.webSocket.readyState){case WebSocket.OPEN:this.serverAndClient.notify(e,t);break;case WebSocket.CLOSED:break}}connect(e){this.closed||(console.log(`connect`,this.url),this.webSocket=new WebSocket(this.url),this.serverAndClient||=new import_dist.JSONRPCServerAndClient(new import_dist.JSONRPCServer,new import_dist.JSONRPCClient(e=>{try{return this.webSocket.send(JSON.stringify(e)),Promise.resolve()}catch(e){return Promise.reject(e)}})),this.webSocket.onmessage=e=>{this.serverAndClient.receiveAndSend(JSON.parse(e.data.toString()))},this.webSocket.onopen=()=>{console.log(`WebSocket connection established`),this.connectionEstablished=!0,this.onConnected&&this.onConnected(),this.requestMessageQueue()},this.webSocket.onclose=e=>{console.error(`WebScoket closed`,e),this.serverAndClient.rejectAllPendingRequests(`Connection is closed (${e.reason}).`),this.connectionEstablished&&this.onClosed(),this.timerId=setTimeout(()=>{this.connect()},3e3)},this.webSocket.onerror=e=>{console.error(`WebSocket error:`,e),this.webSocket.close()})}},keyevent_exports=__exportAll({convertKeyEvent:()=>convertKeyEvent}),modifierKeys=[`Shift`,`Control`,`Alt`,`Meta`,`CapsLock`],convertKeyTable={Enter:`Return`,ArrowRight:`Right`,ArrowLeft:`Left`,ArrowUp:`Up`,ArrowDown:`Down`,"¡":`1`,"™":`2`,"£":`3`,"¢":`4`,"∞":`5`,"§":`6`,"¶":`7`,"•":`8`,ª:`9`,º:`0`,"–":`-`,"≠":`=`,"“":`[`,"‘":`]`,"«":`\\`,"…":`;`,æ:`'`,"≤":`,`,"≥":`.`,"÷":`/`,"⁄":`!`,"€":`@`,"‹":`#`,"›":`$`,fi:`%`,fl:`^`,"‡":`&`,"°":`*`,"·":`(`,"‚":`)`,"—":`_`,"±":`+`,"”":`{`,"’":`}`,"»":`|`,Ú:`:`,Æ:`"`,"¯":`<`,"˘":`>`,"¿":`?`,œ:`q`,"∑":`w`,"´":`e`,"®":`r`,"†":`t`,"¥":`y`,"¨":`u`,ˆ:`i`,ø:`o`,π:`p`,å:`a`,ß:`s`,"∂":`d`,ƒ:`f`,"©":`g`,"˙":`h`,"∆":`j`,"˚":`k`,"¬":`l`,Ω:`z`,"≈":`x`,ç:`c`,"√":`v`,"∫":`b`,"˜":`n`,µ:`m`,Œ:`Q`,"„":`W`,"´":`E`,"‰":`R`,ˇ:`T`,Á:`Y`,"¨":`U`,ˆ:`I`,Ø:`O`,"∏":`P`,Å:`A`,Í:`S`,Î:`D`,Ï:`F`,"˝":`G`,Ó:`H`,Ô:`J`,"":`K`,Ò:`L`,"¸":`Z`,"˛":`X`,Ç:`C`,"◊":`V`,ı:`B`,"˜":`N`,Â:`M`};function getKey(e){return e.altKey?convertKeyTable[e.key]||(e.code.startsWith(`Key`)?e.code[3].toLowerCase():null)||e.key:convertKeyTable[e.key]||e.key}function convertKeyEvent(e){return modifierKeys.indexOf(e.key)===-1?{key:getKey(e),ctrl:e.ctrlKey,meta:e.altKey,super:e.metaKey,shift:e.shiftKey}:null}var lib_exports=__exportAll({computeWidth:()=>computeWidth,eawVersion:()=>version,getEAW:()=>getEAW}),defs=[[0,31,`N`],[32,126,`Na`],[127,160,`N`],[161,161,`A`],[162,163,`Na`],[164,164,`A`],[165,166,`Na`],[167,168,`A`],[169,169,`N`],[170,170,`A`],[171,171,`N`],[172,172,`Na`],[173,174,`A`],[175,175,`Na`],[176,180,`A`],[181,181,`N`],[182,186,`A`],[187,187,`N`],[188,191,`A`],[192,197,`N`],[198,198,`A`],[199,207,`N`],[208,208,`A`],[209,214,`N`],[215,216,`A`],[217,221,`N`],[222,225,`A`],[226,229,`N`],[230,230,`A`],[231,231,`N`],[232,234,`A`],[235,235,`N`],[236,237,`A`],[238,239,`N`],[240,240,`A`],[241,241,`N`],[242,243,`A`],[244,246,`N`],[247,250,`A`],[251,251,`N`],[252,252,`A`],[253,253,`N`],[254,254,`A`],[255,256,`N`],[257,257,`A`],[258,272,`N`],[273,273,`A`],[274,274,`N`],[275,275,`A`],[276,282,`N`],[283,283,`A`],[284,293,`N`],[294,295,`A`],[296,298,`N`],[299,299,`A`],[300,304,`N`],[305,307,`A`],[308,311,`N`],[312,312,`A`],[313,318,`N`],[319,322,`A`],[323,323,`N`],[324,324,`A`],[325,327,`N`],[328,331,`A`],[332,332,`N`],[333,333,`A`],[334,337,`N`],[338,339,`A`],[340,357,`N`],[358,359,`A`],[360,362,`N`],[363,363,`A`],[364,461,`N`],[462,462,`A`],[463,463,`N`],[464,464,`A`],[465,465,`N`],[466,466,`A`],[467,467,`N`],[468,468,`A`],[469,469,`N`],[470,470,`A`],[471,471,`N`],[472,472,`A`],[473,473,`N`],[474,474,`A`],[475,475,`N`],[476,476,`A`],[477,592,`N`],[593,593,`A`],[594,608,`N`],[609,609,`A`],[610,707,`N`],[708,708,`A`],[709,710,`N`],[711,711,`A`],[712,712,`N`],[713,715,`A`],[716,716,`N`],[717,717,`A`],[718,719,`N`],[720,720,`A`],[721,727,`N`],[728,731,`A`],[732,732,`N`],[733,733,`A`],[734,734,`N`],[735,735,`A`],[736,767,`N`],[768,879,`A`],[880,912,`N`],[913,929,`A`],[930,930,`N`],[931,937,`A`],[938,944,`N`],[945,961,`A`],[962,962,`N`],[963,969,`A`],[970,1024,`N`],[1025,1025,`A`],[1026,1039,`N`],[1040,1103,`A`],[1104,1104,`N`],[1105,1105,`A`],[1106,4351,`N`],[4352,4447,`W`],[4448,8207,`N`],[8208,8208,`A`],[8209,8210,`N`],[8211,8214,`A`],[8215,8215,`N`],[8216,8217,`A`],[8218,8219,`N`],[8220,8221,`A`],[8222,8223,`N`],[8224,8226,`A`],[8227,8227,`N`],[8228,8231,`A`],[8232,8239,`N`],[8240,8240,`A`],[8241,8241,`N`],[8242,8243,`A`],[8244,8244,`N`],[8245,8245,`A`],[8246,8250,`N`],[8251,8251,`A`],[8252,8253,`N`],[8254,8254,`A`],[8255,8307,`N`],[8308,8308,`A`],[8309,8318,`N`],[8319,8319,`A`],[8320,8320,`N`],[8321,8324,`A`],[8325,8360,`N`],[8361,8361,`H`],[8362,8363,`N`],[8364,8364,`A`],[8365,8450,`N`],[8451,8451,`A`],[8452,8452,`N`],[8453,8453,`A`],[8454,8456,`N`],[8457,8457,`A`],[8458,8466,`N`],[8467,8467,`A`],[8468,8469,`N`],[8470,8470,`A`],[8471,8480,`N`],[8481,8482,`A`],[8483,8485,`N`],[8486,8486,`A`],[8487,8490,`N`],[8491,8491,`A`],[8492,8530,`N`],[8531,8532,`A`],[8533,8538,`N`],[8539,8542,`A`],[8543,8543,`N`],[8544,8555,`A`],[8556,8559,`N`],[8560,8569,`A`],[8570,8584,`N`],[8585,8585,`A`],[8586,8591,`N`],[8592,8601,`A`],[8602,8631,`N`],[8632,8633,`A`],[8634,8657,`N`],[8658,8658,`A`],[8659,8659,`N`],[8660,8660,`A`],[8661,8678,`N`],[8679,8679,`A`],[8680,8703,`N`],[8704,8704,`A`],[8705,8705,`N`],[8706,8707,`A`],[8708,8710,`N`],[8711,8712,`A`],[8713,8714,`N`],[8715,8715,`A`],[8716,8718,`N`],[8719,8719,`A`],[8720,8720,`N`],[8721,8721,`A`],[8722,8724,`N`],[8725,8725,`A`],[8726,8729,`N`],[8730,8730,`A`],[8731,8732,`N`],[8733,8736,`A`],[8737,8738,`N`],[8739,8739,`A`],[8740,8740,`N`],[8741,8741,`A`],[8742,8742,`N`],[8743,8748,`A`],[8749,8749,`N`],[8750,8750,`A`],[8751,8755,`N`],[8756,8759,`A`],[8760,8763,`N`],[8764,8765,`A`],[8766,8775,`N`],[8776,8776,`A`],[8777,8779,`N`],[8780,8780,`A`],[8781,8785,`N`],[8786,8786,`A`],[8787,8799,`N`],[8800,8801,`A`],[8802,8803,`N`],[8804,8807,`A`],[8808,8809,`N`],[8810,8811,`A`],[8812,8813,`N`],[8814,8815,`A`],[8816,8833,`N`],[8834,8835,`A`],[8836,8837,`N`],[8838,8839,`A`],[8840,8852,`N`],[8853,8853,`A`],[8854,8856,`N`],[8857,8857,`A`],[8858,8868,`N`],[8869,8869,`A`],[8870,8894,`N`],[8895,8895,`A`],[8896,8977,`N`],[8978,8978,`A`],[8979,8985,`N`],[8986,8987,`W`],[8988,9e3,`N`],[9001,9002,`W`],[9003,9192,`N`],[9193,9196,`W`],[9197,9199,`N`],[9200,9200,`W`],[9201,9202,`N`],[9203,9203,`W`],[9204,9311,`N`],[9312,9449,`A`],[9450,9450,`N`],[9451,9547,`A`],[9548,9551,`N`],[9552,9587,`A`],[9588,9599,`N`],[9600,9615,`A`],[9616,9617,`N`],[9618,9621,`A`],[9622,9631,`N`],[9632,9633,`A`],[9634,9634,`N`],[9635,9641,`A`],[9642,9649,`N`],[9650,9651,`A`],[9652,9653,`N`],[9654,9655,`A`],[9656,9659,`N`],[9660,9661,`A`],[9662,9663,`N`],[9664,9665,`A`],[9666,9669,`N`],[9670,9672,`A`],[9673,9674,`N`],[9675,9675,`A`],[9676,9677,`N`],[9678,9681,`A`],[9682,9697,`N`],[9698,9701,`A`],[9702,9710,`N`],[9711,9711,`A`],[9712,9724,`N`],[9725,9726,`W`],[9727,9732,`N`],[9733,9734,`A`],[9735,9736,`N`],[9737,9737,`A`],[9738,9741,`N`],[9742,9743,`A`],[9744,9747,`N`],[9748,9749,`W`],[9750,9755,`N`],[9756,9756,`A`],[9757,9757,`N`],[9758,9758,`A`],[9759,9791,`N`],[9792,9792,`A`],[9793,9793,`N`],[9794,9794,`A`],[9795,9799,`N`],[9800,9811,`W`],[9812,9823,`N`],[9824,9825,`A`],[9826,9826,`N`],[9827,9829,`A`],[9830,9830,`N`],[9831,9834,`A`],[9835,9835,`N`],[9836,9837,`A`],[9838,9838,`N`],[9839,9839,`A`],[9840,9854,`N`],[9855,9855,`W`],[9856,9874,`N`],[9875,9875,`W`],[9876,9885,`N`],[9886,9887,`A`],[9888,9888,`N`],[9889,9889,`W`],[9890,9897,`N`],[9898,9899,`W`],[9900,9916,`N`],[9917,9918,`W`],[9919,9919,`A`],[9920,9923,`N`],[9924,9925,`W`],[9926,9933,`A`],[9934,9934,`W`],[9935,9939,`A`],[9940,9940,`W`],[9941,9953,`A`],[9954,9954,`N`],[9955,9955,`A`],[9956,9959,`N`],[9960,9961,`A`],[9962,9962,`W`],[9963,9969,`A`],[9970,9971,`W`],[9972,9972,`A`],[9973,9973,`W`],[9974,9977,`A`],[9978,9978,`W`],[9979,9980,`A`],[9981,9981,`W`],[9982,9983,`A`],[9984,9988,`N`],[9989,9989,`W`],[9990,9993,`N`],[9994,9995,`W`],[9996,10023,`N`],[10024,10024,`W`],[10025,10044,`N`],[10045,10045,`A`],[10046,10059,`N`],[10060,10060,`W`],[10061,10061,`N`],[10062,10062,`W`],[10063,10066,`N`],[10067,10069,`W`],[10070,10070,`N`],[10071,10071,`W`],[10072,10101,`N`],[10102,10111,`A`],[10112,10132,`N`],[10133,10135,`W`],[10136,10159,`N`],[10160,10160,`W`],[10161,10174,`N`],[10175,10175,`W`],[10176,10213,`N`],[10214,10221,`Na`],[10222,10628,`N`],[10629,10630,`Na`],[10631,11034,`N`],[11035,11036,`W`],[11037,11087,`N`],[11088,11088,`W`],[11089,11092,`N`],[11093,11093,`W`],[11094,11097,`A`],[11098,11903,`N`],[11904,11929,`W`],[11930,11930,`N`],[11931,12019,`W`],[12020,12031,`N`],[12032,12245,`W`],[12246,12271,`N`],[12272,12287,`W`],[12288,12288,`F`],[12289,12350,`W`],[12351,12352,`N`],[12353,12438,`W`],[12439,12440,`N`],[12441,12543,`W`],[12544,12548,`N`],[12549,12591,`W`],[12592,12592,`N`],[12593,12686,`W`],[12687,12687,`N`],[12688,12771,`W`],[12772,12782,`N`],[12783,12830,`W`],[12831,12831,`N`],[12832,12871,`W`],[12872,12879,`A`],[12880,19903,`W`],[19904,19967,`N`],[19968,42124,`W`],[42125,42127,`N`],[42128,42182,`W`],[42183,43359,`N`],[43360,43388,`W`],[43389,44031,`N`],[44032,55203,`W`],[55204,57343,`N`],[57344,63743,`A`],[63744,64255,`W`],[64256,65023,`N`],[65024,65039,`A`],[65040,65049,`W`],[65050,65071,`N`],[65072,65106,`W`],[65107,65107,`N`],[65108,65126,`W`],[65127,65127,`N`],[65128,65131,`W`],[65132,65280,`N`],[65281,65376,`F`],[65377,65470,`H`],[65471,65473,`N`],[65474,65479,`H`],[65480,65481,`N`],[65482,65487,`H`],[65488,65489,`N`],[65490,65495,`H`],[65496,65497,`N`],[65498,65500,`H`],[65501,65503,`N`],[65504,65510,`F`],[65511,65511,`N`],[65512,65518,`H`],[65519,65532,`N`],[65533,65533,`A`],[65534,94175,`N`],[94176,94180,`W`],[94181,94191,`N`],[94192,94193,`W`],[94194,94207,`N`],[94208,100343,`W`],[100344,100351,`N`],[100352,101589,`W`],[101590,101631,`N`],[101632,101640,`W`],[101641,110575,`N`],[110576,110579,`W`],[110580,110580,`N`],[110581,110587,`W`],[110588,110588,`N`],[110589,110590,`W`],[110591,110591,`N`],[110592,110882,`W`],[110883,110897,`N`],[110898,110898,`W`],[110899,110927,`N`],[110928,110930,`W`],[110931,110932,`N`],[110933,110933,`W`],[110934,110947,`N`],[110948,110951,`W`],[110952,110959,`N`],[110960,111355,`W`],[111356,126979,`N`],[126980,126980,`W`],[126981,127182,`N`],[127183,127183,`W`],[127184,127231,`N`],[127232,127242,`A`],[127243,127247,`N`],[127248,127277,`A`],[127278,127279,`N`],[127280,127337,`A`],[127338,127343,`N`],[127344,127373,`A`],[127374,127374,`W`],[127375,127376,`A`],[127377,127386,`W`],[127387,127404,`A`],[127405,127487,`N`],[127488,127490,`W`],[127491,127503,`N`],[127504,127547,`W`],[127548,127551,`N`],[127552,127560,`W`],[127561,127567,`N`],[127568,127569,`W`],[127570,127583,`N`],[127584,127589,`W`],[127590,127743,`N`],[127744,127776,`W`],[127777,127788,`N`],[127789,127797,`W`],[127798,127798,`N`],[127799,127868,`W`],[127869,127869,`N`],[127870,127891,`W`],[127892,127903,`N`],[127904,127946,`W`],[127947,127950,`N`],[127951,127955,`W`],[127956,127967,`N`],[127968,127984,`W`],[127985,127987,`N`],[127988,127988,`W`],[127989,127991,`N`],[127992,128062,`W`],[128063,128063,`N`],[128064,128064,`W`],[128065,128065,`N`],[128066,128252,`W`],[128253,128254,`N`],[128255,128317,`W`],[128318,128330,`N`],[128331,128334,`W`],[128335,128335,`N`],[128336,128359,`W`],[128360,128377,`N`],[128378,128378,`W`],[128379,128404,`N`],[128405,128406,`W`],[128407,128419,`N`],[128420,128420,`W`],[128421,128506,`N`],[128507,128591,`W`],[128592,128639,`N`],[128640,128709,`W`],[128710,128715,`N`],[128716,128716,`W`],[128717,128719,`N`],[128720,128722,`W`],[128723,128724,`N`],[128725,128727,`W`],[128728,128731,`N`],[128732,128735,`W`],[128736,128746,`N`],[128747,128748,`W`],[128749,128755,`N`],[128756,128764,`W`],[128765,128991,`N`],[128992,129003,`W`],[129004,129007,`N`],[129008,129008,`W`],[129009,129291,`N`],[129292,129338,`W`],[129339,129339,`N`],[129340,129349,`W`],[129350,129350,`N`],[129351,129535,`W`],[129536,129647,`N`],[129648,129660,`W`],[129661,129663,`N`],[129664,129672,`W`],[129673,129679,`N`],[129680,129725,`W`],[129726,129726,`N`],[129727,129733,`W`],[129734,129741,`N`],[129742,129755,`W`],[129756,129759,`N`],[129760,129768,`W`],[129769,129775,`N`],[129776,129784,`W`],[129785,131071,`N`],[131072,196605,`W`],[196606,196607,`N`],[196608,262141,`W`],[262142,917759,`N`],[917760,917999,`A`],[918e3,983039,`N`],[983040,1048573,`A`],[1048574,1048575,`N`],[1048576,1114109,`A`],[1114110,1114111,`N`]],version=`15.1.0`;function getEAWOfCodePoint(e){let t=0,n=defs.length-1;for(;t!==n;){let r=t+(n-t>>1),[i,a,o]=defs[r];if(ea)t=r+1;else return o}return defs[t][2]}function getEAW(e,t=0){let n=e.codePointAt(t);if(n!==void 0)return getEAWOfCodePoint(n)}var defaultWidths={N:1,Na:1,W:2,F:2,H:1,A:1};function computeWidth(e,t){let n=0;for(let r of e){let e=getEAW(r);n+=t&&t[e]||defaultWidths[e]}return n}var textOffsetY=5,isSafari=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);function isWideChar(e){switch(getEAW(e)){case`F`:case`W`:return!0;default:return!1}}function isMacOS(){return window.navigator.userAgent.indexOf(`Mac OS X`)!==-1}function computeFontSize(e){let t=document.createElement(`canvas`).getContext(`2d`);t.font=e;let n=t.measureText(`W`);return[Math.floor(n.width),Math.round(n.fontBoundingBoxAscent+textOffsetY+(n.emHeightDescent||0)),Math.round(n.fontBoundingBoxAscent+textOffsetY)]}function drawBlock({ctx:e,x:t,y:n,width:r,height:i,style:a}){e.fillStyle=a,e.fillRect(t,n,r,i)}function drawText({ctx:e,x:t,y:n,text:r,font:i,style:a,option:o}){n+=Math.round(textOffsetY),e.fillStyle=a,e.font=i,e.textBaseline=`top`;for(let i of r)isWideChar(i)?(e.fillText(i,t,n,o.fontWidth*2),t+=o.fontWidth*2):(e.fillText(i,t,n,o.fontWidth),t+=o.fontWidth)}function drawHorizontalLine({ctx:e,x:t,y:n,width:r,style:i,lineWidth:a=1}){e.strokeStyle=i,e.lineWidth=a,e.setLineDash=[],e.beginPath(),e.moveTo(t,n),e.lineTo(t+r,n),e.stroke()}var Option=class{constructor({fontName:e,fontSize:t}){this.setFont(e,t),this.foreground=`#cccccc`,this.background=`#2d2d2d`}setFont(e,t){let n=t+`px `+e,[r,i,a]=computeFontSize(n);this.fontName=e,this.fontSize=t,this.fontWidth=r,this.fontHeight=i,this.fontAscent=a,this.font=n}};function getLemEditorElement(){return document.getElementById(`lem-editor`)}function normalizeWheelDelta(e,t,n,r){switch(n){case 0:return{dx:e/r,dy:t/r};case 2:return{dx:e*20,dy:t*20};default:return{dx:e,dy:t}}}function extractWholeLines(e,t){let n=Math.trunc(e),r=Math.trunc(t);return{scrollX:n,scrollY:r,remainderX:e-n,remainderY:t-r}}function cursorPosition(e,t){let[n,r]=t.getDisplayRectangle(),i=e.clientX-n,a=e.clientY-r;return{pixelX:i,pixelY:a,x:Math.floor(i/t.option.fontWidth),y:Math.floor(a/t.option.fontHeight)}}function makeWheelHandler(e){let t={x:0,y:0},n=!1,r={pixelX:0,pixelY:0,x:0,y:0};return i=>{i.preventDefault(),r=cursorPosition(i,e);let{dx:a,dy:o}=normalizeWheelDelta(i.deltaX,i.deltaY,i.deltaMode,e.option.fontHeight);t={x:t.x+a,y:t.y+o},n||(n=!0,requestAnimationFrame(()=>{n=!1;let{scrollX:i,scrollY:a,remainderX:o,remainderY:s}=extractWholeLines(t.x,t.y);t={x:o,y:s},(i!==0||a!==0)&&e.jsonrpc.notify(`input`,{kind:`wheel`,value:{...r,wheelX:-i,wheelY:-a}})}))}}function addMouseEventListeners({dom:e,editor:t,isDraggable:n,draggableStyle:r}){e.addEventListener(`contextmenu`,e=>{e.preventDefault()});let i=(e,n)=>{e.preventDefault();let[r,i]=t.getDisplayRectangle(),a=e.clientX-r,o=e.clientY-i,s=Math.floor(a/t.option.fontWidth),c=Math.floor(o/t.option.fontHeight);t.jsonrpc.notify(`input`,{kind:n,value:{x:s,y:c,pixelX:a,pixelY:o,button:e.button,clicks:e.detail}})};e.addEventListener(`mousedown`,e=>{n&&(document.body.style.cursor=r),t.focusHiddenInput(),i(e,`mousedown`)}),e.addEventListener(`mouseup`,e=>{n&&(document.body.style.cursor=`default`),i(e,`mouseup`)});let a=0;e.addEventListener(`mousemove`,e=>{e.preventDefault();let n=Date.now();if(n-a>50){a=n;let[r,i]=t.getDisplayRectangle(),o=e.clientX-r,s=e.clientY-i,c=Math.floor(o/t.option.fontWidth),l=Math.floor(s/t.option.fontHeight);t.jsonrpc.notify(`input`,{kind:`mousemove`,value:{x:c,y:l,pixelX:o,pixelY:s,button:e.buttons===0?null:e.buttons-1}})}}),n&&(e.addEventListener(`mouseover`,()=>{document.body.style.cursor=r}),e.addEventListener(`mouseout`,e=>{e.buttons!==1&&(document.body.style.cursor=`default`)})),e.addEventListener(`wheel`,makeWheelHandler(t))}var zIndexTable={"floating-window":200,modeline:100,"vertical-border":100,"horizontal-border":101};function zindex(e){return zIndexTable[e]||0}var borderOffsetX=5,borderOffsetY=10,BaseSurface=class{constructor({editor:e}){this.editor=e,this.mainDOM=null,this.wrapper=null}delete(){this.wrapper?getLemEditorElement().removeChild(this.wrapper):getLemEditorElement().removeChild(this.mainDOM)}setupDOM({dom:e,isFloating:t,border:n,cssClassName:r}){this.mainDOM=e,t&&n?(this.wrapper=document.createElement(`div`),r&&(this.wrapper.className=r),this.wrapper.style.position=`absolute`,this.wrapper.style.backgroundColor=this.editor.option.background,this.wrapper.style.zIndex=zindex(`floating-window`),this.wrapper.appendChild(e),getLemEditorElement().appendChild(this.wrapper)):(r&&(e.className=r),getLemEditorElement().appendChild(e))}move(e,t){let[n,r]=this.editor.getDisplayRectangle(),i=Math.floor(n+e),a=Math.floor(r+t);this.wrapper?(this.wrapper.style.left=i-borderOffsetX+`px`,this.wrapper.style.top=a-borderOffsetY+`px`,this.mainDOM.style.left=borderOffsetX+`px`,this.mainDOM.style.top=borderOffsetY+`px`):(this.mainDOM.style.left=i+`px`,this.mainDOM.style.top=a+`px`)}_resize(e,t){let n=window.devicePixelRatio||1;this.mainDOM.width=e*n,this.mainDOM.height=t*n,this.mainDOM.style.width=e+`px`,this.mainDOM.style.height=t+`px`,this.wrapper&&(this.wrapper.style.width=e+borderOffsetX*2+`px`,this.wrapper.style.height=t+borderOffsetY*2+`px`)}drawBlock(e,t,n,r,i){}drawText(e,t,n,r,i,a,o){}drawImage(e,t,n,r,i,a,o){}clearImages(e,t){}clearAllImages(){}touch(){}evalIn(code){return eval(code)}},CanvasSurface=class extends BaseSurface{constructor({editor:e,view:t,pixelX:n,pixelY:r,pixelWidth:i,pixelHeight:a,styles:o,isFloating:s,border:c,cssClassName:l}){super({editor:e});let u=this.setupCanvas(o);this.setupDOM({dom:u,isFloating:s,border:c,cssClassName:l}),this.move(n,r),this.resize(i,a),this.drawingQueue=[],addMouseEventListeners({dom:u,editor:e})}setupCanvas(e){let t=document.createElement(`canvas`);if(t.style.position=`absolute`,e)for(let n in e)t.style[n]=e[n];return t}resize(e,t){this._resize(e,t);let n=window.devicePixelRatio||1;this.mainDOM.getContext(`2d`).scale(n,n)}move(e,t){if(super.move(e,t),this.imageEls)for(let[,e]of this.imageEls)this.positionImage(e)}delete(){this.clearAllImages(),super.delete()}drawBlock(e,t,n,r,i){this.drawingQueue.push(function(a){drawBlock({ctx:a,x:e,y:t,width:n,height:r,style:i})})}drawText(e,t,n,r,i,a,o){let s=this.editor.option,c=o||s.fontHeight;this.drawingQueue.push(function(o){if(a=a?`${s.fontSize}px ${a}`:s.font,!i)drawBlock({ctx:o,x:e,y:t,width:r,height:c,style:s.background}),drawText({ctx:o,x:e,y:t,text:n,style:s.foreground,font:a,option:s});else{let{foreground:l,background:u,bold:d,reverse:f,underline:p,cursor:m}=i;if(l||=s.foreground,u||=s.background,f){let e=u;u=l,l=e}m&&(u=s.background),drawBlock({ctx:o,x:e,y:t,width:r,height:c,style:u}),drawText({ctx:o,x:e,y:t,text:n,style:l,font:d?`bold `+a:a,option:s}),p&&drawHorizontalLine({ctx:o,x:e,y:t+s.fontHeight-2,width:r,style:typeof p==`string`?p:l,lineWidth:2})}})}imageBaseLeft(){return parseFloat(this.mainDOM.style.left)||0}imageBaseTop(){return parseFloat(this.mainDOM.style.top)||0}drawImage(e,t,n,r,i,a,o){this.imageEls||=new Map;let s=e+`,`+t,c=this.imageEls.get(s);if(c&&c.url!==o&&(c.el.remove(),this.imageEls.delete(s),c=null),!c){let e=document.createElement(`img`);e.style.position=`absolute`,e.style.pointerEvents=`none`,e.style.zIndex=`1`,e.src=o,this.mainDOM.parentNode.appendChild(e),c={el:e,url:o},this.imageEls.set(s,c)}c.x=e,c.y=t,c.width=n,c.height=r,c.clipWidth=i,c.clipHeight=a,this.positionImage(c)}positionImage(e){e.el.style.left=this.imageBaseLeft()+e.x+`px`,e.el.style.top=this.imageBaseTop()+e.y+`px`,e.el.style.width=e.width+`px`,e.el.style.height=e.height+`px`;let t=e.clipWidth==null?0:Math.max(0,e.width-e.clipWidth),n=e.clipHeight==null?0:Math.max(0,e.height-e.clipHeight);e.el.style.clipPath=t>0||n>0?`inset(0px ${t}px ${n}px 0px)`:``}clearImages(e,t){if(this.imageEls)for(let[n,r]of this.imageEls){let i=r.y+(r.height||0);r.ye&&(r.el.remove(),this.imageEls.delete(n))}}clearAllImages(){if(this.imageEls){for(let[,e]of this.imageEls)e.el.remove();this.imageEls.clear()}}touch(){let e=this.mainDOM.getContext(`2d`);for(let t of this.drawingQueue)t(e);this.drawingQueue=[]}activate(){this.mainDOM.dataset.store=`active`}deactivate(){this.mainDOM.dataset.store=`inactive`}},HTMLSurface=class extends BaseSurface{constructor({editor:e,pixelX:t,pixelY:n,pixelWidth:r,pixelHeight:i,styles:a,option:o,isFloating:s,border:c,html:l}){super({editor:e});let u=document.createElement(`iframe`);this.setupDOM({dom:u,isFloating:s,border:c}),u.style.position=`absolute`,u.style.backgroundColor=o.background,u.setAttribute(`sandbox`,`allow-scripts allow-same-origin`),u.srcdoc=l,u.addEventListener(`load`,()=>{let e=u.contentWindow;e.invokeLem=(e,t)=>parent.postMessage({type:`invoke-lem`,method:e,args:t})}),this.iframe=u,this.move(t,n),this.resize(r,i)}resize(e,t){this._resize(e,t)}update(e){let t=this.iframe.contentWindow.scrollY;this.iframe.srcdoc=e,this.iframe.onload=()=>{this.iframe.onload=null,this.iframe.contentWindow.scrollTo(0,t)}}evalIn(e){return this.iframe.contentWindow.eval(e)}},VerticalBorder=class{constructor({x:e,y:t,height:n,option:r,editor:i}){this.option=r,this.editor=i,this.line=document.createElement(`div`),this.line.className=`lem-editor__vertical-border`,this.line.style.height=n+`px`,this.line.style.position=`absolute`,this.line.style.zIndex=zindex(`vertical-border`),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:i,isDraggable:!0,draggableStyle:`col-resize`})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.line.style.left=Math.floor(n+e-this.option.fontWidth/2)+`px`,this.line.style.top=r+t+`px`}resize(e){this.line.style.height=e+`px`}},HorizontalBorder=class{constructor({x:e,y:t,width:n,option:r,editor:i}){this.option=r,this.editor=i,this.line=document.createElement(`div`),this.line.className=`lem-editor__horizontal-border`,this.line.style.width=n+`px`,this.line.style.position=`absolute`,this.line.style.zIndex=zindex(`horizontal-border`),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:i,isDraggable:!0,draggableStyle:`row-resize`})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.line.style.left=n+e+`px`,this.line.style.top=Math.floor(r+t-4)+`px`}resize(e){this.line.style.width=e+`px`}},viewStyles={header:()=>{},tile:()=>{},floating:e=>({boxSizing:`border-box`,borderColor:e.foreground,backgroundColor:e.background})};function getViewStyle(e,t){return viewStyles[e](t)||{}}var View=class{constructor({id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,useModeline:l,kind:u,type:d,content:f,border:p,borderShape:m,option:h,editor:g}){switch(this.option=h,this.id=e,this.x=t,this.y=n,this.width=r,this.height=i,this.pixelX=a,this.pixelY=o,this.pixelWidth=s,this.pixelHeight=c,this.useModeline=l,this.kind=u,this.type=d,this.border=p,this.borderShape=m,this.editor=g,this.bottomBar=null,this.leftsideBar=null,u){case`tile`:this.mainSurface=this.makeSurface(d,f),this.leftSideBar=new VerticalBorder({x:a,y:o,height:c+(l?h.fontHeight:0),option:h,editor:g}),l||(this.bottomBar=new HorizontalBorder({x:a,y:o+c-h.fontHeight,width:s,option:h,editor:g}));break;case`header`:this.mainSurface=this.makeSurface(d,f);break;case`floating`:this.mainSurface=this.makeSurface(d,f),m===`left-border`&&(this.leftSideBar=new VerticalBorder({x:a,y:o,height:c,option:h,editor:g}));break}this.modelineSurface=l?this.makeModelineSurface():null}delete(){this.mainSurface.delete(),this.modelineSurface&&this.modelineSurface.delete(),this.leftSideBar&&this.leftSideBar.delete(),this.bottomBar&&this.bottomBar.delete()}move(e,t,n,r){this.x=e,this.y=t,this.pixelX=n,this.pixelY=r,this.mainSurface.move(n,r),this.modelineSurface&&this.modelineSurface.move(n,r+this.pixelHeight),this.leftSideBar&&this.leftSideBar.move(n,r),this.bottomBar&&this.bottomBar.move(n,r+this.pixelHeight)}resize(e,t,n,r){this.width=e,this.height=t,this.pixelWidth=n,this.pixelHeight=r,this.mainSurface.resize(n,r),this.modelineSurface&&(this.modelineSurface.move(this.pixelX,this.pixelY+r),this.modelineSurface.resize(n,this.option.fontHeight)),this.leftSideBar&&this.leftSideBar.resize(r+(this.modelineSurface?this.option.fontHeight:0)),this.bottomBar&&this.bottomBar.resize(n)}clear(){this.mainSurface.drawBlock(0,0,this.pixelWidth,this.pixelHeight,this.option.background),this.mainSurface.clearImages(0,this.pixelHeight)}clearEol(e,t,n){n??=this.option.fontHeight,this.mainSurface.drawBlock(e,t,this.pixelWidth-e,n,this.option.background),this.mainSurface.clearImages(t,t+n)}clearEob(e,t){this.mainSurface.drawBlock(e,t,this.pixelWidth,this.pixelHeight-t,this.option.background),this.mainSurface.clearImages(t,this.pixelHeight)}print(e,t,n,r,i,a,o){this.mainSurface.drawText(e,t,n,r,i,a,o)}printImage(e,t,n,r,i,a,o){this.mainSurface.drawImage(e,t,n,r,i,a,o)}printToModeline(e,t,n,r,i,a){this.modelineSurface&&this.modelineSurface.drawText(e,t,n,r,i,null,a)}touch(e){this.mainSurface.touch(),this.modelineSurface&&(this.modelineSurface.touch(),e?this.modelineSurface.activate():this.modelineSurface.deactivate())}makeSurface(e,t){switch(e){case`html`:return this.makeHTMLSurface(t);case`editor`:return this.makeEditorSurface();default:console.error(`unknown type: ${e}`)}}makeHTMLSurface(e){return new HTMLSurface({editor:this.editor,pixelX:this.pixelX,pixelY:this.pixelY,pixelWidth:this.pixelWidth,pixelHeight:this.pixelHeight,styles:getViewStyle(this.kind,this.option),option:this.option,isFloating:this.kind===`floating`,border:this.border,html:e})}makeEditorSurface(){let e=this.borderShape===`left-border`?0:this.border,t=this.kind===`floating`;return new CanvasSurface({option:this.editor.option,pixelX:this.pixelX,pixelY:this.pixelY,pixelWidth:this.pixelWidth,pixelHeight:this.pixelHeight,styles:getViewStyle(this.kind,this.option),editor:this.editor,border:e,isFloating:t,view:this,cssClassName:t&&e?`lem-editor__floating-window--bordered`:null})}makeModelineSurface(){let e=new CanvasSurface({option:this.editor.option,pixelX:this.pixelX,pixelY:this.pixelY+this.pixelHeight,pixelWidth:this.pixelWidth,pixelHeight:this.option.fontHeight,editor:this.editor,view:this,styles:{zIndex:zindex(`modeline`)},cssClassName:`lem-editor__mode-line`});return addMouseEventListeners({dom:e.mainDOM,editor:this.editor,isDraggable:!0,draggableStyle:`row-resize`}),e}changeToHTMLContent(e){this.mainSurface.constructor.name===`HTMLSurface`?this.mainSurface.update(e):(this.mainSurface.delete(),this.mainSurface=this.makeHTMLSurface(e))}changeToEditorContent(){this.mainSurface.delete(),this.mainSurface=this.makeEditorSurface()}evalIn(e){return this.mainSurface.evalIn(e)}};function isPasteKeyEvent(e){return isMacOS()?e.metaKey&&e.key===`v`:e.ctrlKey&&e.shiftKey&&e.key===`V`}var Input=class{constructor(e){let t=e.option;this.editor=e,this.composition=!1,this.ignoreKeydownAfterCompositionend=!1,this.span=document.createElement(`span`),this.span.style.color=t.foreground,this.span.style.backgroundColor=t.background,this.span.style.position=`absolute`,this.span.style.zIndex=1e6,this.span.style.top=`0`,this.span.style.left=`0`,this.span.style.font=t.font,this.input=document.createElement(`input`),this.input.style.backgroundColor=`transparent`,this.input.style.color=`transparent`,this.input.style.width=`0`,this.input.style.padding=`0`,this.input.style.margin=`0`,this.input.style.border=`none`,this.input.style.position=`absolute`,this.input.style.zIndex=`-10`,this.input.style.top=`0`,this.input.style.left=`0`,this.input.style.font=t.font,this.input.addEventListener(`blur`,e=>{this.input.focus()}),this.input.addEventListener(`input`,e=>{this.composition===!1&&(this.input.value=``,this.span.innerHTML=``,this.input.style.width=`0`,isMacOS()||this.editor.emitInputString(e.data))}),this.input.addEventListener(`paste`,async e=>{e.preventDefault();let t=e.clipboardData||window.Clipboard.data,n=t?.getData(`text`)??t?.getData(`text/plain`);if(n&&n.length>0){this.editor.emitInputString(n);return}try{if(navigator.clipboard?.readText){let e=await navigator.clipboard.readText();if(e&&e.length>0){this.editor.emitInputString(e);return}}}catch(e){console.warn(`clipboard.readText() failed:`,e)}alert(`Paste failed (permission/environment restriction`)}),this.input.addEventListener(`keydown`,e=>{if(!isPasteKeyEvent(e)&&!(e.isComposing||this.composition)&&e.key!==`Process`){if(this.ignoreKeydownAfterCompositionend&&(isSafari||isMacOS())){e.preventDefault(),this.ignoreKeydownAfterCompositionend=!1;return}if(!(!isMacOS()&&!e.ctrlKey&&!e.altKey&&e.key.length===1)&&(e.preventDefault(),e.isComposing!==!0&&e.code!==``))return setTimeout(()=>{this.composition||(this.editor.emitInput(e),this.input.value=``)},0),!1}}),this.input.addEventListener(`compositionstart`,e=>{this.composition=!0,this.span.innerHTML=this.input.value,this.input.style.width=this.span.offsetWidth+`px`}),this.input.addEventListener(`compositionupdate`,e=>{this.span.innerHTML=e.data,this.input.style.width=this.span.offsetWidth+`px`}),this.input.addEventListener(`compositionend`,e=>{this.composition=!1,this.editor.emitInputString(this.input.value),this.input.value=``,this.span.innerHTML=this.input.value,this.input.style.width=`0`,this.ignoreKeydownAfterCompositionend=!0}),document.body.appendChild(this.input),document.body.appendChild(this.span),this.input.focus()}finalize(){document.body.removeChild(this.input),document.body.removeChild(this.span)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.span.style.top=r+t+`px`,this.span.style.left=n+e+`px`,this.input.style.top=this.span.offsetTop+`px`,this.input.style.left=this.span.offsetLeft+`px`}updateForeground(e){this.span.style.color=e}updateBackground(e){this.span.style.backgroundColor=e}},MessageTable=class{constructor(){this.map=new Map}register(e,t){for(let n in t){let r=t[n];this.map.set(n,r),e.on(n,r)}}get(e){return this.map.get(e)}};function getDisplayRectangleDefault(){return[0,0,window.innerWidth,window.innerHeight]}var Editor=class{constructor({getDisplayRectangle:e=getDisplayRectangleDefault,fontName:t,fontSize:n,url:r,onExit:i,onClosed:a}){this.getDisplayRectangle=e,this.option=new Option({fontName:t,fontSize:n}),this.onExit=i,this.input=new Input(this),this.cursors=new Map,this.cursorOverlay=document.createElement(`div`),this.cursorOverlay.className=`lem-cursor`,this.cursorOverlay.style.width=this.option.fontWidth+`px`,this.cursorOverlay.style.height=this.option.fontHeight+`px`,this.cursorOverlay.style.backgroundColor=`#ffffff`,this.cursorType=`box`,this.viewMap=new Map,this.jsonrpc=new JSONRPC(r,{onClosed:()=>{a()}}),this.messageTable=new MessageTable,this.messageTable.register(this.jsonrpc,{"update-foreground":this.updateForeground.bind(this),"update-background":this.updateBackground.bind(this),"make-view":this.makeView.bind(this),"delete-view":this.deleteView.bind(this),"resize-view":this.resize.bind(this),"move-view":this.move.bind(this),"redraw-view-after":this.redrawViewAfter.bind(this),clear:this.clear.bind(this),"clear-eol":this.clearEol.bind(this),"clear-eob":this.clearEob.bind(this),put:this.put.bind(this),"put-image":this.putImage.bind(this),"modeline-put":this.modelinePut.bind(this),"update-display":this.updateDisplay.bind(this),"move-cursor":this.moveCursor.bind(this),"change-view":this.changeView.bind(this),"resize-display":this.resizeDisplay.bind(this),bulk:this.bulk.bind(this),exit:this.exitEditor.bind(this),"get-clipboard-text":this.getClipboardText.bind(this),"set-clipboard-text":this.setClipboardText.bind(this),"js-eval":this.jsEval.bind(this),"set-font":this.setFont.bind(this),"get-font":this.getFont.bind(this),"get-display-size":this.getDisplaySize.bind(this),"load-css":this.loadCSS.bind(this),"update-cursor-shape":this.updateCursorShape.bind(this)}),this.login(),this.boundedHandleResize=this.handleResize.bind(this),this.focusHiddenInput=this.focusHiddenInput.bind(this)}init(){window.addEventListener(`resize`,this.boundedHandleResize),document.getElementsByTagName(`html`)[0].style[`background-color`]=`#333`,getLemEditorElement().appendChild(this.cursorOverlay)}finalize(){window.removeEventListener(`resize`,this.boundedHandleResize),this.input.finalize(),this.cursorOverlay.parentNode&&this.cursorOverlay.parentNode.removeChild(this.cursorOverlay)}closeConnection(){this.jsonrpc.close()}emitInput(e){let t=convertKeyEvent(e);if(t){if(t.key===`]`&&t.ctrl&&!t.meta&&!t.super&&!t.shift){this.jsonrpc.notify(`input`,{kind:`abort`});return}t.key!==`Unidentified`&&this.jsonrpc.notify(`input`,{kind:`key`,value:t})}}emitInputString(e){e?this.jsonrpc.notify(`input`,{kind:`input-string`,value:e}):console.error(`unexpected argument`,e)}redrawParams(){return{size:this.getDisplaySize(),fontWidth:this.option.fontWidth,fontHeight:this.option.fontHeight,fontAscent:this.option.fontAscent}}handleResize(e){this.jsonrpc.notify(`redraw`,this.redrawParams())}focusHiddenInput(){let e=this.input?.input;if(e){try{window.focus()}catch{}requestAnimationFrame(()=>{setTimeout(()=>{e.focus({preventScroll:!0})},0)})}}sendNotification(e,t){this.jsonrpc.notify(e,t)}request(e,t,n){this.jsonrpc.request(e,t,n)}getDisplaySize(){let[e,t,n,r]=this.getDisplayRectangle();return{width:Math.floor(n/this.option.fontWidth),height:Math.floor(r/this.option.fontHeight)}}callMessage(e,t){this.messageTable.get(e)(t)}findViewById(e){return this.viewMap.get(e)}login(){this.jsonrpc.request(`login`,{size:this.getDisplaySize(),foreground:this.option.foreground,background:this.option.background,fontWidth:this.option.fontWidth,fontHeight:this.option.fontHeight,fontAscent:this.option.fontAscent},e=>{if(this.updateForeground(e.foreground),this.updateBackground(e.background),e.views)for(let t of e.views)this.makeView(t);this.jsonrpc.notify(`redraw`,this.redrawParams())})}updateForeground(e){e!=null&&(this.option.foreground=e,this.input.updateForeground(e))}updateBackground(e){if(e==null)return;this.option.background=e,this.input.updateBackground(e);let t=getLemEditorElement();t.style.backgroundColor=e}makeView({id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,use_modeline:l,kind:u,type:d,content:f,border:p,border_shape:m}){let h=new View({option:this.option,id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,useModeline:l,kind:u,type:d,content:f,border:p,borderShape:m,editor:this});this.viewMap.set(e,h)}deleteView({viewInfo:{id:e}}){this.findViewById(e).delete(),this.viewMap.delete(e)}resize({viewInfo:{id:e},width:t,height:n,pixelWidth:r,pixelHeight:i}){let a=this.findViewById(e);a?a.resize(t,n,r,i):console.warn(`resize: view not found for id ${e}`)}move({viewInfo:{id:e},x:t,y:n,pixelX:r,pixelY:i}){let a=this.findViewById(e);a?a.move(t,n,r,i):console.warn(`move: view not found for id ${e}`)}redrawViewAfter({viewInfo:{id:e},isActive:t}){this.findViewById(e).touch(t)}clear({viewInfo:{id:e}}){this.findViewById(e).clear()}clearEol({viewInfo:{id:e},x:t,y:n,height:r}){this.findViewById(e).clearEol(t,n,r)}clearEob({viewInfo:{id:e},x:t,y:n}){this.findViewById(e).clearEob(t,n)}put({viewInfo:{id:e},x:t,y:n,text:r,textWidth:i,attribute:a,font:o,height:s}){this.findViewById(e).print(t,n,r,i,a,o,s)}putImage({viewInfo:{id:e},x:t,y:n,pixelWidth:r,pixelHeight:i,clipWidth:a,clipHeight:o,url:s}){this.findViewById(e).printImage(t,n,r,i,a,o,s)}modelinePut({viewInfo:{id:e},x:t,y:n,text:r,textWidth:i,attribute:a,height:o}){this.findViewById(e).printToModeline(t,n,r,i,a,o)}updateDisplay(){}moveCursor({viewInfo:{id:e},x:t,y:n,color:r,cursorText:i,cursorForeground:a}){let o=this.findViewById(e),[s,c]=this.getDisplayRectangle(),l=o.pixelX+t,u=o.pixelY+n;this.input.move(l,u);let d=r||this.option.foreground,f=a||this.option.background,p=this.cursorOverlay;switch(this.cursorType){case`bar`:p.style.left=s+l+`px`,p.style.top=c+u+`px`,p.style.width=`2px`,p.style.height=this.option.fontHeight+`px`,p.style.backgroundColor=d,p.textContent=``,p.style.color=``,p.style.font=``,p.style.paddingTop=``;break;case`underline`:p.style.left=s+l+`px`,p.style.top=c+u+this.option.fontHeight-2+`px`,p.style.width=this.option.fontWidth+`px`,p.style.height=`2px`,p.style.backgroundColor=d,p.textContent=``,p.style.color=``,p.style.font=``,p.style.paddingTop=``;break;default:p.style.left=s+l+`px`,p.style.top=c+u+`px`,p.style.width=this.option.fontWidth+`px`,p.style.height=this.option.fontHeight+`px`,p.style.backgroundColor=d,p.style.font=this.option.font,p.style.paddingTop=textOffsetY+`px`,p.textContent=i||``,p.style.color=f;break}p.style.animation=`none`,p.offsetHeight,p.style.animation=``}updateCursorShape({cursorType:e}){this.cursorType=e||`box`}changeView({viewInfo:{id:e},type:t,content:n}){let r=this.findViewById(e);switch(t){case`html`:r.changeToHTMLContent(n);break;case`editor`:r.changeToEditorContent();break}}resizeDisplay({width:e,height:t}){let n=getLemEditorElement();n.style.width=Math.floor(e*this.option.fontWidth)+`px`,n.style.height=Math.floor(t*this.option.fontHeight)+`px`}bulk(e){for(let{method:t,argument:n}of e)this.callMessage(t,n)}exitEditor(){this.onExit&&this.onExit()}getClipboardText(){navigator.clipboard?.readText().then(e=>{this.jsonrpc.notify(`got-clipboard-text`,{text:e})})}setClipboardText({text:e}){navigator.clipboard&&navigator.clipboard.writeText(e)}jsEval({viewInfo:{id:e},code:t}){let n=this.findViewById(e).evalIn(t);return n&&n.toString()}setFont({fontName:e,fontSize:t}){this.option.setFont(e||this.option.fontName,t||this.option.fontSize),this.cursorOverlay.style.width=this.option.fontWidth+`px`,this.cursorOverlay.style.height=this.option.fontHeight+`px`,this.jsonrpc.notify(`redraw`,this.redrawParams())}getFont(){return{name:this.option.fontName,size:this.option.fontSize}}loadCSS({content:e}){let t=document.createElement(`style`);t.textContent=e,document.head.appendChild(t)}notifyToServer(e,t){this.jsonrpc.notify(`invoke`,{method:e,args:t})}},canvas=document.querySelector(`#editor`);async function main(){await Promise.all([document.fonts.load(`19px file-icons`),document.fonts.load(`19px AllTheIcons`),document.fonts.load(`19px fontawesome`),document.fonts.load(`19px material-design-icons`),document.fonts.load(`19px octicons`)]),await document.fonts.ready;let e=new Editor({canvas,fontName:`Monospace`,fontSize:18,url:`${window.location.protocol===`https:`?`wss`:`ws`}://${window.location.hostname}:${window.location.port}`,onExit:null,onClosed:null});window.addEventListener(`message`,t=>{t.data.type===`invoke-lem`&&e.notifyToServer(t.data.method,t.data.args)}),e.init()}main(); \ No newline at end of file +var __defProp=Object.defineProperty,__commonJSMin=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),__exportAll=(e,t)=>{let n={};for(var r in e)__defProp(n,r,{get:e[r],enumerable:!0});return t||__defProp(n,Symbol.toStringTag,{value:`Module`}),n};(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var require_models=__commonJSMin((e=>{var t=e&&e.__extends||(function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};return function(t,n){if(typeof n!=`function`&&n!==null)throw TypeError(`Class extends value `+String(n)+` is not a constructor or null`);e(t,n);function r(){this.constructor=t}t.prototype=n===null?Object.create(n):(r.prototype=n.prototype,new r)}})();Object.defineProperty(e,"__esModule",{value:!0}),e.createJSONRPCNotification=e.createJSONRPCRequest=e.createJSONRPCSuccessResponse=e.createJSONRPCErrorResponse=e.JSONRPCErrorCode=e.JSONRPCErrorException=e.isJSONRPCResponses=e.isJSONRPCResponse=e.isJSONRPCRequests=e.isJSONRPCRequest=e.isJSONRPCID=e.JSONRPC=void 0,e.JSONRPC=`2.0`,e.isJSONRPCID=function(e){return typeof e==`string`||typeof e==`number`||e===null},e.isJSONRPCRequest=function(t){return t.jsonrpc===e.JSONRPC&&t.method!==void 0&&t.result===void 0&&t.error===void 0},e.isJSONRPCRequests=function(t){return Array.isArray(t)&&t.every(e.isJSONRPCRequest)},e.isJSONRPCResponse=function(t){return t.jsonrpc===e.JSONRPC&&t.id!==void 0&&(t.result!==void 0||t.error!==void 0)},e.isJSONRPCResponses=function(t){return Array.isArray(t)&&t.every(e.isJSONRPCResponse)};var n=function(e,t,n){var r={code:e,message:t};return n!=null&&(r.data=n),r};e.JSONRPCErrorException=function(e){t(r,e);function r(t,n,i){var a=e.call(this,t)||this;return Object.setPrototypeOf(a,r.prototype),a.code=n,a.data=i,a}return r.prototype.toObject=function(){return n(this.code,this.message,this.data)},r}(Error),(function(e){e[e.ParseError=-32700]=`ParseError`,e[e.InvalidRequest=-32600]=`InvalidRequest`,e[e.MethodNotFound=-32601]=`MethodNotFound`,e[e.InvalidParams=-32602]=`InvalidParams`,e[e.InternalError=-32603]=`InternalError`})(e.JSONRPCErrorCode||={}),e.createJSONRPCErrorResponse=function(t,r,i,a){return{jsonrpc:e.JSONRPC,id:t,error:n(r,i,a)}},e.createJSONRPCSuccessResponse=function(t,n){return{jsonrpc:e.JSONRPC,id:t,result:n??null}},e.createJSONRPCRequest=function(t,n,r){return{jsonrpc:e.JSONRPC,id:t,method:n,params:r}},e.createJSONRPCNotification=function(t,n){return{jsonrpc:e.JSONRPC,method:t,params:n}}})),require_internal=__commonJSMin((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.DefaultErrorCode=void 0,e.DefaultErrorCode=0})),require_client=__commonJSMin((e=>{var t=e&&e.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||=Promise)(function(n,a){function o(e){try{c(r.next(e))}catch(e){a(e)}}function s(e){try{c(r.throw(e))}catch(e){a(e)}}function c(e){e.done?n(e.value):i(e.value).then(o,s)}c((r=r.apply(e,t||[])).next())})},n=e&&e.__generator||function(e,t){var n={label:0,sent:function(){if(a[0]&1)throw a[1];return a[1]},trys:[],ops:[]},r,i,a,o;return o={next:s(0),throw:s(1),return:s(2)},typeof Symbol==`function`&&(o[Symbol.iterator]=function(){return this}),o;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw TypeError(`Generator is already executing.`);for(;o&&(o=0,s[0]&&(n=0)),n;)try{if(r=1,i&&(a=s[0]&2?i.return:s[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,s[1])).done)return a;switch(i=0,a&&(s=[s[0]&2,a.value]),s[0]){case 0:case 1:a=s;break;case 4:return n.label++,{value:s[1],done:!1};case 5:n.label++,i=s[1],s=[0];continue;case 7:s=n.ops.pop(),n.trys.pop();continue;default:if((a=n.trys,!(a=a.length>0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{Object.defineProperty(e,"__esModule",{value:!0})})),require_server=__commonJSMin((e=>{var t=e&&e.__assign||function(){return t=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{var t=e&&e.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||=Promise)(function(n,a){function o(e){try{c(r.next(e))}catch(e){a(e)}}function s(e){try{c(r.throw(e))}catch(e){a(e)}}function c(e){e.done?n(e.value):i(e.value).then(o,s)}c((r=r.apply(e,t||[])).next())})},n=e&&e.__generator||function(e,t){var n={label:0,sent:function(){if(a[0]&1)throw a[1];return a[1]},trys:[],ops:[]},r,i,a,o;return o={next:s(0),throw:s(1),return:s(2)},typeof Symbol==`function`&&(o[Symbol.iterator]=function(){return this}),o;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw TypeError(`Generator is already executing.`);for(;o&&(o=0,s[0]&&(n=0)),n;)try{if(r=1,i&&(a=s[0]&2?i.return:s[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,s[1])).done)return a;switch(i=0,a&&(s=[s[0]&2,a.value]),s[0]){case 0:case 1:a=s;break;case 4:return n.label++,{value:s[1],done:!1};case 5:n.label++,i=s[1],s=[0];continue;case 7:s=n.ops.pop(),n.trys.pop();continue;default:if((a=n.trys,!(a=a.length>0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{var t=e&&e.__createBinding||(Object.create?(function(e,t,n,r){r===void 0&&(r=n);var i=Object.getOwnPropertyDescriptor(t,n);(!i||(`get`in i?!t.__esModule:i.writable||i.configurable))&&(i={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,r,i)}):(function(e,t,n,r){r===void 0&&(r=n),e[r]=t[n]})),n=e&&e.__exportStar||function(e,n){for(var r in e)r!=="default"&&!Object.prototype.hasOwnProperty.call(n,r)&&t(n,e,r)};Object.defineProperty(e,"__esModule",{value:!0}),n(require_client(),e),n(require_interfaces(),e),n(require_models(),e),n(require_server(),e),n(require_server_and_client(),e)})),import_dist=require_dist(),JSONRPC=class{constructor(e,{onConnected:t,onClosed:n}){this.url=e,this.onConnected=t,this.onClosed=n,this.messageQueue=[],this.serverAndClient=null,this.connect(),this.connectionEstablished=!1,this.timerId=null,this.closed=!1}close(){this.timerId&&clearTimeout(this.timerId),this.webSocket.close(),this.closed=!0}on(e,t){this.serverAndClient.addMethod(e,t)}async requestInternal(e,t,n){let r=await this.serverAndClient.request(e,t);n&&n(r)}requestMessageQueue(){this.messageQueue.forEach(e=>{let[t,n,r]=e;this.requestInternal(t,n,r)}),this.messageQueue=[]}request(e,t,n){this.webSocket.readyState===WebSocket.OPEN?this.requestInternal(e,t,n):this.messageQueue.push([e,t,n])}notify(e,t){switch(this.webSocket.readyState){case WebSocket.OPEN:this.serverAndClient.notify(e,t);break;case WebSocket.CLOSED:break}}connect(e){this.closed||(console.log(`connect`,this.url),this.webSocket=new WebSocket(this.url),this.serverAndClient||=new import_dist.JSONRPCServerAndClient(new import_dist.JSONRPCServer,new import_dist.JSONRPCClient(e=>{try{return this.webSocket.send(JSON.stringify(e)),Promise.resolve()}catch(e){return Promise.reject(e)}})),this.webSocket.onmessage=e=>{this.serverAndClient.receiveAndSend(JSON.parse(e.data.toString()))},this.webSocket.onopen=()=>{console.log(`WebSocket connection established`),this.connectionEstablished=!0,this.onConnected&&this.onConnected(),this.requestMessageQueue()},this.webSocket.onclose=e=>{console.error(`WebScoket closed`,e),this.serverAndClient.rejectAllPendingRequests(`Connection is closed (${e.reason}).`),this.connectionEstablished&&this.onClosed(),this.timerId=setTimeout(()=>{this.connect()},3e3)},this.webSocket.onerror=e=>{console.error(`WebSocket error:`,e),this.webSocket.close()})}},keyevent_exports=__exportAll({convertKeyEvent:()=>convertKeyEvent}),modifierKeys=[`Shift`,`Control`,`Alt`,`Meta`,`CapsLock`],convertKeyTable={Enter:`Return`,ArrowRight:`Right`,ArrowLeft:`Left`,ArrowUp:`Up`,ArrowDown:`Down`,"¡":`1`,"™":`2`,"£":`3`,"¢":`4`,"∞":`5`,"§":`6`,"¶":`7`,"•":`8`,ª:`9`,º:`0`,"–":`-`,"≠":`=`,"“":`[`,"‘":`]`,"«":`\\`,"…":`;`,æ:`'`,"≤":`,`,"≥":`.`,"÷":`/`,"⁄":`!`,"€":`@`,"‹":`#`,"›":`$`,fi:`%`,fl:`^`,"‡":`&`,"°":`*`,"·":`(`,"‚":`)`,"—":`_`,"±":`+`,"”":`{`,"’":`}`,"»":`|`,Ú:`:`,Æ:`"`,"¯":`<`,"˘":`>`,"¿":`?`,œ:`q`,"∑":`w`,"´":`e`,"®":`r`,"†":`t`,"¥":`y`,"¨":`u`,ˆ:`i`,ø:`o`,π:`p`,å:`a`,ß:`s`,"∂":`d`,ƒ:`f`,"©":`g`,"˙":`h`,"∆":`j`,"˚":`k`,"¬":`l`,Ω:`z`,"≈":`x`,ç:`c`,"√":`v`,"∫":`b`,"˜":`n`,µ:`m`,Œ:`Q`,"„":`W`,"´":`E`,"‰":`R`,ˇ:`T`,Á:`Y`,"¨":`U`,ˆ:`I`,Ø:`O`,"∏":`P`,Å:`A`,Í:`S`,Î:`D`,Ï:`F`,"˝":`G`,Ó:`H`,Ô:`J`,"":`K`,Ò:`L`,"¸":`Z`,"˛":`X`,Ç:`C`,"◊":`V`,ı:`B`,"˜":`N`,Â:`M`};function getKey(e){return e.altKey?convertKeyTable[e.key]||(e.code.startsWith(`Key`)?e.code[3].toLowerCase():null)||e.key:convertKeyTable[e.key]||e.key}function convertKeyEvent(e){return modifierKeys.indexOf(e.key)===-1?{key:getKey(e),ctrl:e.ctrlKey,meta:e.altKey,super:e.metaKey,shift:e.shiftKey}:null}var lib_exports=__exportAll({computeWidth:()=>computeWidth,eawVersion:()=>version,getEAW:()=>getEAW}),defs=[[0,31,`N`],[32,126,`Na`],[127,160,`N`],[161,161,`A`],[162,163,`Na`],[164,164,`A`],[165,166,`Na`],[167,168,`A`],[169,169,`N`],[170,170,`A`],[171,171,`N`],[172,172,`Na`],[173,174,`A`],[175,175,`Na`],[176,180,`A`],[181,181,`N`],[182,186,`A`],[187,187,`N`],[188,191,`A`],[192,197,`N`],[198,198,`A`],[199,207,`N`],[208,208,`A`],[209,214,`N`],[215,216,`A`],[217,221,`N`],[222,225,`A`],[226,229,`N`],[230,230,`A`],[231,231,`N`],[232,234,`A`],[235,235,`N`],[236,237,`A`],[238,239,`N`],[240,240,`A`],[241,241,`N`],[242,243,`A`],[244,246,`N`],[247,250,`A`],[251,251,`N`],[252,252,`A`],[253,253,`N`],[254,254,`A`],[255,256,`N`],[257,257,`A`],[258,272,`N`],[273,273,`A`],[274,274,`N`],[275,275,`A`],[276,282,`N`],[283,283,`A`],[284,293,`N`],[294,295,`A`],[296,298,`N`],[299,299,`A`],[300,304,`N`],[305,307,`A`],[308,311,`N`],[312,312,`A`],[313,318,`N`],[319,322,`A`],[323,323,`N`],[324,324,`A`],[325,327,`N`],[328,331,`A`],[332,332,`N`],[333,333,`A`],[334,337,`N`],[338,339,`A`],[340,357,`N`],[358,359,`A`],[360,362,`N`],[363,363,`A`],[364,461,`N`],[462,462,`A`],[463,463,`N`],[464,464,`A`],[465,465,`N`],[466,466,`A`],[467,467,`N`],[468,468,`A`],[469,469,`N`],[470,470,`A`],[471,471,`N`],[472,472,`A`],[473,473,`N`],[474,474,`A`],[475,475,`N`],[476,476,`A`],[477,592,`N`],[593,593,`A`],[594,608,`N`],[609,609,`A`],[610,707,`N`],[708,708,`A`],[709,710,`N`],[711,711,`A`],[712,712,`N`],[713,715,`A`],[716,716,`N`],[717,717,`A`],[718,719,`N`],[720,720,`A`],[721,727,`N`],[728,731,`A`],[732,732,`N`],[733,733,`A`],[734,734,`N`],[735,735,`A`],[736,767,`N`],[768,879,`A`],[880,912,`N`],[913,929,`A`],[930,930,`N`],[931,937,`A`],[938,944,`N`],[945,961,`A`],[962,962,`N`],[963,969,`A`],[970,1024,`N`],[1025,1025,`A`],[1026,1039,`N`],[1040,1103,`A`],[1104,1104,`N`],[1105,1105,`A`],[1106,4351,`N`],[4352,4447,`W`],[4448,8207,`N`],[8208,8208,`A`],[8209,8210,`N`],[8211,8214,`A`],[8215,8215,`N`],[8216,8217,`A`],[8218,8219,`N`],[8220,8221,`A`],[8222,8223,`N`],[8224,8226,`A`],[8227,8227,`N`],[8228,8231,`A`],[8232,8239,`N`],[8240,8240,`A`],[8241,8241,`N`],[8242,8243,`A`],[8244,8244,`N`],[8245,8245,`A`],[8246,8250,`N`],[8251,8251,`A`],[8252,8253,`N`],[8254,8254,`A`],[8255,8307,`N`],[8308,8308,`A`],[8309,8318,`N`],[8319,8319,`A`],[8320,8320,`N`],[8321,8324,`A`],[8325,8360,`N`],[8361,8361,`H`],[8362,8363,`N`],[8364,8364,`A`],[8365,8450,`N`],[8451,8451,`A`],[8452,8452,`N`],[8453,8453,`A`],[8454,8456,`N`],[8457,8457,`A`],[8458,8466,`N`],[8467,8467,`A`],[8468,8469,`N`],[8470,8470,`A`],[8471,8480,`N`],[8481,8482,`A`],[8483,8485,`N`],[8486,8486,`A`],[8487,8490,`N`],[8491,8491,`A`],[8492,8530,`N`],[8531,8532,`A`],[8533,8538,`N`],[8539,8542,`A`],[8543,8543,`N`],[8544,8555,`A`],[8556,8559,`N`],[8560,8569,`A`],[8570,8584,`N`],[8585,8585,`A`],[8586,8591,`N`],[8592,8601,`A`],[8602,8631,`N`],[8632,8633,`A`],[8634,8657,`N`],[8658,8658,`A`],[8659,8659,`N`],[8660,8660,`A`],[8661,8678,`N`],[8679,8679,`A`],[8680,8703,`N`],[8704,8704,`A`],[8705,8705,`N`],[8706,8707,`A`],[8708,8710,`N`],[8711,8712,`A`],[8713,8714,`N`],[8715,8715,`A`],[8716,8718,`N`],[8719,8719,`A`],[8720,8720,`N`],[8721,8721,`A`],[8722,8724,`N`],[8725,8725,`A`],[8726,8729,`N`],[8730,8730,`A`],[8731,8732,`N`],[8733,8736,`A`],[8737,8738,`N`],[8739,8739,`A`],[8740,8740,`N`],[8741,8741,`A`],[8742,8742,`N`],[8743,8748,`A`],[8749,8749,`N`],[8750,8750,`A`],[8751,8755,`N`],[8756,8759,`A`],[8760,8763,`N`],[8764,8765,`A`],[8766,8775,`N`],[8776,8776,`A`],[8777,8779,`N`],[8780,8780,`A`],[8781,8785,`N`],[8786,8786,`A`],[8787,8799,`N`],[8800,8801,`A`],[8802,8803,`N`],[8804,8807,`A`],[8808,8809,`N`],[8810,8811,`A`],[8812,8813,`N`],[8814,8815,`A`],[8816,8833,`N`],[8834,8835,`A`],[8836,8837,`N`],[8838,8839,`A`],[8840,8852,`N`],[8853,8853,`A`],[8854,8856,`N`],[8857,8857,`A`],[8858,8868,`N`],[8869,8869,`A`],[8870,8894,`N`],[8895,8895,`A`],[8896,8977,`N`],[8978,8978,`A`],[8979,8985,`N`],[8986,8987,`W`],[8988,9e3,`N`],[9001,9002,`W`],[9003,9192,`N`],[9193,9196,`W`],[9197,9199,`N`],[9200,9200,`W`],[9201,9202,`N`],[9203,9203,`W`],[9204,9311,`N`],[9312,9449,`A`],[9450,9450,`N`],[9451,9547,`A`],[9548,9551,`N`],[9552,9587,`A`],[9588,9599,`N`],[9600,9615,`A`],[9616,9617,`N`],[9618,9621,`A`],[9622,9631,`N`],[9632,9633,`A`],[9634,9634,`N`],[9635,9641,`A`],[9642,9649,`N`],[9650,9651,`A`],[9652,9653,`N`],[9654,9655,`A`],[9656,9659,`N`],[9660,9661,`A`],[9662,9663,`N`],[9664,9665,`A`],[9666,9669,`N`],[9670,9672,`A`],[9673,9674,`N`],[9675,9675,`A`],[9676,9677,`N`],[9678,9681,`A`],[9682,9697,`N`],[9698,9701,`A`],[9702,9710,`N`],[9711,9711,`A`],[9712,9724,`N`],[9725,9726,`W`],[9727,9732,`N`],[9733,9734,`A`],[9735,9736,`N`],[9737,9737,`A`],[9738,9741,`N`],[9742,9743,`A`],[9744,9747,`N`],[9748,9749,`W`],[9750,9755,`N`],[9756,9756,`A`],[9757,9757,`N`],[9758,9758,`A`],[9759,9791,`N`],[9792,9792,`A`],[9793,9793,`N`],[9794,9794,`A`],[9795,9799,`N`],[9800,9811,`W`],[9812,9823,`N`],[9824,9825,`A`],[9826,9826,`N`],[9827,9829,`A`],[9830,9830,`N`],[9831,9834,`A`],[9835,9835,`N`],[9836,9837,`A`],[9838,9838,`N`],[9839,9839,`A`],[9840,9854,`N`],[9855,9855,`W`],[9856,9874,`N`],[9875,9875,`W`],[9876,9885,`N`],[9886,9887,`A`],[9888,9888,`N`],[9889,9889,`W`],[9890,9897,`N`],[9898,9899,`W`],[9900,9916,`N`],[9917,9918,`W`],[9919,9919,`A`],[9920,9923,`N`],[9924,9925,`W`],[9926,9933,`A`],[9934,9934,`W`],[9935,9939,`A`],[9940,9940,`W`],[9941,9953,`A`],[9954,9954,`N`],[9955,9955,`A`],[9956,9959,`N`],[9960,9961,`A`],[9962,9962,`W`],[9963,9969,`A`],[9970,9971,`W`],[9972,9972,`A`],[9973,9973,`W`],[9974,9977,`A`],[9978,9978,`W`],[9979,9980,`A`],[9981,9981,`W`],[9982,9983,`A`],[9984,9988,`N`],[9989,9989,`W`],[9990,9993,`N`],[9994,9995,`W`],[9996,10023,`N`],[10024,10024,`W`],[10025,10044,`N`],[10045,10045,`A`],[10046,10059,`N`],[10060,10060,`W`],[10061,10061,`N`],[10062,10062,`W`],[10063,10066,`N`],[10067,10069,`W`],[10070,10070,`N`],[10071,10071,`W`],[10072,10101,`N`],[10102,10111,`A`],[10112,10132,`N`],[10133,10135,`W`],[10136,10159,`N`],[10160,10160,`W`],[10161,10174,`N`],[10175,10175,`W`],[10176,10213,`N`],[10214,10221,`Na`],[10222,10628,`N`],[10629,10630,`Na`],[10631,11034,`N`],[11035,11036,`W`],[11037,11087,`N`],[11088,11088,`W`],[11089,11092,`N`],[11093,11093,`W`],[11094,11097,`A`],[11098,11903,`N`],[11904,11929,`W`],[11930,11930,`N`],[11931,12019,`W`],[12020,12031,`N`],[12032,12245,`W`],[12246,12271,`N`],[12272,12287,`W`],[12288,12288,`F`],[12289,12350,`W`],[12351,12352,`N`],[12353,12438,`W`],[12439,12440,`N`],[12441,12543,`W`],[12544,12548,`N`],[12549,12591,`W`],[12592,12592,`N`],[12593,12686,`W`],[12687,12687,`N`],[12688,12771,`W`],[12772,12782,`N`],[12783,12830,`W`],[12831,12831,`N`],[12832,12871,`W`],[12872,12879,`A`],[12880,19903,`W`],[19904,19967,`N`],[19968,42124,`W`],[42125,42127,`N`],[42128,42182,`W`],[42183,43359,`N`],[43360,43388,`W`],[43389,44031,`N`],[44032,55203,`W`],[55204,57343,`N`],[57344,63743,`A`],[63744,64255,`W`],[64256,65023,`N`],[65024,65039,`A`],[65040,65049,`W`],[65050,65071,`N`],[65072,65106,`W`],[65107,65107,`N`],[65108,65126,`W`],[65127,65127,`N`],[65128,65131,`W`],[65132,65280,`N`],[65281,65376,`F`],[65377,65470,`H`],[65471,65473,`N`],[65474,65479,`H`],[65480,65481,`N`],[65482,65487,`H`],[65488,65489,`N`],[65490,65495,`H`],[65496,65497,`N`],[65498,65500,`H`],[65501,65503,`N`],[65504,65510,`F`],[65511,65511,`N`],[65512,65518,`H`],[65519,65532,`N`],[65533,65533,`A`],[65534,94175,`N`],[94176,94180,`W`],[94181,94191,`N`],[94192,94193,`W`],[94194,94207,`N`],[94208,100343,`W`],[100344,100351,`N`],[100352,101589,`W`],[101590,101631,`N`],[101632,101640,`W`],[101641,110575,`N`],[110576,110579,`W`],[110580,110580,`N`],[110581,110587,`W`],[110588,110588,`N`],[110589,110590,`W`],[110591,110591,`N`],[110592,110882,`W`],[110883,110897,`N`],[110898,110898,`W`],[110899,110927,`N`],[110928,110930,`W`],[110931,110932,`N`],[110933,110933,`W`],[110934,110947,`N`],[110948,110951,`W`],[110952,110959,`N`],[110960,111355,`W`],[111356,126979,`N`],[126980,126980,`W`],[126981,127182,`N`],[127183,127183,`W`],[127184,127231,`N`],[127232,127242,`A`],[127243,127247,`N`],[127248,127277,`A`],[127278,127279,`N`],[127280,127337,`A`],[127338,127343,`N`],[127344,127373,`A`],[127374,127374,`W`],[127375,127376,`A`],[127377,127386,`W`],[127387,127404,`A`],[127405,127487,`N`],[127488,127490,`W`],[127491,127503,`N`],[127504,127547,`W`],[127548,127551,`N`],[127552,127560,`W`],[127561,127567,`N`],[127568,127569,`W`],[127570,127583,`N`],[127584,127589,`W`],[127590,127743,`N`],[127744,127776,`W`],[127777,127788,`N`],[127789,127797,`W`],[127798,127798,`N`],[127799,127868,`W`],[127869,127869,`N`],[127870,127891,`W`],[127892,127903,`N`],[127904,127946,`W`],[127947,127950,`N`],[127951,127955,`W`],[127956,127967,`N`],[127968,127984,`W`],[127985,127987,`N`],[127988,127988,`W`],[127989,127991,`N`],[127992,128062,`W`],[128063,128063,`N`],[128064,128064,`W`],[128065,128065,`N`],[128066,128252,`W`],[128253,128254,`N`],[128255,128317,`W`],[128318,128330,`N`],[128331,128334,`W`],[128335,128335,`N`],[128336,128359,`W`],[128360,128377,`N`],[128378,128378,`W`],[128379,128404,`N`],[128405,128406,`W`],[128407,128419,`N`],[128420,128420,`W`],[128421,128506,`N`],[128507,128591,`W`],[128592,128639,`N`],[128640,128709,`W`],[128710,128715,`N`],[128716,128716,`W`],[128717,128719,`N`],[128720,128722,`W`],[128723,128724,`N`],[128725,128727,`W`],[128728,128731,`N`],[128732,128735,`W`],[128736,128746,`N`],[128747,128748,`W`],[128749,128755,`N`],[128756,128764,`W`],[128765,128991,`N`],[128992,129003,`W`],[129004,129007,`N`],[129008,129008,`W`],[129009,129291,`N`],[129292,129338,`W`],[129339,129339,`N`],[129340,129349,`W`],[129350,129350,`N`],[129351,129535,`W`],[129536,129647,`N`],[129648,129660,`W`],[129661,129663,`N`],[129664,129672,`W`],[129673,129679,`N`],[129680,129725,`W`],[129726,129726,`N`],[129727,129733,`W`],[129734,129741,`N`],[129742,129755,`W`],[129756,129759,`N`],[129760,129768,`W`],[129769,129775,`N`],[129776,129784,`W`],[129785,131071,`N`],[131072,196605,`W`],[196606,196607,`N`],[196608,262141,`W`],[262142,917759,`N`],[917760,917999,`A`],[918e3,983039,`N`],[983040,1048573,`A`],[1048574,1048575,`N`],[1048576,1114109,`A`],[1114110,1114111,`N`]],version=`15.1.0`;function getEAWOfCodePoint(e){let t=0,n=defs.length-1;for(;t!==n;){let r=t+(n-t>>1),[i,a,o]=defs[r];if(ea)t=r+1;else return o}return defs[t][2]}function getEAW(e,t=0){let n=e.codePointAt(t);if(n!==void 0)return getEAWOfCodePoint(n)}var defaultWidths={N:1,Na:1,W:2,F:2,H:1,A:1};function computeWidth(e,t){let n=0;for(let r of e){let e=getEAW(r);n+=t&&t[e]||defaultWidths[e]}return n}var textOffsetY=5,isSafari=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);function isWideChar(e){switch(getEAW(e)){case`F`:case`W`:return!0;default:return!1}}function isMacOS(){return window.navigator.userAgent.indexOf(`Mac OS X`)!==-1}function computeFontSize(e){let t=document.createElement(`canvas`).getContext(`2d`);t.font=e;let n=t.measureText(`W`);return[Math.floor(n.width),Math.round(n.fontBoundingBoxAscent+textOffsetY+(n.emHeightDescent||0)),Math.round(n.fontBoundingBoxAscent+textOffsetY)]}function drawBlock({ctx:e,x:t,y:n,width:r,height:i,style:a}){e.fillStyle=a,e.fillRect(t,n,r,i)}function drawText({ctx:e,x:t,y:n,text:r,font:i,style:a,option:o}){n+=Math.round(textOffsetY),e.fillStyle=a,e.font=i,e.textBaseline=`top`;for(let i of r)isWideChar(i)?(e.fillText(i,t,n,o.fontWidth*2),t+=o.fontWidth*2):(e.fillText(i,t,n,o.fontWidth),t+=o.fontWidth)}function drawHorizontalLine({ctx:e,x:t,y:n,width:r,style:i,lineWidth:a=1}){e.strokeStyle=i,e.lineWidth=a,e.setLineDash=[],e.beginPath(),e.moveTo(t,n),e.lineTo(t+r,n),e.stroke()}var Option=class{constructor({fontName:e,fontSize:t}){this.setFont(e,t),this.foreground=`#cccccc`,this.background=`#2d2d2d`}setFont(e,t){let n=t+`px `+e,[r,i,a]=computeFontSize(n);this.fontName=e,this.fontSize=t,this.fontWidth=r,this.fontHeight=i,this.fontAscent=a,this.font=n}};function getLemEditorElement(){return document.getElementById(`lem-editor`)}function normalizeWheelDelta(e,t,n,r){switch(n){case 0:return{dx:e/r,dy:t/r};case 2:return{dx:e*20,dy:t*20};default:return{dx:e,dy:t}}}function extractWholeLines(e,t){let n=Math.trunc(e),r=Math.trunc(t);return{scrollX:n,scrollY:r,remainderX:e-n,remainderY:t-r}}function cursorPosition(e,t){let[n,r]=t.getDisplayRectangle(),i=e.clientX-n,a=e.clientY-r;return{pixelX:i,pixelY:a,x:Math.floor(i/t.option.fontWidth),y:Math.floor(a/t.option.fontHeight)}}function makeWheelHandler(e){let t={x:0,y:0},n=!1,r={pixelX:0,pixelY:0,x:0,y:0};return i=>{i.preventDefault(),r=cursorPosition(i,e);let{dx:a,dy:o}=normalizeWheelDelta(i.deltaX,i.deltaY,i.deltaMode,e.option.fontHeight);t={x:t.x+a,y:t.y+o},n||(n=!0,requestAnimationFrame(()=>{n=!1;let{scrollX:i,scrollY:a,remainderX:o,remainderY:s}=extractWholeLines(t.x,t.y);t={x:o,y:s},(i!==0||a!==0)&&e.jsonrpc.notify(`input`,{kind:`wheel`,value:{...r,wheelX:-i,wheelY:-a}})}))}}function addMouseEventListeners({dom:e,editor:t,isDraggable:n,draggableStyle:r}){e.addEventListener(`contextmenu`,e=>{e.preventDefault()});let i=(e,n)=>{e.preventDefault();let[r,i]=t.getDisplayRectangle(),a=e.clientX-r,o=e.clientY-i,s=Math.floor(a/t.option.fontWidth),c=Math.floor(o/t.option.fontHeight);t.jsonrpc.notify(`input`,{kind:n,value:{x:s,y:c,pixelX:a,pixelY:o,button:e.button,clicks:e.detail}})};e.addEventListener(`mousedown`,e=>{n&&(document.body.style.cursor=r),t.focusHiddenInput(),i(e,`mousedown`)}),e.addEventListener(`mouseup`,e=>{n&&(document.body.style.cursor=`default`),i(e,`mouseup`)});let a=0;e.addEventListener(`mousemove`,e=>{e.preventDefault();let n=Date.now();if(n-a>50){a=n;let[r,i]=t.getDisplayRectangle(),o=e.clientX-r,s=e.clientY-i,c=Math.floor(o/t.option.fontWidth),l=Math.floor(s/t.option.fontHeight);t.jsonrpc.notify(`input`,{kind:`mousemove`,value:{x:c,y:l,pixelX:o,pixelY:s,button:e.buttons===0?null:e.buttons-1}})}}),n&&(e.addEventListener(`mouseover`,()=>{document.body.style.cursor=r}),e.addEventListener(`mouseout`,e=>{e.buttons!==1&&(document.body.style.cursor=`default`)})),e.addEventListener(`wheel`,makeWheelHandler(t))}var zIndexTable={"floating-window":200,modeline:100,"vertical-border":100,"horizontal-border":101};function zindex(e){return zIndexTable[e]||0}var borderOffsetX=5,borderOffsetY=10,BaseSurface=class{constructor({editor:e}){this.editor=e,this.mainDOM=null,this.wrapper=null}delete(){this.wrapper?getLemEditorElement().removeChild(this.wrapper):getLemEditorElement().removeChild(this.mainDOM)}setupDOM({dom:e,isFloating:t,border:n,cssClassName:r}){this.mainDOM=e,t&&n?(this.wrapper=document.createElement(`div`),r&&(this.wrapper.className=r),this.wrapper.style.position=`absolute`,this.wrapper.style.backgroundColor=this.editor.option.background,this.wrapper.style.zIndex=zindex(`floating-window`),this.wrapper.appendChild(e),getLemEditorElement().appendChild(this.wrapper)):(r&&(e.className=r),getLemEditorElement().appendChild(e))}move(e,t){let[n,r]=this.editor.getDisplayRectangle(),i=Math.floor(n+e),a=Math.floor(r+t);this.wrapper?(this.wrapper.style.left=i-borderOffsetX+`px`,this.wrapper.style.top=a-borderOffsetY+`px`,this.mainDOM.style.left=borderOffsetX+`px`,this.mainDOM.style.top=borderOffsetY+`px`):(this.mainDOM.style.left=i+`px`,this.mainDOM.style.top=a+`px`)}_resize(e,t){let n=window.devicePixelRatio||1;this.mainDOM.width=e*n,this.mainDOM.height=t*n,this.mainDOM.style.width=e+`px`,this.mainDOM.style.height=t+`px`,this.wrapper&&(this.wrapper.style.width=e+borderOffsetX*2+`px`,this.wrapper.style.height=t+borderOffsetY*2+`px`)}drawBlock(e,t,n,r,i){}drawText(e,t,n,r,i,a){}drawImage(e,t,n,r,i,a,o){}clearImages(e,t){}clearAllImages(){}touch(){}evalIn(code){return eval(code)}},CanvasSurface=class extends BaseSurface{constructor({editor:e,view:t,pixelX:n,pixelY:r,pixelWidth:i,pixelHeight:a,styles:o,isFloating:s,border:c,cssClassName:l}){super({editor:e});let u=this.setupCanvas(o);this.setupDOM({dom:u,isFloating:s,border:c,cssClassName:l}),this.move(n,r),this.resize(i,a),this.drawingQueue=[],addMouseEventListeners({dom:u,editor:e})}setupCanvas(e){let t=document.createElement(`canvas`);if(t.style.position=`absolute`,e)for(let n in e)t.style[n]=e[n];return t}resize(e,t){this._resize(e,t);let n=window.devicePixelRatio||1;this.mainDOM.getContext(`2d`).scale(n,n)}move(e,t){if(super.move(e,t),this.imageEls)for(let[,e]of this.imageEls)this.positionImage(e)}delete(){this.clearAllImages(),super.delete()}drawBlock(e,t,n,r,i){this.drawingQueue.push(function(a){drawBlock({ctx:a,x:e,y:t,width:n,height:r,style:i})})}drawText(e,t,n,r,i,a){let o=this.editor.option,s=o.fontHeight;this.drawingQueue.push(function(c){if(a=a?`${o.fontSize}px ${a}`:o.font,!i)drawBlock({ctx:c,x:e,y:t,width:r,height:s,style:o.background}),drawText({ctx:c,x:e,y:t,text:n,style:o.foreground,font:a,option:o});else{let{foreground:l,background:u,bold:d,reverse:f,underline:p,cursor:m}=i;if(l||=o.foreground,u||=o.background,f){let e=u;u=l,l=e}m&&(u=o.background),drawBlock({ctx:c,x:e,y:t,width:r,height:s,style:u}),drawText({ctx:c,x:e,y:t,text:n,style:l,font:d?`bold `+a:a,option:o}),p&&drawHorizontalLine({ctx:c,x:e,y:t+o.fontHeight-2,width:r,style:typeof p==`string`?p:l,lineWidth:2})}})}imageBaseLeft(){return parseFloat(this.mainDOM.style.left)||0}imageBaseTop(){return parseFloat(this.mainDOM.style.top)||0}drawImage(e,t,n,r,i,a,o){this.imageEls||=new Map;let s=e+`,`+t,c=this.imageEls.get(s);if(c&&c.url!==o&&(c.el.remove(),this.imageEls.delete(s),c=null),!c){let e=document.createElement(`img`);e.style.position=`absolute`,e.style.pointerEvents=`none`,e.style.zIndex=`1`,e.src=o,this.mainDOM.parentNode.appendChild(e),c={el:e,url:o},this.imageEls.set(s,c)}c.x=e,c.y=t,c.width=n,c.height=r,c.clipWidth=i,c.clipHeight=a,this.positionImage(c)}positionImage(e){e.el.style.left=this.imageBaseLeft()+e.x+`px`,e.el.style.top=this.imageBaseTop()+e.y+`px`,e.el.style.width=e.width+`px`,e.el.style.height=e.height+`px`;let t=e.clipWidth==null?0:Math.max(0,e.width-e.clipWidth),n=e.clipHeight==null?0:Math.max(0,e.height-e.clipHeight);e.el.style.clipPath=t>0||n>0?`inset(0px ${t}px ${n}px 0px)`:``}clearImages(e,t){if(this.imageEls)for(let[n,r]of this.imageEls){let i=r.y+(r.height||0);r.ye&&(r.el.remove(),this.imageEls.delete(n))}}clearAllImages(){if(this.imageEls){for(let[,e]of this.imageEls)e.el.remove();this.imageEls.clear()}}touch(){let e=this.mainDOM.getContext(`2d`);for(let t of this.drawingQueue)t(e);this.drawingQueue=[]}activate(){this.mainDOM.dataset.store=`active`}deactivate(){this.mainDOM.dataset.store=`inactive`}},HTMLSurface=class extends BaseSurface{constructor({editor:e,pixelX:t,pixelY:n,pixelWidth:r,pixelHeight:i,styles:a,option:o,isFloating:s,border:c,html:l}){super({editor:e});let u=document.createElement(`iframe`);this.setupDOM({dom:u,isFloating:s,border:c}),u.style.position=`absolute`,u.style.backgroundColor=o.background,u.setAttribute(`sandbox`,`allow-scripts allow-same-origin`),u.srcdoc=l,u.addEventListener(`load`,()=>{let e=u.contentWindow;e.invokeLem=(e,t)=>parent.postMessage({type:`invoke-lem`,method:e,args:t})}),this.iframe=u,this.move(t,n),this.resize(r,i)}resize(e,t){this._resize(e,t)}update(e){let t=this.iframe.contentWindow.scrollY;this.iframe.srcdoc=e,this.iframe.onload=()=>{this.iframe.onload=null,this.iframe.contentWindow.scrollTo(0,t)}}evalIn(e){return this.iframe.contentWindow.eval(e)}},VerticalBorder=class{constructor({x:e,y:t,height:n,option:r,editor:i}){this.option=r,this.editor=i,this.line=document.createElement(`div`),this.line.className=`lem-editor__vertical-border`,this.line.style.height=n+`px`,this.line.style.position=`absolute`,this.line.style.zIndex=zindex(`vertical-border`),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:i,isDraggable:!0,draggableStyle:`col-resize`})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.line.style.left=Math.floor(n+e-this.option.fontWidth/2)+`px`,this.line.style.top=r+t+`px`}resize(e){this.line.style.height=e+`px`}},HorizontalBorder=class{constructor({x:e,y:t,width:n,option:r,editor:i}){this.option=r,this.editor=i,this.line=document.createElement(`div`),this.line.className=`lem-editor__horizontal-border`,this.line.style.width=n+`px`,this.line.style.position=`absolute`,this.line.style.zIndex=zindex(`horizontal-border`),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:i,isDraggable:!0,draggableStyle:`row-resize`})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.line.style.left=n+e+`px`,this.line.style.top=Math.floor(r+t-4)+`px`}resize(e){this.line.style.width=e+`px`}},viewStyles={header:()=>{},tile:()=>{},floating:e=>({boxSizing:`border-box`,borderColor:e.foreground,backgroundColor:e.background})};function getViewStyle(e,t){return viewStyles[e](t)||{}}var View=class{constructor({id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,useModeline:l,kind:u,type:d,content:f,border:p,borderShape:m,option:h,editor:g}){switch(this.option=h,this.id=e,this.x=t,this.y=n,this.width=r,this.height=i,this.pixelX=a,this.pixelY=o,this.pixelWidth=s,this.pixelHeight=c,this.useModeline=l,this.kind=u,this.type=d,this.border=p,this.borderShape=m,this.editor=g,this.bottomBar=null,this.leftsideBar=null,u){case`tile`:this.mainSurface=this.makeSurface(d,f),this.leftSideBar=new VerticalBorder({x:a,y:o,height:c+(l?h.fontHeight:0),option:h,editor:g}),l||(this.bottomBar=new HorizontalBorder({x:a,y:o+c-h.fontHeight,width:s,option:h,editor:g}));break;case`header`:this.mainSurface=this.makeSurface(d,f);break;case`floating`:this.mainSurface=this.makeSurface(d,f),m===`left-border`&&(this.leftSideBar=new VerticalBorder({x:a,y:o,height:c,option:h,editor:g}));break}this.modelineSurface=l?this.makeModelineSurface():null}delete(){this.mainSurface.delete(),this.modelineSurface&&this.modelineSurface.delete(),this.leftSideBar&&this.leftSideBar.delete(),this.bottomBar&&this.bottomBar.delete()}move(e,t,n,r){this.x=e,this.y=t,this.pixelX=n,this.pixelY=r,this.mainSurface.move(n,r),this.modelineSurface&&this.modelineSurface.move(n,r+this.pixelHeight),this.leftSideBar&&this.leftSideBar.move(n,r),this.bottomBar&&this.bottomBar.move(n,r+this.pixelHeight)}resize(e,t,n,r){this.width=e,this.height=t,this.pixelWidth=n,this.pixelHeight=r,this.mainSurface.resize(n,r),this.modelineSurface&&(this.modelineSurface.move(this.pixelX,this.pixelY+r),this.modelineSurface.resize(n,this.option.fontHeight)),this.leftSideBar&&this.leftSideBar.resize(r+(this.modelineSurface?this.option.fontHeight:0)),this.bottomBar&&this.bottomBar.resize(n)}clear(){this.mainSurface.drawBlock(0,0,this.pixelWidth,this.pixelHeight,this.option.background),this.mainSurface.clearImages(0,this.pixelHeight)}clearEol(e,t,n){n??=this.option.fontHeight,this.mainSurface.drawBlock(e,t,this.pixelWidth-e,n,this.option.background),this.mainSurface.clearImages(t,t+n)}clearEob(e,t){this.mainSurface.drawBlock(e,t,this.pixelWidth,this.pixelHeight-t,this.option.background),this.mainSurface.clearImages(t,this.pixelHeight)}print(e,t,n,r,i,a){this.mainSurface.drawText(e,t,n,r,i,a)}drawBlock(e,t,n,r,i){this.mainSurface.drawBlock(e,t,n,r,i||this.option.background)}drawBlockOnModeline(e,t,n,r,i){this.modelineSurface&&this.modelineSurface.drawBlock(e,t,n,r,i||this.option.background)}printImage(e,t,n,r,i,a,o){this.mainSurface.drawImage(e,t,n,r,i,a,o)}printToModeline(e,t,n,r,i){this.modelineSurface&&this.modelineSurface.drawText(e,t,n,r,i,null)}touch(e){this.mainSurface.touch(),this.modelineSurface&&(this.modelineSurface.touch(),e?this.modelineSurface.activate():this.modelineSurface.deactivate())}makeSurface(e,t){switch(e){case`html`:return this.makeHTMLSurface(t);case`editor`:return this.makeEditorSurface();default:console.error(`unknown type: ${e}`)}}makeHTMLSurface(e){return new HTMLSurface({editor:this.editor,pixelX:this.pixelX,pixelY:this.pixelY,pixelWidth:this.pixelWidth,pixelHeight:this.pixelHeight,styles:getViewStyle(this.kind,this.option),option:this.option,isFloating:this.kind===`floating`,border:this.border,html:e})}makeEditorSurface(){let e=this.borderShape===`left-border`?0:this.border,t=this.kind===`floating`;return new CanvasSurface({option:this.editor.option,pixelX:this.pixelX,pixelY:this.pixelY,pixelWidth:this.pixelWidth,pixelHeight:this.pixelHeight,styles:getViewStyle(this.kind,this.option),editor:this.editor,border:e,isFloating:t,view:this,cssClassName:t&&e?`lem-editor__floating-window--bordered`:null})}makeModelineSurface(){let e=new CanvasSurface({option:this.editor.option,pixelX:this.pixelX,pixelY:this.pixelY+this.pixelHeight,pixelWidth:this.pixelWidth,pixelHeight:this.option.fontHeight,editor:this.editor,view:this,styles:{zIndex:zindex(`modeline`)},cssClassName:`lem-editor__mode-line`});return addMouseEventListeners({dom:e.mainDOM,editor:this.editor,isDraggable:!0,draggableStyle:`row-resize`}),e}changeToHTMLContent(e){this.mainSurface.constructor.name===`HTMLSurface`?this.mainSurface.update(e):(this.mainSurface.delete(),this.mainSurface=this.makeHTMLSurface(e))}changeToEditorContent(){this.mainSurface.delete(),this.mainSurface=this.makeEditorSurface()}evalIn(e){return this.mainSurface.evalIn(e)}};function isPasteKeyEvent(e){return isMacOS()?e.metaKey&&e.key===`v`:e.ctrlKey&&e.shiftKey&&e.key===`V`}var Input=class{constructor(e){let t=e.option;this.editor=e,this.composition=!1,this.ignoreKeydownAfterCompositionend=!1,this.span=document.createElement(`span`),this.span.style.color=t.foreground,this.span.style.backgroundColor=t.background,this.span.style.position=`absolute`,this.span.style.zIndex=1e6,this.span.style.top=`0`,this.span.style.left=`0`,this.span.style.font=t.font,this.input=document.createElement(`input`),this.input.style.backgroundColor=`transparent`,this.input.style.color=`transparent`,this.input.style.width=`0`,this.input.style.padding=`0`,this.input.style.margin=`0`,this.input.style.border=`none`,this.input.style.position=`absolute`,this.input.style.zIndex=`-10`,this.input.style.top=`0`,this.input.style.left=`0`,this.input.style.font=t.font,this.input.addEventListener(`blur`,e=>{this.input.focus()}),this.input.addEventListener(`input`,e=>{this.composition===!1&&(this.input.value=``,this.span.innerHTML=``,this.input.style.width=`0`,isMacOS()||this.editor.emitInputString(e.data))}),this.input.addEventListener(`paste`,async e=>{e.preventDefault();let t=e.clipboardData||window.Clipboard.data,n=t?.getData(`text`)??t?.getData(`text/plain`);if(n&&n.length>0){this.editor.emitInputString(n);return}try{if(navigator.clipboard?.readText){let e=await navigator.clipboard.readText();if(e&&e.length>0){this.editor.emitInputString(e);return}}}catch(e){console.warn(`clipboard.readText() failed:`,e)}alert(`Paste failed (permission/environment restriction`)}),this.input.addEventListener(`keydown`,e=>{if(!isPasteKeyEvent(e)&&!(e.isComposing||this.composition)&&e.key!==`Process`){if(this.ignoreKeydownAfterCompositionend&&(isSafari||isMacOS())){e.preventDefault(),this.ignoreKeydownAfterCompositionend=!1;return}if(!(!isMacOS()&&!e.ctrlKey&&!e.altKey&&e.key.length===1)&&(e.preventDefault(),e.isComposing!==!0&&e.code!==``))return setTimeout(()=>{this.composition||(this.editor.emitInput(e),this.input.value=``)},0),!1}}),this.input.addEventListener(`compositionstart`,e=>{this.composition=!0,this.span.innerHTML=this.input.value,this.input.style.width=this.span.offsetWidth+`px`}),this.input.addEventListener(`compositionupdate`,e=>{this.span.innerHTML=e.data,this.input.style.width=this.span.offsetWidth+`px`}),this.input.addEventListener(`compositionend`,e=>{this.composition=!1,this.editor.emitInputString(this.input.value),this.input.value=``,this.span.innerHTML=this.input.value,this.input.style.width=`0`,this.ignoreKeydownAfterCompositionend=!0}),document.body.appendChild(this.input),document.body.appendChild(this.span),this.input.focus()}finalize(){document.body.removeChild(this.input),document.body.removeChild(this.span)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.span.style.top=r+t+`px`,this.span.style.left=n+e+`px`,this.input.style.top=this.span.offsetTop+`px`,this.input.style.left=this.span.offsetLeft+`px`}updateForeground(e){this.span.style.color=e}updateBackground(e){this.span.style.backgroundColor=e}},MessageTable=class{constructor(){this.map=new Map}register(e,t){for(let n in t){let r=t[n];this.map.set(n,r),e.on(n,r)}}get(e){return this.map.get(e)}};function getDisplayRectangleDefault(){return[0,0,window.innerWidth,window.innerHeight]}var Editor=class{constructor({getDisplayRectangle:e=getDisplayRectangleDefault,fontName:t,fontSize:n,url:r,onExit:i,onClosed:a}){this.getDisplayRectangle=e,this.option=new Option({fontName:t,fontSize:n}),this.onExit=i,this.input=new Input(this),this.cursors=new Map,this.cursorOverlay=document.createElement(`div`),this.cursorOverlay.className=`lem-cursor`,this.cursorOverlay.style.width=this.option.fontWidth+`px`,this.cursorOverlay.style.height=this.option.fontHeight+`px`,this.cursorOverlay.style.backgroundColor=`#ffffff`,this.cursorType=`box`,this.viewMap=new Map,this.jsonrpc=new JSONRPC(r,{onClosed:()=>{a()}}),this.messageTable=new MessageTable,this.messageTable.register(this.jsonrpc,{"update-foreground":this.updateForeground.bind(this),"update-background":this.updateBackground.bind(this),"make-view":this.makeView.bind(this),"delete-view":this.deleteView.bind(this),"resize-view":this.resize.bind(this),"move-view":this.move.bind(this),"redraw-view-after":this.redrawViewAfter.bind(this),clear:this.clear.bind(this),"clear-eol":this.clearEol.bind(this),"clear-eob":this.clearEob.bind(this),put:this.put.bind(this),"put-image":this.putImage.bind(this),"modeline-put":this.modelinePut.bind(this),"draw-block":this.drawBlock.bind(this),"modeline-draw-block":this.modelineDrawBlock.bind(this),"update-display":this.updateDisplay.bind(this),"move-cursor":this.moveCursor.bind(this),"change-view":this.changeView.bind(this),"resize-display":this.resizeDisplay.bind(this),bulk:this.bulk.bind(this),exit:this.exitEditor.bind(this),"get-clipboard-text":this.getClipboardText.bind(this),"set-clipboard-text":this.setClipboardText.bind(this),"js-eval":this.jsEval.bind(this),"set-font":this.setFont.bind(this),"get-font":this.getFont.bind(this),"get-display-size":this.getDisplaySize.bind(this),"load-css":this.loadCSS.bind(this),"update-cursor-shape":this.updateCursorShape.bind(this)}),this.login(),this.boundedHandleResize=this.handleResize.bind(this),this.focusHiddenInput=this.focusHiddenInput.bind(this)}init(){window.addEventListener(`resize`,this.boundedHandleResize),document.getElementsByTagName(`html`)[0].style[`background-color`]=`#333`,getLemEditorElement().appendChild(this.cursorOverlay)}finalize(){window.removeEventListener(`resize`,this.boundedHandleResize),this.input.finalize(),this.cursorOverlay.parentNode&&this.cursorOverlay.parentNode.removeChild(this.cursorOverlay)}closeConnection(){this.jsonrpc.close()}emitInput(e){let t=convertKeyEvent(e);if(t){if(t.key===`]`&&t.ctrl&&!t.meta&&!t.super&&!t.shift){this.jsonrpc.notify(`input`,{kind:`abort`});return}t.key!==`Unidentified`&&this.jsonrpc.notify(`input`,{kind:`key`,value:t})}}emitInputString(e){e?this.jsonrpc.notify(`input`,{kind:`input-string`,value:e}):console.error(`unexpected argument`,e)}redrawParams(){return{size:this.getDisplaySize(),fontWidth:this.option.fontWidth,fontHeight:this.option.fontHeight,fontAscent:this.option.fontAscent}}handleResize(e){this.jsonrpc.notify(`redraw`,this.redrawParams())}focusHiddenInput(){let e=this.input?.input;if(e){try{window.focus()}catch{}requestAnimationFrame(()=>{setTimeout(()=>{e.focus({preventScroll:!0})},0)})}}sendNotification(e,t){this.jsonrpc.notify(e,t)}request(e,t,n){this.jsonrpc.request(e,t,n)}getDisplaySize(){let[e,t,n,r]=this.getDisplayRectangle();return{width:Math.floor(n/this.option.fontWidth),height:Math.floor(r/this.option.fontHeight)}}callMessage(e,t){this.messageTable.get(e)(t)}findViewById(e){return this.viewMap.get(e)}login(){this.jsonrpc.request(`login`,{size:this.getDisplaySize(),foreground:this.option.foreground,background:this.option.background,fontWidth:this.option.fontWidth,fontHeight:this.option.fontHeight,fontAscent:this.option.fontAscent},e=>{if(this.updateForeground(e.foreground),this.updateBackground(e.background),e.views)for(let t of e.views)this.makeView(t);this.jsonrpc.notify(`redraw`,this.redrawParams())})}updateForeground(e){e!=null&&(this.option.foreground=e,this.input.updateForeground(e))}updateBackground(e){if(e==null)return;this.option.background=e,this.input.updateBackground(e);let t=getLemEditorElement();t.style.backgroundColor=e}makeView({id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,use_modeline:l,kind:u,type:d,content:f,border:p,border_shape:m}){let h=new View({option:this.option,id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,useModeline:l,kind:u,type:d,content:f,border:p,borderShape:m,editor:this});this.viewMap.set(e,h)}deleteView({viewInfo:{id:e}}){this.findViewById(e).delete(),this.viewMap.delete(e)}resize({viewInfo:{id:e},width:t,height:n,pixelWidth:r,pixelHeight:i}){let a=this.findViewById(e);a?a.resize(t,n,r,i):console.warn(`resize: view not found for id ${e}`)}move({viewInfo:{id:e},x:t,y:n,pixelX:r,pixelY:i}){let a=this.findViewById(e);a?a.move(t,n,r,i):console.warn(`move: view not found for id ${e}`)}redrawViewAfter({viewInfo:{id:e},isActive:t}){this.findViewById(e).touch(t)}clear({viewInfo:{id:e}}){this.findViewById(e).clear()}clearEol({viewInfo:{id:e},x:t,y:n,height:r}){this.findViewById(e).clearEol(t,n,r)}clearEob({viewInfo:{id:e},x:t,y:n}){this.findViewById(e).clearEob(t,n)}put({viewInfo:{id:e},x:t,y:n,text:r,textWidth:i,attribute:a,font:o}){this.findViewById(e).print(t,n,r,i,a,o)}drawBlock({viewInfo:{id:e},x:t,y:n,width:r,height:i,color:a}){this.findViewById(e).drawBlock(t,n,r,i,a)}modelineDrawBlock({viewInfo:{id:e},x:t,y:n,width:r,height:i,color:a}){this.findViewById(e).drawBlockOnModeline(t,n,r,i,a)}putImage({viewInfo:{id:e},x:t,y:n,pixelWidth:r,pixelHeight:i,clipWidth:a,clipHeight:o,url:s}){this.findViewById(e).printImage(t,n,r,i,a,o,s)}modelinePut({viewInfo:{id:e},x:t,y:n,text:r,textWidth:i,attribute:a}){this.findViewById(e).printToModeline(t,n,r,i,a)}updateDisplay(){}moveCursor({viewInfo:{id:e},x:t,y:n,color:r,cursorText:i,cursorForeground:a}){let o=this.findViewById(e),[s,c]=this.getDisplayRectangle(),l=o.pixelX+t,u=o.pixelY+n;this.input.move(l,u);let d=r||this.option.foreground,f=a||this.option.background,p=this.cursorOverlay;switch(this.cursorType){case`bar`:p.style.left=s+l+`px`,p.style.top=c+u+`px`,p.style.width=`2px`,p.style.height=this.option.fontHeight+`px`,p.style.backgroundColor=d,p.textContent=``,p.style.color=``,p.style.font=``,p.style.paddingTop=``;break;case`underline`:p.style.left=s+l+`px`,p.style.top=c+u+this.option.fontHeight-2+`px`,p.style.width=this.option.fontWidth+`px`,p.style.height=`2px`,p.style.backgroundColor=d,p.textContent=``,p.style.color=``,p.style.font=``,p.style.paddingTop=``;break;default:p.style.left=s+l+`px`,p.style.top=c+u+`px`,p.style.width=this.option.fontWidth+`px`,p.style.height=this.option.fontHeight+`px`,p.style.backgroundColor=d,p.style.font=this.option.font,p.style.paddingTop=textOffsetY+`px`,p.textContent=i||``,p.style.color=f;break}p.style.animation=`none`,p.offsetHeight,p.style.animation=``}updateCursorShape({cursorType:e}){this.cursorType=e||`box`}changeView({viewInfo:{id:e},type:t,content:n}){let r=this.findViewById(e);switch(t){case`html`:r.changeToHTMLContent(n);break;case`editor`:r.changeToEditorContent();break}}resizeDisplay({width:e,height:t}){let n=getLemEditorElement();n.style.width=Math.floor(e*this.option.fontWidth)+`px`,n.style.height=Math.floor(t*this.option.fontHeight)+`px`}bulk(e){for(let{method:t,argument:n}of e)this.callMessage(t,n)}exitEditor(){this.onExit&&this.onExit()}getClipboardText(){navigator.clipboard?.readText().then(e=>{this.jsonrpc.notify(`got-clipboard-text`,{text:e})})}setClipboardText({text:e}){navigator.clipboard&&navigator.clipboard.writeText(e)}jsEval({viewInfo:{id:e},code:t}){let n=this.findViewById(e).evalIn(t);return n&&n.toString()}setFont({fontName:e,fontSize:t}){this.option.setFont(e||this.option.fontName,t||this.option.fontSize),this.cursorOverlay.style.width=this.option.fontWidth+`px`,this.cursorOverlay.style.height=this.option.fontHeight+`px`,this.jsonrpc.notify(`redraw`,this.redrawParams())}getFont(){return{name:this.option.fontName,size:this.option.fontSize}}loadCSS({content:e}){let t=document.createElement(`style`);t.textContent=e,document.head.appendChild(t)}notifyToServer(e,t){this.jsonrpc.notify(`invoke`,{method:e,args:t})}},canvas=document.querySelector(`#editor`);async function main(){await Promise.all([document.fonts.load(`19px file-icons`),document.fonts.load(`19px AllTheIcons`),document.fonts.load(`19px fontawesome`),document.fonts.load(`19px material-design-icons`),document.fonts.load(`19px octicons`)]),await document.fonts.ready;let e=new Editor({canvas,fontName:`Monospace`,fontSize:18,url:`${window.location.protocol===`https:`?`wss`:`ws`}://${window.location.hostname}:${window.location.port}`,onExit:null,onClosed:null});window.addEventListener(`message`,t=>{t.data.type===`invoke-lem`&&e.notifyToServer(t.data.method,t.data.args)}),e.init()}main(); \ No newline at end of file diff --git a/frontends/server/frontend/editor.js b/frontends/server/frontend/editor.js index 3ad4569fe..99f213b91 100644 --- a/frontends/server/frontend/editor.js +++ b/frontends/server/frontend/editor.js @@ -345,7 +345,7 @@ class BaseSurface { // drawing coordinates are relative to the surface's own top-left corner. drawBlock(x, y, width, height, color) { } - drawText(x, y, text, textWidth, attribute, font, height) { } + drawText(x, y, text, textWidth, attribute, font) { } drawImage(x, y, width, height, clipWidth, clipHeight, url) { } clearImages(yStart, yEnd) { } @@ -411,12 +411,11 @@ class CanvasSurface extends BaseSurface { }); } - // the background is filled first, textWidth by blockHeight, then the text drawn over it, so a - // fill with no text is an empty string with a width. only those fills pass a height, to cover a - // row an image made taller than one line of text. text keeps its own cell height. - drawText(x, y, text, textWidth, attribute, font, height) { + // the background is filled first, textWidth by one line of text, then the text drawn over it. a + // rectangle taller than that is a `drawBlock', not a text-less draw through here. + drawText(x, y, text, textWidth, attribute, font) { const option = this.editor.option; - const blockHeight = height || option.fontHeight; + const blockHeight = option.fontHeight; this.drawingQueue.push(function(ctx) { font = font ? `${option.fontSize}px ${font}` : option.font; if (!attribute) { @@ -868,7 +867,7 @@ class View { this.mainSurface.clearImages(y, this.pixelHeight); } - print(x, y, text, textWidth, attribute, font, height) { + print(x, y, text, textWidth, attribute, font) { this.mainSurface.drawText( x, y, @@ -876,15 +875,26 @@ class View { textWidth, attribute, font, - height, ); } + // a fill of its own, for a rectangle that is not one line of text tall. a missing color is the + // editor's default background, as an attribute with no background of its own would be. + drawBlock(x, y, width, height, color) { + this.mainSurface.drawBlock(x, y, width, height, color || this.option.background); + } + + drawBlockOnModeline(x, y, width, height, color) { + if (this.modelineSurface) { + this.modelineSurface.drawBlock(x, y, width, height, color || this.option.background); + } + } + printImage(x, y, pixelWidth, pixelHeight, clipWidth, clipHeight, url) { this.mainSurface.drawImage(x, y, pixelWidth, pixelHeight, clipWidth, clipHeight, url); } - printToModeline(x, y, text, textWidth, attribute, height) { + printToModeline(x, y, text, textWidth, attribute) { if (this.modelineSurface) { this.modelineSurface.drawText( x, @@ -893,7 +903,6 @@ class View { textWidth, attribute, null, - height, ); } } @@ -1245,6 +1254,8 @@ export class Editor { 'put': this.put.bind(this), 'put-image': this.putImage.bind(this), 'modeline-put': this.modelinePut.bind(this), + 'draw-block': this.drawBlock.bind(this), + 'modeline-draw-block': this.modelineDrawBlock.bind(this), 'update-display': this.updateDisplay.bind(this), 'move-cursor': this.moveCursor.bind(this), 'change-view': this.changeView.bind(this), @@ -1461,9 +1472,19 @@ export class Editor { view.clearEob(x, y); } - put({ viewInfo: { id }, x, y, text, textWidth, attribute, font, height }) { + put({ viewInfo: { id }, x, y, text, textWidth, attribute, font }) { + const view = this.findViewById(id); + view.print(x, y, text, textWidth, attribute, font); + } + + drawBlock({ viewInfo: { id }, x, y, width, height, color }) { + const view = this.findViewById(id); + view.drawBlock(x, y, width, height, color); + } + + modelineDrawBlock({ viewInfo: { id }, x, y, width, height, color }) { const view = this.findViewById(id); - view.print(x, y, text, textWidth, attribute, font, height); + view.drawBlockOnModeline(x, y, width, height, color); } putImage({ viewInfo: { id }, x, y, pixelWidth, pixelHeight, clipWidth, clipHeight, url }) { @@ -1471,9 +1492,9 @@ export class Editor { view.printImage(x, y, pixelWidth, pixelHeight, clipWidth, clipHeight, url); } - modelinePut({ viewInfo: { id }, x, y, text, textWidth, attribute, height }) { + modelinePut({ viewInfo: { id }, x, y, text, textWidth, attribute }) { const view = this.findViewById(id); - view.printToModeline(x, y, text, textWidth, attribute, height); + view.printToModeline(x, y, text, textWidth, attribute); } updateDisplay() { diff --git a/frontends/server/main.lisp b/frontends/server/main.lisp index 8ee0c365c..56170190e 100644 --- a/frontends/server/main.lisp +++ b/frontends/server/main.lisp @@ -651,9 +651,8 @@ same hash." (setf attribute (lem:make-attribute :background lem-if:*background-color-of-drawing-window*))) (attribute-to-hash attribute))) -(defun put (jsonrpc view x y string attribute &key font text-width height) - "draw STRING at pixel position X, Y in VIEW, over a TEXT-WIDTH by HEIGHT pixel background. -HEIGHT defaults to one line of text." +(defun put (jsonrpc view x y string attribute &key font text-width) + "draw STRING at pixel position X, Y in VIEW, over a TEXT-WIDTH background one line of text tall." (with-error-handler () (notify* jsonrpc (ecase *put-target* @@ -664,10 +663,25 @@ HEIGHT defaults to one line of text." "y" y "text" string "textWidth" (or text-width (* (lem:string-width string) (jsonrpc-cell-width jsonrpc))) - "height" height "attribute" (ensure-attribute attribute) "font" font)))) +(defun draw-block (jsonrpc view x y width height color) + "fill the WIDTH by HEIGHT rectangle at pixel position X, Y in VIEW with COLOR. +unlike `put', which is one line of text tall, this covers a row an image made taller. a NIL COLOR +leaves the client to use its default background." + (with-error-handler () + (notify* jsonrpc + (ecase *put-target* + (:edit-area "draw-block") + (:modeline "modeline-draw-block")) + (hash "viewInfo" (view-id-hash view) + "x" x + "y" y + "width" width + "height" height + "color" (and color (lem:color-to-hex-string color)))))) + (defmethod draw-object (jsonrpc (object display:text-object) x y view) (let* ((string (display:text-object-string object)) (attribute (display:text-object-attribute object)) @@ -762,22 +776,18 @@ a string already carrying a data:/https: URL is passed through unchanged." (defun draw-row (jsonrpc view row) "draw ROW's background fill, then everything placed on it. -the client paints a put's background before its text, so the fill goes as an empty string sized -TEXT-WIDTH by HEIGHT, the row's full height, which may exceed a single text line's height when a -tall object (e.g. an image) sits on the row." +the fill covers the row's full height, which a tall object (e.g. an image) can push past a single +text line's, so it goes as a `draw-block' rather than a put's background." (let ((width (view-px-width view))) (when (and (display:row-fill-color row) (< (display:row-fill-x row) width)) - (put jsonrpc - view - (display:row-fill-x row) - (display:row-top row) - "" - (lem:make-attribute - :background - (lem:color-to-hex-string (display:row-fill-color row))) - :text-width (- width (display:row-fill-x row)) - :height (display:row-height row)))) + (draw-block jsonrpc + view + (display:row-fill-x row) + (display:row-top row) + (- width (display:row-fill-x row)) + (display:row-height row) + (display:row-fill-color row)))) (loop :for placement :in (display:row-placements row) :do (draw-object jsonrpc (display:placement-object placement) @@ -798,18 +808,15 @@ tall object (e.g. an image) sits on the row." (defmethod lem-if:render-modeline-row ((jsonrpc jsonrpc) view row default-attribute) ;; the modeline has a surface of its own here, so the row is drawn where it was laid out. (let ((*put-target* :modeline)) - (with-error-handler () - (notify* jsonrpc - "modeline-put" - (hash "viewInfo" (view-id-hash view) - "x" 0 - "y" (display:row-top row) - ;; the modeline's own background: no text, just fill - "text" "" - "textWidth" (view-px-width view) - "height" (display:row-height row) - "attribute" (attribute-to-hash default-attribute))) - (draw-row jsonrpc view row)))) + ;; the modeline's own background, under everything the row places on it + (draw-block jsonrpc + view + 0 + (display:row-top row) + (view-px-width view) + (display:row-height row) + (lem:attribute-background-with-reverse default-attribute)) + (draw-row jsonrpc view row))) (defmethod lem-if:clear-to-end-of-window ((jsonrpc jsonrpc) view y) (notify* jsonrpc From 5c33e4040696cd246c985a283c90907e1bf8f6e9 Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Mon, 3 Aug 2026 15:19:53 +0300 Subject: [PATCH 20/26] give object-width's cache a slot of its own drawing-object's width and image-object's were one slot, not two. --- src/display/physical-line.lisp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/display/physical-line.lisp b/src/display/physical-line.lisp index bc02215e2..43beef662 100644 --- a/src/display/physical-line.lisp +++ b/src/display/physical-line.lisp @@ -15,7 +15,8 @@ (setf (window-parameter window 'redrawing-cache) value)) (defclass drawing-object () - ((width :initform nil :accessor drawing-object-width))) + ;; where `object-width' caches its result. `width' is left free for subclasses like `image-object'. + ((occupied-width :initform nil :accessor drawing-object-width))) (defclass void-object (drawing-object) ()) From 71141f9ed6a8e8da1de048f3d03e0cd78574beea Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Mon, 3 Aug 2026 15:19:56 +0300 Subject: [PATCH 21/26] turn a click into a column by walking the objects drawn --- src/display/physical-line.lisp | 105 +++++++++++++++++++++++++-------- src/mouse.lisp | 35 +++++++---- 2 files changed, 104 insertions(+), 36 deletions(-) diff --git a/src/display/physical-line.lisp b/src/display/physical-line.lisp index 43beef662..72483667c 100644 --- a/src/display/physical-line.lisp +++ b/src/display/physical-line.lisp @@ -63,9 +63,14 @@ (defclass image-object (drawing-object) ((image :initarg :image :reader image-object-image) + ;; the size the image is drawn at, in pixels, or NIL for its natural one. (width :initarg :width :reader image-object-width) (height :initarg :height :reader image-object-height) (attribute :initarg :attribute :reader image-object-attribute) + ;; columns of the line the image accounts for, so a click can be turned back into a position. + (columns :initarg :columns + :initform 1 + :reader image-object-columns) ;; how much of the width may be shown, or NIL for all of it. see `crop-image-object'. (visible-width :initarg :visible-width :initform nil @@ -142,6 +147,7 @@ frontend can report one (`lem-if:image-natural-size'), otherwise one cell wide." :width (image-object-width object) :height (image-object-height object) :attribute (image-object-attribute object) + :columns (image-object-columns object) :visible-width (alexandria:if-let ((visible (image-object-visible-width object))) (min width visible) width))) @@ -190,6 +196,9 @@ frontend can report one (`lem-if:image-natural-size'), otherwise one cell wide." (and (eq (image-object-image drawing-object-1) (image-object-image drawing-object-2)) (equal (image-object-width drawing-object-1) (image-object-width drawing-object-2)) (equal (image-object-height drawing-object-1) (image-object-height drawing-object-2)) + ;; the cursor landing on the image changes its attribute and nothing else + (attribute-equal (image-object-attribute drawing-object-1) + (image-object-attribute drawing-object-2)) ;; a differently cropped image draws differently, so the cached row must not be reused (equal (image-object-visible-width drawing-object-1) (image-object-visible-width drawing-object-2)))) @@ -274,6 +283,18 @@ frontend can report one (`lem-if:image-natural-size'), otherwise one cell wide." (defun object-ascent (drawing-object) (lem-if:object-ascent (implementation) drawing-object)) +(defgeneric object-columns (drawing-object) + (:documentation "How many columns of the line DRAWING-OBJECT accounts for. +Not its width in pixels (`object-width'): an image can account for one column and be hundreds of +pixels wide.") + (:method (drawing-object) 0) + (:method ((drawing-object text-object)) + (string-width (text-object-string drawing-object))) + ;; drawn past the end of the line, so it accounts for nothing on it. + (:method ((drawing-object line-end-object)) 0) + (:method ((drawing-object image-object)) + (image-object-columns drawing-object))) + (defun split-string-by-character-type (string) (loop :with pos := 0 :and items := '() :while (< pos (length string)) @@ -365,7 +386,8 @@ frontend can report one (`lem-if:image-natural-size'), otherwise one cell wide." :image (attribute-image attribute) :width (attribute-width attribute) :height (attribute-height attribute) - :attribute attribute))) + :attribute attribute + :columns (string-width string)))) (t (loop :for (type . string) :in (split-string-by-character-type string) :unless (alexandria:emptyp string) @@ -539,12 +561,13 @@ Returns T if the cached entry matches (render can be skipped)." nil))) (defun render-row-with-caching (window y objects) - "Lay OBJECTS out as one screen row of WINDOW at Y and draw it, unless it is already on screen." + "Lay OBJECTS out as one screen row of WINDOW at Y and draw it, unless it is already on screen. +Returns the laid-out row." (let* ((reduced (reduce-objects objects)) (row (layout-row y reduced))) (unless (update-and-validate-cache-p window y (row-height row) reduced) (render-row (window-view window) row)) - (row-height row))) + row)) (defun text-row-metrics () "The ascent and height of a row holding nothing but text, as (values ASCENT HEIGHT). @@ -770,11 +793,18 @@ over the top-level spine and tolerant of improper (dotted) lists." (defstruct screen-row "One drawn row of a window, recorded as it was drawn." - height ;; which buffer line this row's logical line starts on. line-number ;; this row's index within its line. a break in virtual text starts a row without advancing it. - wrap-index) + wrap-index + ;; how much of the row's left edge the left area took + left-width + ;; as `layout-row' laid it out and the frontend drew it, so a pixel position can be read back + ;; against what is on screen rather than derived a second time. + row) + +(defun screen-row-height (screen-row) + (row-height (screen-row-row screen-row))) (defun window-screen-rows (window) "Every screen row of WINDOW, top to bottom, as recorded while it was drawn." @@ -799,6 +829,27 @@ Walks the rows because they are not all one height, so there is nothing to divid there." (nth index (window-screen-rows window))) +(defun screen-row-column-at-x (screen-row x) + "The column of SCREEN-ROW's line that pixel X, measured from the window's left edge, is over. +Walks the objects drawn rather than dividing by a cell width, which would miscount every row holding +something not one cell wide." + (let ((column 0) + (right (screen-row-left-width screen-row))) + (dolist (placement (row-placements (screen-row-row screen-row))) + (let ((object (placement-object placement)) + (left (placement-x placement))) + ;; skip the left area: line numbers and the like, which account for no column + (when (<= (screen-row-left-width screen-row) left) + (let ((width (object-width object))) + (when (and (plusp width) (< x (+ left width))) + (return-from screen-row-column-at-x + (+ column (floor (* (object-columns object) (- x left)) width)))) + (incf column (object-columns object)) + (setf right (max right (+ left width))))))) + ;; no object under x, so it is out past the line where the window is plain cells. callers clamp + ;; this to the line's end. + (+ column (floor (max 0 (- x right)) (lem-if:cell-width (implementation)))))) + (defun check-line-fingerprint (window y fingerprint) "Check if the fingerprint for line at Y matches. Returns the cached list of rows, or NIL. One entry per row, so a line taken from the cache still contributes its rows to @@ -822,11 +873,12 @@ iteration per pixel, against a cache holding one entry per line drawn." (defun update-line-fingerprint (window y fingerprint rows) "Store the fingerprint and ROWS for line at Y, and drop the rows it covers. -ROWS is one (HEIGHT . WRAP-INDEX) per screen row the line drew, as the redraw functions collect -them." +ROWS is one `screen-row' per screen row the line drew, as the redraw functions collect them." (let ((cache (line-fingerprint-cache window))) (setf (gethash y cache) (cons fingerprint rows)) - (evict-line-fingerprint-shadow cache y (reduce #'+ rows :key #'car :initial-value 0)))) + (evict-line-fingerprint-shadow cache + y + (reduce #'+ rows :key #'screen-row-height :initial-value 0)))) (defun left-side-character-count (left-side-objects) (loop :for obj :in left-side-objects @@ -856,11 +908,13 @@ them." ;; an empty row is still a row when more of the line follows, which is what a break at ;; the very start of the virtual text asks for. (unless (or objects rest-line-objects) (return)) - (let* ((all-objects (append left-side-objects objects)) - (height (render-row-with-caching window y all-objects))) - (incf y height) + (let ((row (render-row-with-caching window y (append left-side-objects objects)))) + (incf y (row-height row)) (setq left-side-objects wrapped-left-side-objects) - (push (cons height wrap-index) rows) + (push (make-screen-row :row row + :wrap-index wrap-index + :left-width left-side-width) + rows) ;; only running out of width advances the position, a virtual-text break does not. (when (eq why :wrapped) (incf wrap-index)) @@ -960,7 +1014,7 @@ creating zero temporary letter-objects." (return-from redraw-logical-line-when-horizontal-scroll cached-rows)) (let* ((rows (split-objects-at-line-breaks (create-drawing-objects logical-line))) (left-side-characters (left-side-character-count left-side-objects)) - (row-heights) + (screen-rows) (total-height 0)) ;; the cursor is on one of the rows, scrolling follows it there. (dolist (row-objects rows) @@ -989,15 +1043,16 @@ creating zero temporary letter-objects." (horizontal-scroll-start window) (+ (horizontal-scroll-start window) (window-view-width window)))) - (height (render-row-with-caching window (+ y total-height) - (append side clipped)))) - (incf total-height height) + (row (render-row-with-caching window (+ y total-height) + (append side clipped)))) + (incf total-height (row-height row)) ;; wrapping is off here, so every row begins where the line does, index 0 - (push (cons height 0) row-heights)) + (push (make-screen-row :row row :wrap-index 0 :left-width left-side-width) + screen-rows)) ;; y is in the frontend's units, as is the bound (when (<= (window-view-height window) (+ y total-height)) (return)))) - (setf row-heights (nreverse row-heights)) + (setf screen-rows (nreverse screen-rows)) ;; Reuse fingerprint if scroll position didn't change; avoids redundant sxhash (update-line-fingerprint window y @@ -1006,8 +1061,8 @@ creating zero temporary letter-objects." (compute-line-fingerprint logical-line (horizontal-scroll-start window) left-side-width)) - row-heights) - row-heights))) + screen-rows) + screen-rows))) (defun redraw-lines (window) (let* ((*line-wrap* (variable-value 'line-wrap @@ -1035,12 +1090,10 @@ creating zero temporary letter-objects." (funcall redraw-fn window y logical-line left-side-objects left-side-width)) ;; read once, shared by the line's rows (line-number (line-number-at-point line-point))) - (loop :for (row-height . wrap-index) :in line-rows - :do (push (make-screen-row :height row-height - :line-number line-number - :wrap-index wrap-index) - rows) - (incf y row-height))) + (loop :for row :in line-rows + :do (setf (screen-row-line-number row) line-number) + (push row rows) + (incf y (screen-row-height row)))) (unless (< y height) (return-from outer))))) (setf (window-screen-rows window) (nreverse rows)) diff --git a/src/mouse.lisp b/src/mouse.lisp index 429bb5e0b..84acbfb54 100644 --- a/src/mouse.lisp +++ b/src/mouse.lisp @@ -83,6 +83,19 @@ with or the window is undrawn." fallback-row)) fallback-row)) +(defun mouse-event-screen-column (mouse-event window row-index fallback-column) + "The column that MOUSE-EVENT points at on ROW-INDEX of WINDOW, counted from the top of its view. +FALLBACK-COLUMN is used when there is no pixel position to walk with or no such row was drawn." + (alexandria:if-let ((screen-row (and (mouse-event-pixel-x mouse-event) + (mouse-event-pixel-y mouse-event) + (window-screen-row-at-index window row-index)))) + (multiple-value-bind (relative-x relative-y) + (get-relative-mouse-coordinates-pixels mouse-event window) + (declare (ignore relative-y)) + ;; measured from the window's left edge, where the row was laid out from + (screen-row-column-at-x screen-row relative-x)) + fallback-column)) + (defun move-point-to-screen-row (point window row) "Move POINT to the start of screen ROW of WINDOW, counting rows from the top of its view. Uses the line recorded when the row was drawn, since counting virtual lines down from the view top @@ -216,11 +229,12 @@ that was not drawn, or whose line the buffer no longer has." (mouse-event-y mouse-event)) (when (and window (window-clickable window)) - (handle-mouse-button-down (window-buffer window) - mouse-event - :window window - :x x - :y (mouse-event-screen-row mouse-event window y)))))))) + (let ((row-index (mouse-event-screen-row mouse-event window y))) + (handle-mouse-button-down (window-buffer window) + mouse-event + :window window + :x (mouse-event-screen-column mouse-event window row-index x) + :y row-index)))))))) (defmethod handle-mouse-event ((mouse-event mouse-button-up)) (setf *last-dragged-separator* nil) @@ -282,11 +296,12 @@ that was not drawn, or whose line the buffer no longer has." (mouse-event-x mouse-event) (mouse-event-y mouse-event)) (when window - (handle-mouse-hover (window-buffer window) - mouse-event - :window window - :x x - :y (mouse-event-screen-row mouse-event window y))))) + (let ((row-index (mouse-event-screen-row mouse-event window y))) + (handle-mouse-hover (window-buffer window) + mouse-event + :window window + :x (mouse-event-screen-column mouse-event window row-index x) + :y row-index))))) ((typep *last-dragged-separator* 'window-vertical-separator) (let ((x (mouse-event-x mouse-event)) (button (mouse-event-button mouse-event))) From 003a966509dc97056b6b3a41c6151361579227ce Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Mon, 3 Aug 2026 15:20:02 +0300 Subject: [PATCH 22/26] highlight a row as tall as the image on it put filled a background one text line tall while extend-to-eol filled the whole row, so selecting a line with an image on it drew two heights. send the row's top and height with a put on a tall row, paint the image's own attribute behind it, and report the caret on the row's text line when the cursor sits on the image. --- .../server/frontend/dist/assets/index.js | 2 +- frontends/server/frontend/editor.js | 31 ++++--- frontends/server/main.lisp | 88 +++++++++++++------ 3 files changed, 82 insertions(+), 39 deletions(-) diff --git a/frontends/server/frontend/dist/assets/index.js b/frontends/server/frontend/dist/assets/index.js index 572821549..7cdb66a8b 100644 --- a/frontends/server/frontend/dist/assets/index.js +++ b/frontends/server/frontend/dist/assets/index.js @@ -1 +1 @@ -var __defProp=Object.defineProperty,__commonJSMin=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),__exportAll=(e,t)=>{let n={};for(var r in e)__defProp(n,r,{get:e[r],enumerable:!0});return t||__defProp(n,Symbol.toStringTag,{value:`Module`}),n};(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var require_models=__commonJSMin((e=>{var t=e&&e.__extends||(function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};return function(t,n){if(typeof n!=`function`&&n!==null)throw TypeError(`Class extends value `+String(n)+` is not a constructor or null`);e(t,n);function r(){this.constructor=t}t.prototype=n===null?Object.create(n):(r.prototype=n.prototype,new r)}})();Object.defineProperty(e,"__esModule",{value:!0}),e.createJSONRPCNotification=e.createJSONRPCRequest=e.createJSONRPCSuccessResponse=e.createJSONRPCErrorResponse=e.JSONRPCErrorCode=e.JSONRPCErrorException=e.isJSONRPCResponses=e.isJSONRPCResponse=e.isJSONRPCRequests=e.isJSONRPCRequest=e.isJSONRPCID=e.JSONRPC=void 0,e.JSONRPC=`2.0`,e.isJSONRPCID=function(e){return typeof e==`string`||typeof e==`number`||e===null},e.isJSONRPCRequest=function(t){return t.jsonrpc===e.JSONRPC&&t.method!==void 0&&t.result===void 0&&t.error===void 0},e.isJSONRPCRequests=function(t){return Array.isArray(t)&&t.every(e.isJSONRPCRequest)},e.isJSONRPCResponse=function(t){return t.jsonrpc===e.JSONRPC&&t.id!==void 0&&(t.result!==void 0||t.error!==void 0)},e.isJSONRPCResponses=function(t){return Array.isArray(t)&&t.every(e.isJSONRPCResponse)};var n=function(e,t,n){var r={code:e,message:t};return n!=null&&(r.data=n),r};e.JSONRPCErrorException=function(e){t(r,e);function r(t,n,i){var a=e.call(this,t)||this;return Object.setPrototypeOf(a,r.prototype),a.code=n,a.data=i,a}return r.prototype.toObject=function(){return n(this.code,this.message,this.data)},r}(Error),(function(e){e[e.ParseError=-32700]=`ParseError`,e[e.InvalidRequest=-32600]=`InvalidRequest`,e[e.MethodNotFound=-32601]=`MethodNotFound`,e[e.InvalidParams=-32602]=`InvalidParams`,e[e.InternalError=-32603]=`InternalError`})(e.JSONRPCErrorCode||={}),e.createJSONRPCErrorResponse=function(t,r,i,a){return{jsonrpc:e.JSONRPC,id:t,error:n(r,i,a)}},e.createJSONRPCSuccessResponse=function(t,n){return{jsonrpc:e.JSONRPC,id:t,result:n??null}},e.createJSONRPCRequest=function(t,n,r){return{jsonrpc:e.JSONRPC,id:t,method:n,params:r}},e.createJSONRPCNotification=function(t,n){return{jsonrpc:e.JSONRPC,method:t,params:n}}})),require_internal=__commonJSMin((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.DefaultErrorCode=void 0,e.DefaultErrorCode=0})),require_client=__commonJSMin((e=>{var t=e&&e.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||=Promise)(function(n,a){function o(e){try{c(r.next(e))}catch(e){a(e)}}function s(e){try{c(r.throw(e))}catch(e){a(e)}}function c(e){e.done?n(e.value):i(e.value).then(o,s)}c((r=r.apply(e,t||[])).next())})},n=e&&e.__generator||function(e,t){var n={label:0,sent:function(){if(a[0]&1)throw a[1];return a[1]},trys:[],ops:[]},r,i,a,o;return o={next:s(0),throw:s(1),return:s(2)},typeof Symbol==`function`&&(o[Symbol.iterator]=function(){return this}),o;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw TypeError(`Generator is already executing.`);for(;o&&(o=0,s[0]&&(n=0)),n;)try{if(r=1,i&&(a=s[0]&2?i.return:s[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,s[1])).done)return a;switch(i=0,a&&(s=[s[0]&2,a.value]),s[0]){case 0:case 1:a=s;break;case 4:return n.label++,{value:s[1],done:!1};case 5:n.label++,i=s[1],s=[0];continue;case 7:s=n.ops.pop(),n.trys.pop();continue;default:if((a=n.trys,!(a=a.length>0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{Object.defineProperty(e,"__esModule",{value:!0})})),require_server=__commonJSMin((e=>{var t=e&&e.__assign||function(){return t=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{var t=e&&e.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||=Promise)(function(n,a){function o(e){try{c(r.next(e))}catch(e){a(e)}}function s(e){try{c(r.throw(e))}catch(e){a(e)}}function c(e){e.done?n(e.value):i(e.value).then(o,s)}c((r=r.apply(e,t||[])).next())})},n=e&&e.__generator||function(e,t){var n={label:0,sent:function(){if(a[0]&1)throw a[1];return a[1]},trys:[],ops:[]},r,i,a,o;return o={next:s(0),throw:s(1),return:s(2)},typeof Symbol==`function`&&(o[Symbol.iterator]=function(){return this}),o;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw TypeError(`Generator is already executing.`);for(;o&&(o=0,s[0]&&(n=0)),n;)try{if(r=1,i&&(a=s[0]&2?i.return:s[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,s[1])).done)return a;switch(i=0,a&&(s=[s[0]&2,a.value]),s[0]){case 0:case 1:a=s;break;case 4:return n.label++,{value:s[1],done:!1};case 5:n.label++,i=s[1],s=[0];continue;case 7:s=n.ops.pop(),n.trys.pop();continue;default:if((a=n.trys,!(a=a.length>0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{var t=e&&e.__createBinding||(Object.create?(function(e,t,n,r){r===void 0&&(r=n);var i=Object.getOwnPropertyDescriptor(t,n);(!i||(`get`in i?!t.__esModule:i.writable||i.configurable))&&(i={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,r,i)}):(function(e,t,n,r){r===void 0&&(r=n),e[r]=t[n]})),n=e&&e.__exportStar||function(e,n){for(var r in e)r!=="default"&&!Object.prototype.hasOwnProperty.call(n,r)&&t(n,e,r)};Object.defineProperty(e,"__esModule",{value:!0}),n(require_client(),e),n(require_interfaces(),e),n(require_models(),e),n(require_server(),e),n(require_server_and_client(),e)})),import_dist=require_dist(),JSONRPC=class{constructor(e,{onConnected:t,onClosed:n}){this.url=e,this.onConnected=t,this.onClosed=n,this.messageQueue=[],this.serverAndClient=null,this.connect(),this.connectionEstablished=!1,this.timerId=null,this.closed=!1}close(){this.timerId&&clearTimeout(this.timerId),this.webSocket.close(),this.closed=!0}on(e,t){this.serverAndClient.addMethod(e,t)}async requestInternal(e,t,n){let r=await this.serverAndClient.request(e,t);n&&n(r)}requestMessageQueue(){this.messageQueue.forEach(e=>{let[t,n,r]=e;this.requestInternal(t,n,r)}),this.messageQueue=[]}request(e,t,n){this.webSocket.readyState===WebSocket.OPEN?this.requestInternal(e,t,n):this.messageQueue.push([e,t,n])}notify(e,t){switch(this.webSocket.readyState){case WebSocket.OPEN:this.serverAndClient.notify(e,t);break;case WebSocket.CLOSED:break}}connect(e){this.closed||(console.log(`connect`,this.url),this.webSocket=new WebSocket(this.url),this.serverAndClient||=new import_dist.JSONRPCServerAndClient(new import_dist.JSONRPCServer,new import_dist.JSONRPCClient(e=>{try{return this.webSocket.send(JSON.stringify(e)),Promise.resolve()}catch(e){return Promise.reject(e)}})),this.webSocket.onmessage=e=>{this.serverAndClient.receiveAndSend(JSON.parse(e.data.toString()))},this.webSocket.onopen=()=>{console.log(`WebSocket connection established`),this.connectionEstablished=!0,this.onConnected&&this.onConnected(),this.requestMessageQueue()},this.webSocket.onclose=e=>{console.error(`WebScoket closed`,e),this.serverAndClient.rejectAllPendingRequests(`Connection is closed (${e.reason}).`),this.connectionEstablished&&this.onClosed(),this.timerId=setTimeout(()=>{this.connect()},3e3)},this.webSocket.onerror=e=>{console.error(`WebSocket error:`,e),this.webSocket.close()})}},keyevent_exports=__exportAll({convertKeyEvent:()=>convertKeyEvent}),modifierKeys=[`Shift`,`Control`,`Alt`,`Meta`,`CapsLock`],convertKeyTable={Enter:`Return`,ArrowRight:`Right`,ArrowLeft:`Left`,ArrowUp:`Up`,ArrowDown:`Down`,"¡":`1`,"™":`2`,"£":`3`,"¢":`4`,"∞":`5`,"§":`6`,"¶":`7`,"•":`8`,ª:`9`,º:`0`,"–":`-`,"≠":`=`,"“":`[`,"‘":`]`,"«":`\\`,"…":`;`,æ:`'`,"≤":`,`,"≥":`.`,"÷":`/`,"⁄":`!`,"€":`@`,"‹":`#`,"›":`$`,fi:`%`,fl:`^`,"‡":`&`,"°":`*`,"·":`(`,"‚":`)`,"—":`_`,"±":`+`,"”":`{`,"’":`}`,"»":`|`,Ú:`:`,Æ:`"`,"¯":`<`,"˘":`>`,"¿":`?`,œ:`q`,"∑":`w`,"´":`e`,"®":`r`,"†":`t`,"¥":`y`,"¨":`u`,ˆ:`i`,ø:`o`,π:`p`,å:`a`,ß:`s`,"∂":`d`,ƒ:`f`,"©":`g`,"˙":`h`,"∆":`j`,"˚":`k`,"¬":`l`,Ω:`z`,"≈":`x`,ç:`c`,"√":`v`,"∫":`b`,"˜":`n`,µ:`m`,Œ:`Q`,"„":`W`,"´":`E`,"‰":`R`,ˇ:`T`,Á:`Y`,"¨":`U`,ˆ:`I`,Ø:`O`,"∏":`P`,Å:`A`,Í:`S`,Î:`D`,Ï:`F`,"˝":`G`,Ó:`H`,Ô:`J`,"":`K`,Ò:`L`,"¸":`Z`,"˛":`X`,Ç:`C`,"◊":`V`,ı:`B`,"˜":`N`,Â:`M`};function getKey(e){return e.altKey?convertKeyTable[e.key]||(e.code.startsWith(`Key`)?e.code[3].toLowerCase():null)||e.key:convertKeyTable[e.key]||e.key}function convertKeyEvent(e){return modifierKeys.indexOf(e.key)===-1?{key:getKey(e),ctrl:e.ctrlKey,meta:e.altKey,super:e.metaKey,shift:e.shiftKey}:null}var lib_exports=__exportAll({computeWidth:()=>computeWidth,eawVersion:()=>version,getEAW:()=>getEAW}),defs=[[0,31,`N`],[32,126,`Na`],[127,160,`N`],[161,161,`A`],[162,163,`Na`],[164,164,`A`],[165,166,`Na`],[167,168,`A`],[169,169,`N`],[170,170,`A`],[171,171,`N`],[172,172,`Na`],[173,174,`A`],[175,175,`Na`],[176,180,`A`],[181,181,`N`],[182,186,`A`],[187,187,`N`],[188,191,`A`],[192,197,`N`],[198,198,`A`],[199,207,`N`],[208,208,`A`],[209,214,`N`],[215,216,`A`],[217,221,`N`],[222,225,`A`],[226,229,`N`],[230,230,`A`],[231,231,`N`],[232,234,`A`],[235,235,`N`],[236,237,`A`],[238,239,`N`],[240,240,`A`],[241,241,`N`],[242,243,`A`],[244,246,`N`],[247,250,`A`],[251,251,`N`],[252,252,`A`],[253,253,`N`],[254,254,`A`],[255,256,`N`],[257,257,`A`],[258,272,`N`],[273,273,`A`],[274,274,`N`],[275,275,`A`],[276,282,`N`],[283,283,`A`],[284,293,`N`],[294,295,`A`],[296,298,`N`],[299,299,`A`],[300,304,`N`],[305,307,`A`],[308,311,`N`],[312,312,`A`],[313,318,`N`],[319,322,`A`],[323,323,`N`],[324,324,`A`],[325,327,`N`],[328,331,`A`],[332,332,`N`],[333,333,`A`],[334,337,`N`],[338,339,`A`],[340,357,`N`],[358,359,`A`],[360,362,`N`],[363,363,`A`],[364,461,`N`],[462,462,`A`],[463,463,`N`],[464,464,`A`],[465,465,`N`],[466,466,`A`],[467,467,`N`],[468,468,`A`],[469,469,`N`],[470,470,`A`],[471,471,`N`],[472,472,`A`],[473,473,`N`],[474,474,`A`],[475,475,`N`],[476,476,`A`],[477,592,`N`],[593,593,`A`],[594,608,`N`],[609,609,`A`],[610,707,`N`],[708,708,`A`],[709,710,`N`],[711,711,`A`],[712,712,`N`],[713,715,`A`],[716,716,`N`],[717,717,`A`],[718,719,`N`],[720,720,`A`],[721,727,`N`],[728,731,`A`],[732,732,`N`],[733,733,`A`],[734,734,`N`],[735,735,`A`],[736,767,`N`],[768,879,`A`],[880,912,`N`],[913,929,`A`],[930,930,`N`],[931,937,`A`],[938,944,`N`],[945,961,`A`],[962,962,`N`],[963,969,`A`],[970,1024,`N`],[1025,1025,`A`],[1026,1039,`N`],[1040,1103,`A`],[1104,1104,`N`],[1105,1105,`A`],[1106,4351,`N`],[4352,4447,`W`],[4448,8207,`N`],[8208,8208,`A`],[8209,8210,`N`],[8211,8214,`A`],[8215,8215,`N`],[8216,8217,`A`],[8218,8219,`N`],[8220,8221,`A`],[8222,8223,`N`],[8224,8226,`A`],[8227,8227,`N`],[8228,8231,`A`],[8232,8239,`N`],[8240,8240,`A`],[8241,8241,`N`],[8242,8243,`A`],[8244,8244,`N`],[8245,8245,`A`],[8246,8250,`N`],[8251,8251,`A`],[8252,8253,`N`],[8254,8254,`A`],[8255,8307,`N`],[8308,8308,`A`],[8309,8318,`N`],[8319,8319,`A`],[8320,8320,`N`],[8321,8324,`A`],[8325,8360,`N`],[8361,8361,`H`],[8362,8363,`N`],[8364,8364,`A`],[8365,8450,`N`],[8451,8451,`A`],[8452,8452,`N`],[8453,8453,`A`],[8454,8456,`N`],[8457,8457,`A`],[8458,8466,`N`],[8467,8467,`A`],[8468,8469,`N`],[8470,8470,`A`],[8471,8480,`N`],[8481,8482,`A`],[8483,8485,`N`],[8486,8486,`A`],[8487,8490,`N`],[8491,8491,`A`],[8492,8530,`N`],[8531,8532,`A`],[8533,8538,`N`],[8539,8542,`A`],[8543,8543,`N`],[8544,8555,`A`],[8556,8559,`N`],[8560,8569,`A`],[8570,8584,`N`],[8585,8585,`A`],[8586,8591,`N`],[8592,8601,`A`],[8602,8631,`N`],[8632,8633,`A`],[8634,8657,`N`],[8658,8658,`A`],[8659,8659,`N`],[8660,8660,`A`],[8661,8678,`N`],[8679,8679,`A`],[8680,8703,`N`],[8704,8704,`A`],[8705,8705,`N`],[8706,8707,`A`],[8708,8710,`N`],[8711,8712,`A`],[8713,8714,`N`],[8715,8715,`A`],[8716,8718,`N`],[8719,8719,`A`],[8720,8720,`N`],[8721,8721,`A`],[8722,8724,`N`],[8725,8725,`A`],[8726,8729,`N`],[8730,8730,`A`],[8731,8732,`N`],[8733,8736,`A`],[8737,8738,`N`],[8739,8739,`A`],[8740,8740,`N`],[8741,8741,`A`],[8742,8742,`N`],[8743,8748,`A`],[8749,8749,`N`],[8750,8750,`A`],[8751,8755,`N`],[8756,8759,`A`],[8760,8763,`N`],[8764,8765,`A`],[8766,8775,`N`],[8776,8776,`A`],[8777,8779,`N`],[8780,8780,`A`],[8781,8785,`N`],[8786,8786,`A`],[8787,8799,`N`],[8800,8801,`A`],[8802,8803,`N`],[8804,8807,`A`],[8808,8809,`N`],[8810,8811,`A`],[8812,8813,`N`],[8814,8815,`A`],[8816,8833,`N`],[8834,8835,`A`],[8836,8837,`N`],[8838,8839,`A`],[8840,8852,`N`],[8853,8853,`A`],[8854,8856,`N`],[8857,8857,`A`],[8858,8868,`N`],[8869,8869,`A`],[8870,8894,`N`],[8895,8895,`A`],[8896,8977,`N`],[8978,8978,`A`],[8979,8985,`N`],[8986,8987,`W`],[8988,9e3,`N`],[9001,9002,`W`],[9003,9192,`N`],[9193,9196,`W`],[9197,9199,`N`],[9200,9200,`W`],[9201,9202,`N`],[9203,9203,`W`],[9204,9311,`N`],[9312,9449,`A`],[9450,9450,`N`],[9451,9547,`A`],[9548,9551,`N`],[9552,9587,`A`],[9588,9599,`N`],[9600,9615,`A`],[9616,9617,`N`],[9618,9621,`A`],[9622,9631,`N`],[9632,9633,`A`],[9634,9634,`N`],[9635,9641,`A`],[9642,9649,`N`],[9650,9651,`A`],[9652,9653,`N`],[9654,9655,`A`],[9656,9659,`N`],[9660,9661,`A`],[9662,9663,`N`],[9664,9665,`A`],[9666,9669,`N`],[9670,9672,`A`],[9673,9674,`N`],[9675,9675,`A`],[9676,9677,`N`],[9678,9681,`A`],[9682,9697,`N`],[9698,9701,`A`],[9702,9710,`N`],[9711,9711,`A`],[9712,9724,`N`],[9725,9726,`W`],[9727,9732,`N`],[9733,9734,`A`],[9735,9736,`N`],[9737,9737,`A`],[9738,9741,`N`],[9742,9743,`A`],[9744,9747,`N`],[9748,9749,`W`],[9750,9755,`N`],[9756,9756,`A`],[9757,9757,`N`],[9758,9758,`A`],[9759,9791,`N`],[9792,9792,`A`],[9793,9793,`N`],[9794,9794,`A`],[9795,9799,`N`],[9800,9811,`W`],[9812,9823,`N`],[9824,9825,`A`],[9826,9826,`N`],[9827,9829,`A`],[9830,9830,`N`],[9831,9834,`A`],[9835,9835,`N`],[9836,9837,`A`],[9838,9838,`N`],[9839,9839,`A`],[9840,9854,`N`],[9855,9855,`W`],[9856,9874,`N`],[9875,9875,`W`],[9876,9885,`N`],[9886,9887,`A`],[9888,9888,`N`],[9889,9889,`W`],[9890,9897,`N`],[9898,9899,`W`],[9900,9916,`N`],[9917,9918,`W`],[9919,9919,`A`],[9920,9923,`N`],[9924,9925,`W`],[9926,9933,`A`],[9934,9934,`W`],[9935,9939,`A`],[9940,9940,`W`],[9941,9953,`A`],[9954,9954,`N`],[9955,9955,`A`],[9956,9959,`N`],[9960,9961,`A`],[9962,9962,`W`],[9963,9969,`A`],[9970,9971,`W`],[9972,9972,`A`],[9973,9973,`W`],[9974,9977,`A`],[9978,9978,`W`],[9979,9980,`A`],[9981,9981,`W`],[9982,9983,`A`],[9984,9988,`N`],[9989,9989,`W`],[9990,9993,`N`],[9994,9995,`W`],[9996,10023,`N`],[10024,10024,`W`],[10025,10044,`N`],[10045,10045,`A`],[10046,10059,`N`],[10060,10060,`W`],[10061,10061,`N`],[10062,10062,`W`],[10063,10066,`N`],[10067,10069,`W`],[10070,10070,`N`],[10071,10071,`W`],[10072,10101,`N`],[10102,10111,`A`],[10112,10132,`N`],[10133,10135,`W`],[10136,10159,`N`],[10160,10160,`W`],[10161,10174,`N`],[10175,10175,`W`],[10176,10213,`N`],[10214,10221,`Na`],[10222,10628,`N`],[10629,10630,`Na`],[10631,11034,`N`],[11035,11036,`W`],[11037,11087,`N`],[11088,11088,`W`],[11089,11092,`N`],[11093,11093,`W`],[11094,11097,`A`],[11098,11903,`N`],[11904,11929,`W`],[11930,11930,`N`],[11931,12019,`W`],[12020,12031,`N`],[12032,12245,`W`],[12246,12271,`N`],[12272,12287,`W`],[12288,12288,`F`],[12289,12350,`W`],[12351,12352,`N`],[12353,12438,`W`],[12439,12440,`N`],[12441,12543,`W`],[12544,12548,`N`],[12549,12591,`W`],[12592,12592,`N`],[12593,12686,`W`],[12687,12687,`N`],[12688,12771,`W`],[12772,12782,`N`],[12783,12830,`W`],[12831,12831,`N`],[12832,12871,`W`],[12872,12879,`A`],[12880,19903,`W`],[19904,19967,`N`],[19968,42124,`W`],[42125,42127,`N`],[42128,42182,`W`],[42183,43359,`N`],[43360,43388,`W`],[43389,44031,`N`],[44032,55203,`W`],[55204,57343,`N`],[57344,63743,`A`],[63744,64255,`W`],[64256,65023,`N`],[65024,65039,`A`],[65040,65049,`W`],[65050,65071,`N`],[65072,65106,`W`],[65107,65107,`N`],[65108,65126,`W`],[65127,65127,`N`],[65128,65131,`W`],[65132,65280,`N`],[65281,65376,`F`],[65377,65470,`H`],[65471,65473,`N`],[65474,65479,`H`],[65480,65481,`N`],[65482,65487,`H`],[65488,65489,`N`],[65490,65495,`H`],[65496,65497,`N`],[65498,65500,`H`],[65501,65503,`N`],[65504,65510,`F`],[65511,65511,`N`],[65512,65518,`H`],[65519,65532,`N`],[65533,65533,`A`],[65534,94175,`N`],[94176,94180,`W`],[94181,94191,`N`],[94192,94193,`W`],[94194,94207,`N`],[94208,100343,`W`],[100344,100351,`N`],[100352,101589,`W`],[101590,101631,`N`],[101632,101640,`W`],[101641,110575,`N`],[110576,110579,`W`],[110580,110580,`N`],[110581,110587,`W`],[110588,110588,`N`],[110589,110590,`W`],[110591,110591,`N`],[110592,110882,`W`],[110883,110897,`N`],[110898,110898,`W`],[110899,110927,`N`],[110928,110930,`W`],[110931,110932,`N`],[110933,110933,`W`],[110934,110947,`N`],[110948,110951,`W`],[110952,110959,`N`],[110960,111355,`W`],[111356,126979,`N`],[126980,126980,`W`],[126981,127182,`N`],[127183,127183,`W`],[127184,127231,`N`],[127232,127242,`A`],[127243,127247,`N`],[127248,127277,`A`],[127278,127279,`N`],[127280,127337,`A`],[127338,127343,`N`],[127344,127373,`A`],[127374,127374,`W`],[127375,127376,`A`],[127377,127386,`W`],[127387,127404,`A`],[127405,127487,`N`],[127488,127490,`W`],[127491,127503,`N`],[127504,127547,`W`],[127548,127551,`N`],[127552,127560,`W`],[127561,127567,`N`],[127568,127569,`W`],[127570,127583,`N`],[127584,127589,`W`],[127590,127743,`N`],[127744,127776,`W`],[127777,127788,`N`],[127789,127797,`W`],[127798,127798,`N`],[127799,127868,`W`],[127869,127869,`N`],[127870,127891,`W`],[127892,127903,`N`],[127904,127946,`W`],[127947,127950,`N`],[127951,127955,`W`],[127956,127967,`N`],[127968,127984,`W`],[127985,127987,`N`],[127988,127988,`W`],[127989,127991,`N`],[127992,128062,`W`],[128063,128063,`N`],[128064,128064,`W`],[128065,128065,`N`],[128066,128252,`W`],[128253,128254,`N`],[128255,128317,`W`],[128318,128330,`N`],[128331,128334,`W`],[128335,128335,`N`],[128336,128359,`W`],[128360,128377,`N`],[128378,128378,`W`],[128379,128404,`N`],[128405,128406,`W`],[128407,128419,`N`],[128420,128420,`W`],[128421,128506,`N`],[128507,128591,`W`],[128592,128639,`N`],[128640,128709,`W`],[128710,128715,`N`],[128716,128716,`W`],[128717,128719,`N`],[128720,128722,`W`],[128723,128724,`N`],[128725,128727,`W`],[128728,128731,`N`],[128732,128735,`W`],[128736,128746,`N`],[128747,128748,`W`],[128749,128755,`N`],[128756,128764,`W`],[128765,128991,`N`],[128992,129003,`W`],[129004,129007,`N`],[129008,129008,`W`],[129009,129291,`N`],[129292,129338,`W`],[129339,129339,`N`],[129340,129349,`W`],[129350,129350,`N`],[129351,129535,`W`],[129536,129647,`N`],[129648,129660,`W`],[129661,129663,`N`],[129664,129672,`W`],[129673,129679,`N`],[129680,129725,`W`],[129726,129726,`N`],[129727,129733,`W`],[129734,129741,`N`],[129742,129755,`W`],[129756,129759,`N`],[129760,129768,`W`],[129769,129775,`N`],[129776,129784,`W`],[129785,131071,`N`],[131072,196605,`W`],[196606,196607,`N`],[196608,262141,`W`],[262142,917759,`N`],[917760,917999,`A`],[918e3,983039,`N`],[983040,1048573,`A`],[1048574,1048575,`N`],[1048576,1114109,`A`],[1114110,1114111,`N`]],version=`15.1.0`;function getEAWOfCodePoint(e){let t=0,n=defs.length-1;for(;t!==n;){let r=t+(n-t>>1),[i,a,o]=defs[r];if(ea)t=r+1;else return o}return defs[t][2]}function getEAW(e,t=0){let n=e.codePointAt(t);if(n!==void 0)return getEAWOfCodePoint(n)}var defaultWidths={N:1,Na:1,W:2,F:2,H:1,A:1};function computeWidth(e,t){let n=0;for(let r of e){let e=getEAW(r);n+=t&&t[e]||defaultWidths[e]}return n}var textOffsetY=5,isSafari=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);function isWideChar(e){switch(getEAW(e)){case`F`:case`W`:return!0;default:return!1}}function isMacOS(){return window.navigator.userAgent.indexOf(`Mac OS X`)!==-1}function computeFontSize(e){let t=document.createElement(`canvas`).getContext(`2d`);t.font=e;let n=t.measureText(`W`);return[Math.floor(n.width),Math.round(n.fontBoundingBoxAscent+textOffsetY+(n.emHeightDescent||0)),Math.round(n.fontBoundingBoxAscent+textOffsetY)]}function drawBlock({ctx:e,x:t,y:n,width:r,height:i,style:a}){e.fillStyle=a,e.fillRect(t,n,r,i)}function drawText({ctx:e,x:t,y:n,text:r,font:i,style:a,option:o}){n+=Math.round(textOffsetY),e.fillStyle=a,e.font=i,e.textBaseline=`top`;for(let i of r)isWideChar(i)?(e.fillText(i,t,n,o.fontWidth*2),t+=o.fontWidth*2):(e.fillText(i,t,n,o.fontWidth),t+=o.fontWidth)}function drawHorizontalLine({ctx:e,x:t,y:n,width:r,style:i,lineWidth:a=1}){e.strokeStyle=i,e.lineWidth=a,e.setLineDash=[],e.beginPath(),e.moveTo(t,n),e.lineTo(t+r,n),e.stroke()}var Option=class{constructor({fontName:e,fontSize:t}){this.setFont(e,t),this.foreground=`#cccccc`,this.background=`#2d2d2d`}setFont(e,t){let n=t+`px `+e,[r,i,a]=computeFontSize(n);this.fontName=e,this.fontSize=t,this.fontWidth=r,this.fontHeight=i,this.fontAscent=a,this.font=n}};function getLemEditorElement(){return document.getElementById(`lem-editor`)}function normalizeWheelDelta(e,t,n,r){switch(n){case 0:return{dx:e/r,dy:t/r};case 2:return{dx:e*20,dy:t*20};default:return{dx:e,dy:t}}}function extractWholeLines(e,t){let n=Math.trunc(e),r=Math.trunc(t);return{scrollX:n,scrollY:r,remainderX:e-n,remainderY:t-r}}function cursorPosition(e,t){let[n,r]=t.getDisplayRectangle(),i=e.clientX-n,a=e.clientY-r;return{pixelX:i,pixelY:a,x:Math.floor(i/t.option.fontWidth),y:Math.floor(a/t.option.fontHeight)}}function makeWheelHandler(e){let t={x:0,y:0},n=!1,r={pixelX:0,pixelY:0,x:0,y:0};return i=>{i.preventDefault(),r=cursorPosition(i,e);let{dx:a,dy:o}=normalizeWheelDelta(i.deltaX,i.deltaY,i.deltaMode,e.option.fontHeight);t={x:t.x+a,y:t.y+o},n||(n=!0,requestAnimationFrame(()=>{n=!1;let{scrollX:i,scrollY:a,remainderX:o,remainderY:s}=extractWholeLines(t.x,t.y);t={x:o,y:s},(i!==0||a!==0)&&e.jsonrpc.notify(`input`,{kind:`wheel`,value:{...r,wheelX:-i,wheelY:-a}})}))}}function addMouseEventListeners({dom:e,editor:t,isDraggable:n,draggableStyle:r}){e.addEventListener(`contextmenu`,e=>{e.preventDefault()});let i=(e,n)=>{e.preventDefault();let[r,i]=t.getDisplayRectangle(),a=e.clientX-r,o=e.clientY-i,s=Math.floor(a/t.option.fontWidth),c=Math.floor(o/t.option.fontHeight);t.jsonrpc.notify(`input`,{kind:n,value:{x:s,y:c,pixelX:a,pixelY:o,button:e.button,clicks:e.detail}})};e.addEventListener(`mousedown`,e=>{n&&(document.body.style.cursor=r),t.focusHiddenInput(),i(e,`mousedown`)}),e.addEventListener(`mouseup`,e=>{n&&(document.body.style.cursor=`default`),i(e,`mouseup`)});let a=0;e.addEventListener(`mousemove`,e=>{e.preventDefault();let n=Date.now();if(n-a>50){a=n;let[r,i]=t.getDisplayRectangle(),o=e.clientX-r,s=e.clientY-i,c=Math.floor(o/t.option.fontWidth),l=Math.floor(s/t.option.fontHeight);t.jsonrpc.notify(`input`,{kind:`mousemove`,value:{x:c,y:l,pixelX:o,pixelY:s,button:e.buttons===0?null:e.buttons-1}})}}),n&&(e.addEventListener(`mouseover`,()=>{document.body.style.cursor=r}),e.addEventListener(`mouseout`,e=>{e.buttons!==1&&(document.body.style.cursor=`default`)})),e.addEventListener(`wheel`,makeWheelHandler(t))}var zIndexTable={"floating-window":200,modeline:100,"vertical-border":100,"horizontal-border":101};function zindex(e){return zIndexTable[e]||0}var borderOffsetX=5,borderOffsetY=10,BaseSurface=class{constructor({editor:e}){this.editor=e,this.mainDOM=null,this.wrapper=null}delete(){this.wrapper?getLemEditorElement().removeChild(this.wrapper):getLemEditorElement().removeChild(this.mainDOM)}setupDOM({dom:e,isFloating:t,border:n,cssClassName:r}){this.mainDOM=e,t&&n?(this.wrapper=document.createElement(`div`),r&&(this.wrapper.className=r),this.wrapper.style.position=`absolute`,this.wrapper.style.backgroundColor=this.editor.option.background,this.wrapper.style.zIndex=zindex(`floating-window`),this.wrapper.appendChild(e),getLemEditorElement().appendChild(this.wrapper)):(r&&(e.className=r),getLemEditorElement().appendChild(e))}move(e,t){let[n,r]=this.editor.getDisplayRectangle(),i=Math.floor(n+e),a=Math.floor(r+t);this.wrapper?(this.wrapper.style.left=i-borderOffsetX+`px`,this.wrapper.style.top=a-borderOffsetY+`px`,this.mainDOM.style.left=borderOffsetX+`px`,this.mainDOM.style.top=borderOffsetY+`px`):(this.mainDOM.style.left=i+`px`,this.mainDOM.style.top=a+`px`)}_resize(e,t){let n=window.devicePixelRatio||1;this.mainDOM.width=e*n,this.mainDOM.height=t*n,this.mainDOM.style.width=e+`px`,this.mainDOM.style.height=t+`px`,this.wrapper&&(this.wrapper.style.width=e+borderOffsetX*2+`px`,this.wrapper.style.height=t+borderOffsetY*2+`px`)}drawBlock(e,t,n,r,i){}drawText(e,t,n,r,i,a){}drawImage(e,t,n,r,i,a,o){}clearImages(e,t){}clearAllImages(){}touch(){}evalIn(code){return eval(code)}},CanvasSurface=class extends BaseSurface{constructor({editor:e,view:t,pixelX:n,pixelY:r,pixelWidth:i,pixelHeight:a,styles:o,isFloating:s,border:c,cssClassName:l}){super({editor:e});let u=this.setupCanvas(o);this.setupDOM({dom:u,isFloating:s,border:c,cssClassName:l}),this.move(n,r),this.resize(i,a),this.drawingQueue=[],addMouseEventListeners({dom:u,editor:e})}setupCanvas(e){let t=document.createElement(`canvas`);if(t.style.position=`absolute`,e)for(let n in e)t.style[n]=e[n];return t}resize(e,t){this._resize(e,t);let n=window.devicePixelRatio||1;this.mainDOM.getContext(`2d`).scale(n,n)}move(e,t){if(super.move(e,t),this.imageEls)for(let[,e]of this.imageEls)this.positionImage(e)}delete(){this.clearAllImages(),super.delete()}drawBlock(e,t,n,r,i){this.drawingQueue.push(function(a){drawBlock({ctx:a,x:e,y:t,width:n,height:r,style:i})})}drawText(e,t,n,r,i,a){let o=this.editor.option,s=o.fontHeight;this.drawingQueue.push(function(c){if(a=a?`${o.fontSize}px ${a}`:o.font,!i)drawBlock({ctx:c,x:e,y:t,width:r,height:s,style:o.background}),drawText({ctx:c,x:e,y:t,text:n,style:o.foreground,font:a,option:o});else{let{foreground:l,background:u,bold:d,reverse:f,underline:p,cursor:m}=i;if(l||=o.foreground,u||=o.background,f){let e=u;u=l,l=e}m&&(u=o.background),drawBlock({ctx:c,x:e,y:t,width:r,height:s,style:u}),drawText({ctx:c,x:e,y:t,text:n,style:l,font:d?`bold `+a:a,option:o}),p&&drawHorizontalLine({ctx:c,x:e,y:t+o.fontHeight-2,width:r,style:typeof p==`string`?p:l,lineWidth:2})}})}imageBaseLeft(){return parseFloat(this.mainDOM.style.left)||0}imageBaseTop(){return parseFloat(this.mainDOM.style.top)||0}drawImage(e,t,n,r,i,a,o){this.imageEls||=new Map;let s=e+`,`+t,c=this.imageEls.get(s);if(c&&c.url!==o&&(c.el.remove(),this.imageEls.delete(s),c=null),!c){let e=document.createElement(`img`);e.style.position=`absolute`,e.style.pointerEvents=`none`,e.style.zIndex=`1`,e.src=o,this.mainDOM.parentNode.appendChild(e),c={el:e,url:o},this.imageEls.set(s,c)}c.x=e,c.y=t,c.width=n,c.height=r,c.clipWidth=i,c.clipHeight=a,this.positionImage(c)}positionImage(e){e.el.style.left=this.imageBaseLeft()+e.x+`px`,e.el.style.top=this.imageBaseTop()+e.y+`px`,e.el.style.width=e.width+`px`,e.el.style.height=e.height+`px`;let t=e.clipWidth==null?0:Math.max(0,e.width-e.clipWidth),n=e.clipHeight==null?0:Math.max(0,e.height-e.clipHeight);e.el.style.clipPath=t>0||n>0?`inset(0px ${t}px ${n}px 0px)`:``}clearImages(e,t){if(this.imageEls)for(let[n,r]of this.imageEls){let i=r.y+(r.height||0);r.ye&&(r.el.remove(),this.imageEls.delete(n))}}clearAllImages(){if(this.imageEls){for(let[,e]of this.imageEls)e.el.remove();this.imageEls.clear()}}touch(){let e=this.mainDOM.getContext(`2d`);for(let t of this.drawingQueue)t(e);this.drawingQueue=[]}activate(){this.mainDOM.dataset.store=`active`}deactivate(){this.mainDOM.dataset.store=`inactive`}},HTMLSurface=class extends BaseSurface{constructor({editor:e,pixelX:t,pixelY:n,pixelWidth:r,pixelHeight:i,styles:a,option:o,isFloating:s,border:c,html:l}){super({editor:e});let u=document.createElement(`iframe`);this.setupDOM({dom:u,isFloating:s,border:c}),u.style.position=`absolute`,u.style.backgroundColor=o.background,u.setAttribute(`sandbox`,`allow-scripts allow-same-origin`),u.srcdoc=l,u.addEventListener(`load`,()=>{let e=u.contentWindow;e.invokeLem=(e,t)=>parent.postMessage({type:`invoke-lem`,method:e,args:t})}),this.iframe=u,this.move(t,n),this.resize(r,i)}resize(e,t){this._resize(e,t)}update(e){let t=this.iframe.contentWindow.scrollY;this.iframe.srcdoc=e,this.iframe.onload=()=>{this.iframe.onload=null,this.iframe.contentWindow.scrollTo(0,t)}}evalIn(e){return this.iframe.contentWindow.eval(e)}},VerticalBorder=class{constructor({x:e,y:t,height:n,option:r,editor:i}){this.option=r,this.editor=i,this.line=document.createElement(`div`),this.line.className=`lem-editor__vertical-border`,this.line.style.height=n+`px`,this.line.style.position=`absolute`,this.line.style.zIndex=zindex(`vertical-border`),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:i,isDraggable:!0,draggableStyle:`col-resize`})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.line.style.left=Math.floor(n+e-this.option.fontWidth/2)+`px`,this.line.style.top=r+t+`px`}resize(e){this.line.style.height=e+`px`}},HorizontalBorder=class{constructor({x:e,y:t,width:n,option:r,editor:i}){this.option=r,this.editor=i,this.line=document.createElement(`div`),this.line.className=`lem-editor__horizontal-border`,this.line.style.width=n+`px`,this.line.style.position=`absolute`,this.line.style.zIndex=zindex(`horizontal-border`),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:i,isDraggable:!0,draggableStyle:`row-resize`})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.line.style.left=n+e+`px`,this.line.style.top=Math.floor(r+t-4)+`px`}resize(e){this.line.style.width=e+`px`}},viewStyles={header:()=>{},tile:()=>{},floating:e=>({boxSizing:`border-box`,borderColor:e.foreground,backgroundColor:e.background})};function getViewStyle(e,t){return viewStyles[e](t)||{}}var View=class{constructor({id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,useModeline:l,kind:u,type:d,content:f,border:p,borderShape:m,option:h,editor:g}){switch(this.option=h,this.id=e,this.x=t,this.y=n,this.width=r,this.height=i,this.pixelX=a,this.pixelY=o,this.pixelWidth=s,this.pixelHeight=c,this.useModeline=l,this.kind=u,this.type=d,this.border=p,this.borderShape=m,this.editor=g,this.bottomBar=null,this.leftsideBar=null,u){case`tile`:this.mainSurface=this.makeSurface(d,f),this.leftSideBar=new VerticalBorder({x:a,y:o,height:c+(l?h.fontHeight:0),option:h,editor:g}),l||(this.bottomBar=new HorizontalBorder({x:a,y:o+c-h.fontHeight,width:s,option:h,editor:g}));break;case`header`:this.mainSurface=this.makeSurface(d,f);break;case`floating`:this.mainSurface=this.makeSurface(d,f),m===`left-border`&&(this.leftSideBar=new VerticalBorder({x:a,y:o,height:c,option:h,editor:g}));break}this.modelineSurface=l?this.makeModelineSurface():null}delete(){this.mainSurface.delete(),this.modelineSurface&&this.modelineSurface.delete(),this.leftSideBar&&this.leftSideBar.delete(),this.bottomBar&&this.bottomBar.delete()}move(e,t,n,r){this.x=e,this.y=t,this.pixelX=n,this.pixelY=r,this.mainSurface.move(n,r),this.modelineSurface&&this.modelineSurface.move(n,r+this.pixelHeight),this.leftSideBar&&this.leftSideBar.move(n,r),this.bottomBar&&this.bottomBar.move(n,r+this.pixelHeight)}resize(e,t,n,r){this.width=e,this.height=t,this.pixelWidth=n,this.pixelHeight=r,this.mainSurface.resize(n,r),this.modelineSurface&&(this.modelineSurface.move(this.pixelX,this.pixelY+r),this.modelineSurface.resize(n,this.option.fontHeight)),this.leftSideBar&&this.leftSideBar.resize(r+(this.modelineSurface?this.option.fontHeight:0)),this.bottomBar&&this.bottomBar.resize(n)}clear(){this.mainSurface.drawBlock(0,0,this.pixelWidth,this.pixelHeight,this.option.background),this.mainSurface.clearImages(0,this.pixelHeight)}clearEol(e,t,n){n??=this.option.fontHeight,this.mainSurface.drawBlock(e,t,this.pixelWidth-e,n,this.option.background),this.mainSurface.clearImages(t,t+n)}clearEob(e,t){this.mainSurface.drawBlock(e,t,this.pixelWidth,this.pixelHeight-t,this.option.background),this.mainSurface.clearImages(t,this.pixelHeight)}print(e,t,n,r,i,a){this.mainSurface.drawText(e,t,n,r,i,a)}drawBlock(e,t,n,r,i){this.mainSurface.drawBlock(e,t,n,r,i||this.option.background)}drawBlockOnModeline(e,t,n,r,i){this.modelineSurface&&this.modelineSurface.drawBlock(e,t,n,r,i||this.option.background)}printImage(e,t,n,r,i,a,o){this.mainSurface.drawImage(e,t,n,r,i,a,o)}printToModeline(e,t,n,r,i){this.modelineSurface&&this.modelineSurface.drawText(e,t,n,r,i,null)}touch(e){this.mainSurface.touch(),this.modelineSurface&&(this.modelineSurface.touch(),e?this.modelineSurface.activate():this.modelineSurface.deactivate())}makeSurface(e,t){switch(e){case`html`:return this.makeHTMLSurface(t);case`editor`:return this.makeEditorSurface();default:console.error(`unknown type: ${e}`)}}makeHTMLSurface(e){return new HTMLSurface({editor:this.editor,pixelX:this.pixelX,pixelY:this.pixelY,pixelWidth:this.pixelWidth,pixelHeight:this.pixelHeight,styles:getViewStyle(this.kind,this.option),option:this.option,isFloating:this.kind===`floating`,border:this.border,html:e})}makeEditorSurface(){let e=this.borderShape===`left-border`?0:this.border,t=this.kind===`floating`;return new CanvasSurface({option:this.editor.option,pixelX:this.pixelX,pixelY:this.pixelY,pixelWidth:this.pixelWidth,pixelHeight:this.pixelHeight,styles:getViewStyle(this.kind,this.option),editor:this.editor,border:e,isFloating:t,view:this,cssClassName:t&&e?`lem-editor__floating-window--bordered`:null})}makeModelineSurface(){let e=new CanvasSurface({option:this.editor.option,pixelX:this.pixelX,pixelY:this.pixelY+this.pixelHeight,pixelWidth:this.pixelWidth,pixelHeight:this.option.fontHeight,editor:this.editor,view:this,styles:{zIndex:zindex(`modeline`)},cssClassName:`lem-editor__mode-line`});return addMouseEventListeners({dom:e.mainDOM,editor:this.editor,isDraggable:!0,draggableStyle:`row-resize`}),e}changeToHTMLContent(e){this.mainSurface.constructor.name===`HTMLSurface`?this.mainSurface.update(e):(this.mainSurface.delete(),this.mainSurface=this.makeHTMLSurface(e))}changeToEditorContent(){this.mainSurface.delete(),this.mainSurface=this.makeEditorSurface()}evalIn(e){return this.mainSurface.evalIn(e)}};function isPasteKeyEvent(e){return isMacOS()?e.metaKey&&e.key===`v`:e.ctrlKey&&e.shiftKey&&e.key===`V`}var Input=class{constructor(e){let t=e.option;this.editor=e,this.composition=!1,this.ignoreKeydownAfterCompositionend=!1,this.span=document.createElement(`span`),this.span.style.color=t.foreground,this.span.style.backgroundColor=t.background,this.span.style.position=`absolute`,this.span.style.zIndex=1e6,this.span.style.top=`0`,this.span.style.left=`0`,this.span.style.font=t.font,this.input=document.createElement(`input`),this.input.style.backgroundColor=`transparent`,this.input.style.color=`transparent`,this.input.style.width=`0`,this.input.style.padding=`0`,this.input.style.margin=`0`,this.input.style.border=`none`,this.input.style.position=`absolute`,this.input.style.zIndex=`-10`,this.input.style.top=`0`,this.input.style.left=`0`,this.input.style.font=t.font,this.input.addEventListener(`blur`,e=>{this.input.focus()}),this.input.addEventListener(`input`,e=>{this.composition===!1&&(this.input.value=``,this.span.innerHTML=``,this.input.style.width=`0`,isMacOS()||this.editor.emitInputString(e.data))}),this.input.addEventListener(`paste`,async e=>{e.preventDefault();let t=e.clipboardData||window.Clipboard.data,n=t?.getData(`text`)??t?.getData(`text/plain`);if(n&&n.length>0){this.editor.emitInputString(n);return}try{if(navigator.clipboard?.readText){let e=await navigator.clipboard.readText();if(e&&e.length>0){this.editor.emitInputString(e);return}}}catch(e){console.warn(`clipboard.readText() failed:`,e)}alert(`Paste failed (permission/environment restriction`)}),this.input.addEventListener(`keydown`,e=>{if(!isPasteKeyEvent(e)&&!(e.isComposing||this.composition)&&e.key!==`Process`){if(this.ignoreKeydownAfterCompositionend&&(isSafari||isMacOS())){e.preventDefault(),this.ignoreKeydownAfterCompositionend=!1;return}if(!(!isMacOS()&&!e.ctrlKey&&!e.altKey&&e.key.length===1)&&(e.preventDefault(),e.isComposing!==!0&&e.code!==``))return setTimeout(()=>{this.composition||(this.editor.emitInput(e),this.input.value=``)},0),!1}}),this.input.addEventListener(`compositionstart`,e=>{this.composition=!0,this.span.innerHTML=this.input.value,this.input.style.width=this.span.offsetWidth+`px`}),this.input.addEventListener(`compositionupdate`,e=>{this.span.innerHTML=e.data,this.input.style.width=this.span.offsetWidth+`px`}),this.input.addEventListener(`compositionend`,e=>{this.composition=!1,this.editor.emitInputString(this.input.value),this.input.value=``,this.span.innerHTML=this.input.value,this.input.style.width=`0`,this.ignoreKeydownAfterCompositionend=!0}),document.body.appendChild(this.input),document.body.appendChild(this.span),this.input.focus()}finalize(){document.body.removeChild(this.input),document.body.removeChild(this.span)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.span.style.top=r+t+`px`,this.span.style.left=n+e+`px`,this.input.style.top=this.span.offsetTop+`px`,this.input.style.left=this.span.offsetLeft+`px`}updateForeground(e){this.span.style.color=e}updateBackground(e){this.span.style.backgroundColor=e}},MessageTable=class{constructor(){this.map=new Map}register(e,t){for(let n in t){let r=t[n];this.map.set(n,r),e.on(n,r)}}get(e){return this.map.get(e)}};function getDisplayRectangleDefault(){return[0,0,window.innerWidth,window.innerHeight]}var Editor=class{constructor({getDisplayRectangle:e=getDisplayRectangleDefault,fontName:t,fontSize:n,url:r,onExit:i,onClosed:a}){this.getDisplayRectangle=e,this.option=new Option({fontName:t,fontSize:n}),this.onExit=i,this.input=new Input(this),this.cursors=new Map,this.cursorOverlay=document.createElement(`div`),this.cursorOverlay.className=`lem-cursor`,this.cursorOverlay.style.width=this.option.fontWidth+`px`,this.cursorOverlay.style.height=this.option.fontHeight+`px`,this.cursorOverlay.style.backgroundColor=`#ffffff`,this.cursorType=`box`,this.viewMap=new Map,this.jsonrpc=new JSONRPC(r,{onClosed:()=>{a()}}),this.messageTable=new MessageTable,this.messageTable.register(this.jsonrpc,{"update-foreground":this.updateForeground.bind(this),"update-background":this.updateBackground.bind(this),"make-view":this.makeView.bind(this),"delete-view":this.deleteView.bind(this),"resize-view":this.resize.bind(this),"move-view":this.move.bind(this),"redraw-view-after":this.redrawViewAfter.bind(this),clear:this.clear.bind(this),"clear-eol":this.clearEol.bind(this),"clear-eob":this.clearEob.bind(this),put:this.put.bind(this),"put-image":this.putImage.bind(this),"modeline-put":this.modelinePut.bind(this),"draw-block":this.drawBlock.bind(this),"modeline-draw-block":this.modelineDrawBlock.bind(this),"update-display":this.updateDisplay.bind(this),"move-cursor":this.moveCursor.bind(this),"change-view":this.changeView.bind(this),"resize-display":this.resizeDisplay.bind(this),bulk:this.bulk.bind(this),exit:this.exitEditor.bind(this),"get-clipboard-text":this.getClipboardText.bind(this),"set-clipboard-text":this.setClipboardText.bind(this),"js-eval":this.jsEval.bind(this),"set-font":this.setFont.bind(this),"get-font":this.getFont.bind(this),"get-display-size":this.getDisplaySize.bind(this),"load-css":this.loadCSS.bind(this),"update-cursor-shape":this.updateCursorShape.bind(this)}),this.login(),this.boundedHandleResize=this.handleResize.bind(this),this.focusHiddenInput=this.focusHiddenInput.bind(this)}init(){window.addEventListener(`resize`,this.boundedHandleResize),document.getElementsByTagName(`html`)[0].style[`background-color`]=`#333`,getLemEditorElement().appendChild(this.cursorOverlay)}finalize(){window.removeEventListener(`resize`,this.boundedHandleResize),this.input.finalize(),this.cursorOverlay.parentNode&&this.cursorOverlay.parentNode.removeChild(this.cursorOverlay)}closeConnection(){this.jsonrpc.close()}emitInput(e){let t=convertKeyEvent(e);if(t){if(t.key===`]`&&t.ctrl&&!t.meta&&!t.super&&!t.shift){this.jsonrpc.notify(`input`,{kind:`abort`});return}t.key!==`Unidentified`&&this.jsonrpc.notify(`input`,{kind:`key`,value:t})}}emitInputString(e){e?this.jsonrpc.notify(`input`,{kind:`input-string`,value:e}):console.error(`unexpected argument`,e)}redrawParams(){return{size:this.getDisplaySize(),fontWidth:this.option.fontWidth,fontHeight:this.option.fontHeight,fontAscent:this.option.fontAscent}}handleResize(e){this.jsonrpc.notify(`redraw`,this.redrawParams())}focusHiddenInput(){let e=this.input?.input;if(e){try{window.focus()}catch{}requestAnimationFrame(()=>{setTimeout(()=>{e.focus({preventScroll:!0})},0)})}}sendNotification(e,t){this.jsonrpc.notify(e,t)}request(e,t,n){this.jsonrpc.request(e,t,n)}getDisplaySize(){let[e,t,n,r]=this.getDisplayRectangle();return{width:Math.floor(n/this.option.fontWidth),height:Math.floor(r/this.option.fontHeight)}}callMessage(e,t){this.messageTable.get(e)(t)}findViewById(e){return this.viewMap.get(e)}login(){this.jsonrpc.request(`login`,{size:this.getDisplaySize(),foreground:this.option.foreground,background:this.option.background,fontWidth:this.option.fontWidth,fontHeight:this.option.fontHeight,fontAscent:this.option.fontAscent},e=>{if(this.updateForeground(e.foreground),this.updateBackground(e.background),e.views)for(let t of e.views)this.makeView(t);this.jsonrpc.notify(`redraw`,this.redrawParams())})}updateForeground(e){e!=null&&(this.option.foreground=e,this.input.updateForeground(e))}updateBackground(e){if(e==null)return;this.option.background=e,this.input.updateBackground(e);let t=getLemEditorElement();t.style.backgroundColor=e}makeView({id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,use_modeline:l,kind:u,type:d,content:f,border:p,border_shape:m}){let h=new View({option:this.option,id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,useModeline:l,kind:u,type:d,content:f,border:p,borderShape:m,editor:this});this.viewMap.set(e,h)}deleteView({viewInfo:{id:e}}){this.findViewById(e).delete(),this.viewMap.delete(e)}resize({viewInfo:{id:e},width:t,height:n,pixelWidth:r,pixelHeight:i}){let a=this.findViewById(e);a?a.resize(t,n,r,i):console.warn(`resize: view not found for id ${e}`)}move({viewInfo:{id:e},x:t,y:n,pixelX:r,pixelY:i}){let a=this.findViewById(e);a?a.move(t,n,r,i):console.warn(`move: view not found for id ${e}`)}redrawViewAfter({viewInfo:{id:e},isActive:t}){this.findViewById(e).touch(t)}clear({viewInfo:{id:e}}){this.findViewById(e).clear()}clearEol({viewInfo:{id:e},x:t,y:n,height:r}){this.findViewById(e).clearEol(t,n,r)}clearEob({viewInfo:{id:e},x:t,y:n}){this.findViewById(e).clearEob(t,n)}put({viewInfo:{id:e},x:t,y:n,text:r,textWidth:i,attribute:a,font:o}){this.findViewById(e).print(t,n,r,i,a,o)}drawBlock({viewInfo:{id:e},x:t,y:n,width:r,height:i,color:a}){this.findViewById(e).drawBlock(t,n,r,i,a)}modelineDrawBlock({viewInfo:{id:e},x:t,y:n,width:r,height:i,color:a}){this.findViewById(e).drawBlockOnModeline(t,n,r,i,a)}putImage({viewInfo:{id:e},x:t,y:n,pixelWidth:r,pixelHeight:i,clipWidth:a,clipHeight:o,url:s}){this.findViewById(e).printImage(t,n,r,i,a,o,s)}modelinePut({viewInfo:{id:e},x:t,y:n,text:r,textWidth:i,attribute:a}){this.findViewById(e).printToModeline(t,n,r,i,a)}updateDisplay(){}moveCursor({viewInfo:{id:e},x:t,y:n,color:r,cursorText:i,cursorForeground:a}){let o=this.findViewById(e),[s,c]=this.getDisplayRectangle(),l=o.pixelX+t,u=o.pixelY+n;this.input.move(l,u);let d=r||this.option.foreground,f=a||this.option.background,p=this.cursorOverlay;switch(this.cursorType){case`bar`:p.style.left=s+l+`px`,p.style.top=c+u+`px`,p.style.width=`2px`,p.style.height=this.option.fontHeight+`px`,p.style.backgroundColor=d,p.textContent=``,p.style.color=``,p.style.font=``,p.style.paddingTop=``;break;case`underline`:p.style.left=s+l+`px`,p.style.top=c+u+this.option.fontHeight-2+`px`,p.style.width=this.option.fontWidth+`px`,p.style.height=`2px`,p.style.backgroundColor=d,p.textContent=``,p.style.color=``,p.style.font=``,p.style.paddingTop=``;break;default:p.style.left=s+l+`px`,p.style.top=c+u+`px`,p.style.width=this.option.fontWidth+`px`,p.style.height=this.option.fontHeight+`px`,p.style.backgroundColor=d,p.style.font=this.option.font,p.style.paddingTop=textOffsetY+`px`,p.textContent=i||``,p.style.color=f;break}p.style.animation=`none`,p.offsetHeight,p.style.animation=``}updateCursorShape({cursorType:e}){this.cursorType=e||`box`}changeView({viewInfo:{id:e},type:t,content:n}){let r=this.findViewById(e);switch(t){case`html`:r.changeToHTMLContent(n);break;case`editor`:r.changeToEditorContent();break}}resizeDisplay({width:e,height:t}){let n=getLemEditorElement();n.style.width=Math.floor(e*this.option.fontWidth)+`px`,n.style.height=Math.floor(t*this.option.fontHeight)+`px`}bulk(e){for(let{method:t,argument:n}of e)this.callMessage(t,n)}exitEditor(){this.onExit&&this.onExit()}getClipboardText(){navigator.clipboard?.readText().then(e=>{this.jsonrpc.notify(`got-clipboard-text`,{text:e})})}setClipboardText({text:e}){navigator.clipboard&&navigator.clipboard.writeText(e)}jsEval({viewInfo:{id:e},code:t}){let n=this.findViewById(e).evalIn(t);return n&&n.toString()}setFont({fontName:e,fontSize:t}){this.option.setFont(e||this.option.fontName,t||this.option.fontSize),this.cursorOverlay.style.width=this.option.fontWidth+`px`,this.cursorOverlay.style.height=this.option.fontHeight+`px`,this.jsonrpc.notify(`redraw`,this.redrawParams())}getFont(){return{name:this.option.fontName,size:this.option.fontSize}}loadCSS({content:e}){let t=document.createElement(`style`);t.textContent=e,document.head.appendChild(t)}notifyToServer(e,t){this.jsonrpc.notify(`invoke`,{method:e,args:t})}},canvas=document.querySelector(`#editor`);async function main(){await Promise.all([document.fonts.load(`19px file-icons`),document.fonts.load(`19px AllTheIcons`),document.fonts.load(`19px fontawesome`),document.fonts.load(`19px material-design-icons`),document.fonts.load(`19px octicons`)]),await document.fonts.ready;let e=new Editor({canvas,fontName:`Monospace`,fontSize:18,url:`${window.location.protocol===`https:`?`wss`:`ws`}://${window.location.hostname}:${window.location.port}`,onExit:null,onClosed:null});window.addEventListener(`message`,t=>{t.data.type===`invoke-lem`&&e.notifyToServer(t.data.method,t.data.args)}),e.init()}main(); \ No newline at end of file +var __defProp=Object.defineProperty,__commonJSMin=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),__exportAll=(e,t)=>{let n={};for(var r in e)__defProp(n,r,{get:e[r],enumerable:!0});return t||__defProp(n,Symbol.toStringTag,{value:`Module`}),n};(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var require_models=__commonJSMin((e=>{var t=e&&e.__extends||(function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};return function(t,n){if(typeof n!=`function`&&n!==null)throw TypeError(`Class extends value `+String(n)+` is not a constructor or null`);e(t,n);function r(){this.constructor=t}t.prototype=n===null?Object.create(n):(r.prototype=n.prototype,new r)}})();Object.defineProperty(e,"__esModule",{value:!0}),e.createJSONRPCNotification=e.createJSONRPCRequest=e.createJSONRPCSuccessResponse=e.createJSONRPCErrorResponse=e.JSONRPCErrorCode=e.JSONRPCErrorException=e.isJSONRPCResponses=e.isJSONRPCResponse=e.isJSONRPCRequests=e.isJSONRPCRequest=e.isJSONRPCID=e.JSONRPC=void 0,e.JSONRPC=`2.0`,e.isJSONRPCID=function(e){return typeof e==`string`||typeof e==`number`||e===null},e.isJSONRPCRequest=function(t){return t.jsonrpc===e.JSONRPC&&t.method!==void 0&&t.result===void 0&&t.error===void 0},e.isJSONRPCRequests=function(t){return Array.isArray(t)&&t.every(e.isJSONRPCRequest)},e.isJSONRPCResponse=function(t){return t.jsonrpc===e.JSONRPC&&t.id!==void 0&&(t.result!==void 0||t.error!==void 0)},e.isJSONRPCResponses=function(t){return Array.isArray(t)&&t.every(e.isJSONRPCResponse)};var n=function(e,t,n){var r={code:e,message:t};return n!=null&&(r.data=n),r};e.JSONRPCErrorException=function(e){t(r,e);function r(t,n,i){var a=e.call(this,t)||this;return Object.setPrototypeOf(a,r.prototype),a.code=n,a.data=i,a}return r.prototype.toObject=function(){return n(this.code,this.message,this.data)},r}(Error),(function(e){e[e.ParseError=-32700]=`ParseError`,e[e.InvalidRequest=-32600]=`InvalidRequest`,e[e.MethodNotFound=-32601]=`MethodNotFound`,e[e.InvalidParams=-32602]=`InvalidParams`,e[e.InternalError=-32603]=`InternalError`})(e.JSONRPCErrorCode||={}),e.createJSONRPCErrorResponse=function(t,r,i,a){return{jsonrpc:e.JSONRPC,id:t,error:n(r,i,a)}},e.createJSONRPCSuccessResponse=function(t,n){return{jsonrpc:e.JSONRPC,id:t,result:n??null}},e.createJSONRPCRequest=function(t,n,r){return{jsonrpc:e.JSONRPC,id:t,method:n,params:r}},e.createJSONRPCNotification=function(t,n){return{jsonrpc:e.JSONRPC,method:t,params:n}}})),require_internal=__commonJSMin((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.DefaultErrorCode=void 0,e.DefaultErrorCode=0})),require_client=__commonJSMin((e=>{var t=e&&e.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||=Promise)(function(n,a){function o(e){try{c(r.next(e))}catch(e){a(e)}}function s(e){try{c(r.throw(e))}catch(e){a(e)}}function c(e){e.done?n(e.value):i(e.value).then(o,s)}c((r=r.apply(e,t||[])).next())})},n=e&&e.__generator||function(e,t){var n={label:0,sent:function(){if(a[0]&1)throw a[1];return a[1]},trys:[],ops:[]},r,i,a,o;return o={next:s(0),throw:s(1),return:s(2)},typeof Symbol==`function`&&(o[Symbol.iterator]=function(){return this}),o;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw TypeError(`Generator is already executing.`);for(;o&&(o=0,s[0]&&(n=0)),n;)try{if(r=1,i&&(a=s[0]&2?i.return:s[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,s[1])).done)return a;switch(i=0,a&&(s=[s[0]&2,a.value]),s[0]){case 0:case 1:a=s;break;case 4:return n.label++,{value:s[1],done:!1};case 5:n.label++,i=s[1],s=[0];continue;case 7:s=n.ops.pop(),n.trys.pop();continue;default:if((a=n.trys,!(a=a.length>0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{Object.defineProperty(e,"__esModule",{value:!0})})),require_server=__commonJSMin((e=>{var t=e&&e.__assign||function(){return t=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{var t=e&&e.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||=Promise)(function(n,a){function o(e){try{c(r.next(e))}catch(e){a(e)}}function s(e){try{c(r.throw(e))}catch(e){a(e)}}function c(e){e.done?n(e.value):i(e.value).then(o,s)}c((r=r.apply(e,t||[])).next())})},n=e&&e.__generator||function(e,t){var n={label:0,sent:function(){if(a[0]&1)throw a[1];return a[1]},trys:[],ops:[]},r,i,a,o;return o={next:s(0),throw:s(1),return:s(2)},typeof Symbol==`function`&&(o[Symbol.iterator]=function(){return this}),o;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw TypeError(`Generator is already executing.`);for(;o&&(o=0,s[0]&&(n=0)),n;)try{if(r=1,i&&(a=s[0]&2?i.return:s[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,s[1])).done)return a;switch(i=0,a&&(s=[s[0]&2,a.value]),s[0]){case 0:case 1:a=s;break;case 4:return n.label++,{value:s[1],done:!1};case 5:n.label++,i=s[1],s=[0];continue;case 7:s=n.ops.pop(),n.trys.pop();continue;default:if((a=n.trys,!(a=a.length>0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{var t=e&&e.__createBinding||(Object.create?(function(e,t,n,r){r===void 0&&(r=n);var i=Object.getOwnPropertyDescriptor(t,n);(!i||(`get`in i?!t.__esModule:i.writable||i.configurable))&&(i={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,r,i)}):(function(e,t,n,r){r===void 0&&(r=n),e[r]=t[n]})),n=e&&e.__exportStar||function(e,n){for(var r in e)r!=="default"&&!Object.prototype.hasOwnProperty.call(n,r)&&t(n,e,r)};Object.defineProperty(e,"__esModule",{value:!0}),n(require_client(),e),n(require_interfaces(),e),n(require_models(),e),n(require_server(),e),n(require_server_and_client(),e)})),import_dist=require_dist(),JSONRPC=class{constructor(e,{onConnected:t,onClosed:n}){this.url=e,this.onConnected=t,this.onClosed=n,this.messageQueue=[],this.serverAndClient=null,this.connect(),this.connectionEstablished=!1,this.timerId=null,this.closed=!1}close(){this.timerId&&clearTimeout(this.timerId),this.webSocket.close(),this.closed=!0}on(e,t){this.serverAndClient.addMethod(e,t)}async requestInternal(e,t,n){let r=await this.serverAndClient.request(e,t);n&&n(r)}requestMessageQueue(){this.messageQueue.forEach(e=>{let[t,n,r]=e;this.requestInternal(t,n,r)}),this.messageQueue=[]}request(e,t,n){this.webSocket.readyState===WebSocket.OPEN?this.requestInternal(e,t,n):this.messageQueue.push([e,t,n])}notify(e,t){switch(this.webSocket.readyState){case WebSocket.OPEN:this.serverAndClient.notify(e,t);break;case WebSocket.CLOSED:break}}connect(e){this.closed||(console.log(`connect`,this.url),this.webSocket=new WebSocket(this.url),this.serverAndClient||=new import_dist.JSONRPCServerAndClient(new import_dist.JSONRPCServer,new import_dist.JSONRPCClient(e=>{try{return this.webSocket.send(JSON.stringify(e)),Promise.resolve()}catch(e){return Promise.reject(e)}})),this.webSocket.onmessage=e=>{this.serverAndClient.receiveAndSend(JSON.parse(e.data.toString()))},this.webSocket.onopen=()=>{console.log(`WebSocket connection established`),this.connectionEstablished=!0,this.onConnected&&this.onConnected(),this.requestMessageQueue()},this.webSocket.onclose=e=>{console.error(`WebScoket closed`,e),this.serverAndClient.rejectAllPendingRequests(`Connection is closed (${e.reason}).`),this.connectionEstablished&&this.onClosed(),this.timerId=setTimeout(()=>{this.connect()},3e3)},this.webSocket.onerror=e=>{console.error(`WebSocket error:`,e),this.webSocket.close()})}},keyevent_exports=__exportAll({convertKeyEvent:()=>convertKeyEvent}),modifierKeys=[`Shift`,`Control`,`Alt`,`Meta`,`CapsLock`],convertKeyTable={Enter:`Return`,ArrowRight:`Right`,ArrowLeft:`Left`,ArrowUp:`Up`,ArrowDown:`Down`,"¡":`1`,"™":`2`,"£":`3`,"¢":`4`,"∞":`5`,"§":`6`,"¶":`7`,"•":`8`,ª:`9`,º:`0`,"–":`-`,"≠":`=`,"“":`[`,"‘":`]`,"«":`\\`,"…":`;`,æ:`'`,"≤":`,`,"≥":`.`,"÷":`/`,"⁄":`!`,"€":`@`,"‹":`#`,"›":`$`,fi:`%`,fl:`^`,"‡":`&`,"°":`*`,"·":`(`,"‚":`)`,"—":`_`,"±":`+`,"”":`{`,"’":`}`,"»":`|`,Ú:`:`,Æ:`"`,"¯":`<`,"˘":`>`,"¿":`?`,œ:`q`,"∑":`w`,"´":`e`,"®":`r`,"†":`t`,"¥":`y`,"¨":`u`,ˆ:`i`,ø:`o`,π:`p`,å:`a`,ß:`s`,"∂":`d`,ƒ:`f`,"©":`g`,"˙":`h`,"∆":`j`,"˚":`k`,"¬":`l`,Ω:`z`,"≈":`x`,ç:`c`,"√":`v`,"∫":`b`,"˜":`n`,µ:`m`,Œ:`Q`,"„":`W`,"´":`E`,"‰":`R`,ˇ:`T`,Á:`Y`,"¨":`U`,ˆ:`I`,Ø:`O`,"∏":`P`,Å:`A`,Í:`S`,Î:`D`,Ï:`F`,"˝":`G`,Ó:`H`,Ô:`J`,"":`K`,Ò:`L`,"¸":`Z`,"˛":`X`,Ç:`C`,"◊":`V`,ı:`B`,"˜":`N`,Â:`M`};function getKey(e){return e.altKey?convertKeyTable[e.key]||(e.code.startsWith(`Key`)?e.code[3].toLowerCase():null)||e.key:convertKeyTable[e.key]||e.key}function convertKeyEvent(e){return modifierKeys.indexOf(e.key)===-1?{key:getKey(e),ctrl:e.ctrlKey,meta:e.altKey,super:e.metaKey,shift:e.shiftKey}:null}var lib_exports=__exportAll({computeWidth:()=>computeWidth,eawVersion:()=>version,getEAW:()=>getEAW}),defs=[[0,31,`N`],[32,126,`Na`],[127,160,`N`],[161,161,`A`],[162,163,`Na`],[164,164,`A`],[165,166,`Na`],[167,168,`A`],[169,169,`N`],[170,170,`A`],[171,171,`N`],[172,172,`Na`],[173,174,`A`],[175,175,`Na`],[176,180,`A`],[181,181,`N`],[182,186,`A`],[187,187,`N`],[188,191,`A`],[192,197,`N`],[198,198,`A`],[199,207,`N`],[208,208,`A`],[209,214,`N`],[215,216,`A`],[217,221,`N`],[222,225,`A`],[226,229,`N`],[230,230,`A`],[231,231,`N`],[232,234,`A`],[235,235,`N`],[236,237,`A`],[238,239,`N`],[240,240,`A`],[241,241,`N`],[242,243,`A`],[244,246,`N`],[247,250,`A`],[251,251,`N`],[252,252,`A`],[253,253,`N`],[254,254,`A`],[255,256,`N`],[257,257,`A`],[258,272,`N`],[273,273,`A`],[274,274,`N`],[275,275,`A`],[276,282,`N`],[283,283,`A`],[284,293,`N`],[294,295,`A`],[296,298,`N`],[299,299,`A`],[300,304,`N`],[305,307,`A`],[308,311,`N`],[312,312,`A`],[313,318,`N`],[319,322,`A`],[323,323,`N`],[324,324,`A`],[325,327,`N`],[328,331,`A`],[332,332,`N`],[333,333,`A`],[334,337,`N`],[338,339,`A`],[340,357,`N`],[358,359,`A`],[360,362,`N`],[363,363,`A`],[364,461,`N`],[462,462,`A`],[463,463,`N`],[464,464,`A`],[465,465,`N`],[466,466,`A`],[467,467,`N`],[468,468,`A`],[469,469,`N`],[470,470,`A`],[471,471,`N`],[472,472,`A`],[473,473,`N`],[474,474,`A`],[475,475,`N`],[476,476,`A`],[477,592,`N`],[593,593,`A`],[594,608,`N`],[609,609,`A`],[610,707,`N`],[708,708,`A`],[709,710,`N`],[711,711,`A`],[712,712,`N`],[713,715,`A`],[716,716,`N`],[717,717,`A`],[718,719,`N`],[720,720,`A`],[721,727,`N`],[728,731,`A`],[732,732,`N`],[733,733,`A`],[734,734,`N`],[735,735,`A`],[736,767,`N`],[768,879,`A`],[880,912,`N`],[913,929,`A`],[930,930,`N`],[931,937,`A`],[938,944,`N`],[945,961,`A`],[962,962,`N`],[963,969,`A`],[970,1024,`N`],[1025,1025,`A`],[1026,1039,`N`],[1040,1103,`A`],[1104,1104,`N`],[1105,1105,`A`],[1106,4351,`N`],[4352,4447,`W`],[4448,8207,`N`],[8208,8208,`A`],[8209,8210,`N`],[8211,8214,`A`],[8215,8215,`N`],[8216,8217,`A`],[8218,8219,`N`],[8220,8221,`A`],[8222,8223,`N`],[8224,8226,`A`],[8227,8227,`N`],[8228,8231,`A`],[8232,8239,`N`],[8240,8240,`A`],[8241,8241,`N`],[8242,8243,`A`],[8244,8244,`N`],[8245,8245,`A`],[8246,8250,`N`],[8251,8251,`A`],[8252,8253,`N`],[8254,8254,`A`],[8255,8307,`N`],[8308,8308,`A`],[8309,8318,`N`],[8319,8319,`A`],[8320,8320,`N`],[8321,8324,`A`],[8325,8360,`N`],[8361,8361,`H`],[8362,8363,`N`],[8364,8364,`A`],[8365,8450,`N`],[8451,8451,`A`],[8452,8452,`N`],[8453,8453,`A`],[8454,8456,`N`],[8457,8457,`A`],[8458,8466,`N`],[8467,8467,`A`],[8468,8469,`N`],[8470,8470,`A`],[8471,8480,`N`],[8481,8482,`A`],[8483,8485,`N`],[8486,8486,`A`],[8487,8490,`N`],[8491,8491,`A`],[8492,8530,`N`],[8531,8532,`A`],[8533,8538,`N`],[8539,8542,`A`],[8543,8543,`N`],[8544,8555,`A`],[8556,8559,`N`],[8560,8569,`A`],[8570,8584,`N`],[8585,8585,`A`],[8586,8591,`N`],[8592,8601,`A`],[8602,8631,`N`],[8632,8633,`A`],[8634,8657,`N`],[8658,8658,`A`],[8659,8659,`N`],[8660,8660,`A`],[8661,8678,`N`],[8679,8679,`A`],[8680,8703,`N`],[8704,8704,`A`],[8705,8705,`N`],[8706,8707,`A`],[8708,8710,`N`],[8711,8712,`A`],[8713,8714,`N`],[8715,8715,`A`],[8716,8718,`N`],[8719,8719,`A`],[8720,8720,`N`],[8721,8721,`A`],[8722,8724,`N`],[8725,8725,`A`],[8726,8729,`N`],[8730,8730,`A`],[8731,8732,`N`],[8733,8736,`A`],[8737,8738,`N`],[8739,8739,`A`],[8740,8740,`N`],[8741,8741,`A`],[8742,8742,`N`],[8743,8748,`A`],[8749,8749,`N`],[8750,8750,`A`],[8751,8755,`N`],[8756,8759,`A`],[8760,8763,`N`],[8764,8765,`A`],[8766,8775,`N`],[8776,8776,`A`],[8777,8779,`N`],[8780,8780,`A`],[8781,8785,`N`],[8786,8786,`A`],[8787,8799,`N`],[8800,8801,`A`],[8802,8803,`N`],[8804,8807,`A`],[8808,8809,`N`],[8810,8811,`A`],[8812,8813,`N`],[8814,8815,`A`],[8816,8833,`N`],[8834,8835,`A`],[8836,8837,`N`],[8838,8839,`A`],[8840,8852,`N`],[8853,8853,`A`],[8854,8856,`N`],[8857,8857,`A`],[8858,8868,`N`],[8869,8869,`A`],[8870,8894,`N`],[8895,8895,`A`],[8896,8977,`N`],[8978,8978,`A`],[8979,8985,`N`],[8986,8987,`W`],[8988,9e3,`N`],[9001,9002,`W`],[9003,9192,`N`],[9193,9196,`W`],[9197,9199,`N`],[9200,9200,`W`],[9201,9202,`N`],[9203,9203,`W`],[9204,9311,`N`],[9312,9449,`A`],[9450,9450,`N`],[9451,9547,`A`],[9548,9551,`N`],[9552,9587,`A`],[9588,9599,`N`],[9600,9615,`A`],[9616,9617,`N`],[9618,9621,`A`],[9622,9631,`N`],[9632,9633,`A`],[9634,9634,`N`],[9635,9641,`A`],[9642,9649,`N`],[9650,9651,`A`],[9652,9653,`N`],[9654,9655,`A`],[9656,9659,`N`],[9660,9661,`A`],[9662,9663,`N`],[9664,9665,`A`],[9666,9669,`N`],[9670,9672,`A`],[9673,9674,`N`],[9675,9675,`A`],[9676,9677,`N`],[9678,9681,`A`],[9682,9697,`N`],[9698,9701,`A`],[9702,9710,`N`],[9711,9711,`A`],[9712,9724,`N`],[9725,9726,`W`],[9727,9732,`N`],[9733,9734,`A`],[9735,9736,`N`],[9737,9737,`A`],[9738,9741,`N`],[9742,9743,`A`],[9744,9747,`N`],[9748,9749,`W`],[9750,9755,`N`],[9756,9756,`A`],[9757,9757,`N`],[9758,9758,`A`],[9759,9791,`N`],[9792,9792,`A`],[9793,9793,`N`],[9794,9794,`A`],[9795,9799,`N`],[9800,9811,`W`],[9812,9823,`N`],[9824,9825,`A`],[9826,9826,`N`],[9827,9829,`A`],[9830,9830,`N`],[9831,9834,`A`],[9835,9835,`N`],[9836,9837,`A`],[9838,9838,`N`],[9839,9839,`A`],[9840,9854,`N`],[9855,9855,`W`],[9856,9874,`N`],[9875,9875,`W`],[9876,9885,`N`],[9886,9887,`A`],[9888,9888,`N`],[9889,9889,`W`],[9890,9897,`N`],[9898,9899,`W`],[9900,9916,`N`],[9917,9918,`W`],[9919,9919,`A`],[9920,9923,`N`],[9924,9925,`W`],[9926,9933,`A`],[9934,9934,`W`],[9935,9939,`A`],[9940,9940,`W`],[9941,9953,`A`],[9954,9954,`N`],[9955,9955,`A`],[9956,9959,`N`],[9960,9961,`A`],[9962,9962,`W`],[9963,9969,`A`],[9970,9971,`W`],[9972,9972,`A`],[9973,9973,`W`],[9974,9977,`A`],[9978,9978,`W`],[9979,9980,`A`],[9981,9981,`W`],[9982,9983,`A`],[9984,9988,`N`],[9989,9989,`W`],[9990,9993,`N`],[9994,9995,`W`],[9996,10023,`N`],[10024,10024,`W`],[10025,10044,`N`],[10045,10045,`A`],[10046,10059,`N`],[10060,10060,`W`],[10061,10061,`N`],[10062,10062,`W`],[10063,10066,`N`],[10067,10069,`W`],[10070,10070,`N`],[10071,10071,`W`],[10072,10101,`N`],[10102,10111,`A`],[10112,10132,`N`],[10133,10135,`W`],[10136,10159,`N`],[10160,10160,`W`],[10161,10174,`N`],[10175,10175,`W`],[10176,10213,`N`],[10214,10221,`Na`],[10222,10628,`N`],[10629,10630,`Na`],[10631,11034,`N`],[11035,11036,`W`],[11037,11087,`N`],[11088,11088,`W`],[11089,11092,`N`],[11093,11093,`W`],[11094,11097,`A`],[11098,11903,`N`],[11904,11929,`W`],[11930,11930,`N`],[11931,12019,`W`],[12020,12031,`N`],[12032,12245,`W`],[12246,12271,`N`],[12272,12287,`W`],[12288,12288,`F`],[12289,12350,`W`],[12351,12352,`N`],[12353,12438,`W`],[12439,12440,`N`],[12441,12543,`W`],[12544,12548,`N`],[12549,12591,`W`],[12592,12592,`N`],[12593,12686,`W`],[12687,12687,`N`],[12688,12771,`W`],[12772,12782,`N`],[12783,12830,`W`],[12831,12831,`N`],[12832,12871,`W`],[12872,12879,`A`],[12880,19903,`W`],[19904,19967,`N`],[19968,42124,`W`],[42125,42127,`N`],[42128,42182,`W`],[42183,43359,`N`],[43360,43388,`W`],[43389,44031,`N`],[44032,55203,`W`],[55204,57343,`N`],[57344,63743,`A`],[63744,64255,`W`],[64256,65023,`N`],[65024,65039,`A`],[65040,65049,`W`],[65050,65071,`N`],[65072,65106,`W`],[65107,65107,`N`],[65108,65126,`W`],[65127,65127,`N`],[65128,65131,`W`],[65132,65280,`N`],[65281,65376,`F`],[65377,65470,`H`],[65471,65473,`N`],[65474,65479,`H`],[65480,65481,`N`],[65482,65487,`H`],[65488,65489,`N`],[65490,65495,`H`],[65496,65497,`N`],[65498,65500,`H`],[65501,65503,`N`],[65504,65510,`F`],[65511,65511,`N`],[65512,65518,`H`],[65519,65532,`N`],[65533,65533,`A`],[65534,94175,`N`],[94176,94180,`W`],[94181,94191,`N`],[94192,94193,`W`],[94194,94207,`N`],[94208,100343,`W`],[100344,100351,`N`],[100352,101589,`W`],[101590,101631,`N`],[101632,101640,`W`],[101641,110575,`N`],[110576,110579,`W`],[110580,110580,`N`],[110581,110587,`W`],[110588,110588,`N`],[110589,110590,`W`],[110591,110591,`N`],[110592,110882,`W`],[110883,110897,`N`],[110898,110898,`W`],[110899,110927,`N`],[110928,110930,`W`],[110931,110932,`N`],[110933,110933,`W`],[110934,110947,`N`],[110948,110951,`W`],[110952,110959,`N`],[110960,111355,`W`],[111356,126979,`N`],[126980,126980,`W`],[126981,127182,`N`],[127183,127183,`W`],[127184,127231,`N`],[127232,127242,`A`],[127243,127247,`N`],[127248,127277,`A`],[127278,127279,`N`],[127280,127337,`A`],[127338,127343,`N`],[127344,127373,`A`],[127374,127374,`W`],[127375,127376,`A`],[127377,127386,`W`],[127387,127404,`A`],[127405,127487,`N`],[127488,127490,`W`],[127491,127503,`N`],[127504,127547,`W`],[127548,127551,`N`],[127552,127560,`W`],[127561,127567,`N`],[127568,127569,`W`],[127570,127583,`N`],[127584,127589,`W`],[127590,127743,`N`],[127744,127776,`W`],[127777,127788,`N`],[127789,127797,`W`],[127798,127798,`N`],[127799,127868,`W`],[127869,127869,`N`],[127870,127891,`W`],[127892,127903,`N`],[127904,127946,`W`],[127947,127950,`N`],[127951,127955,`W`],[127956,127967,`N`],[127968,127984,`W`],[127985,127987,`N`],[127988,127988,`W`],[127989,127991,`N`],[127992,128062,`W`],[128063,128063,`N`],[128064,128064,`W`],[128065,128065,`N`],[128066,128252,`W`],[128253,128254,`N`],[128255,128317,`W`],[128318,128330,`N`],[128331,128334,`W`],[128335,128335,`N`],[128336,128359,`W`],[128360,128377,`N`],[128378,128378,`W`],[128379,128404,`N`],[128405,128406,`W`],[128407,128419,`N`],[128420,128420,`W`],[128421,128506,`N`],[128507,128591,`W`],[128592,128639,`N`],[128640,128709,`W`],[128710,128715,`N`],[128716,128716,`W`],[128717,128719,`N`],[128720,128722,`W`],[128723,128724,`N`],[128725,128727,`W`],[128728,128731,`N`],[128732,128735,`W`],[128736,128746,`N`],[128747,128748,`W`],[128749,128755,`N`],[128756,128764,`W`],[128765,128991,`N`],[128992,129003,`W`],[129004,129007,`N`],[129008,129008,`W`],[129009,129291,`N`],[129292,129338,`W`],[129339,129339,`N`],[129340,129349,`W`],[129350,129350,`N`],[129351,129535,`W`],[129536,129647,`N`],[129648,129660,`W`],[129661,129663,`N`],[129664,129672,`W`],[129673,129679,`N`],[129680,129725,`W`],[129726,129726,`N`],[129727,129733,`W`],[129734,129741,`N`],[129742,129755,`W`],[129756,129759,`N`],[129760,129768,`W`],[129769,129775,`N`],[129776,129784,`W`],[129785,131071,`N`],[131072,196605,`W`],[196606,196607,`N`],[196608,262141,`W`],[262142,917759,`N`],[917760,917999,`A`],[918e3,983039,`N`],[983040,1048573,`A`],[1048574,1048575,`N`],[1048576,1114109,`A`],[1114110,1114111,`N`]],version=`15.1.0`;function getEAWOfCodePoint(e){let t=0,n=defs.length-1;for(;t!==n;){let r=t+(n-t>>1),[i,a,o]=defs[r];if(ea)t=r+1;else return o}return defs[t][2]}function getEAW(e,t=0){let n=e.codePointAt(t);if(n!==void 0)return getEAWOfCodePoint(n)}var defaultWidths={N:1,Na:1,W:2,F:2,H:1,A:1};function computeWidth(e,t){let n=0;for(let r of e){let e=getEAW(r);n+=t&&t[e]||defaultWidths[e]}return n}var textOffsetY=5,isSafari=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);function isWideChar(e){switch(getEAW(e)){case`F`:case`W`:return!0;default:return!1}}function isMacOS(){return window.navigator.userAgent.indexOf(`Mac OS X`)!==-1}function computeFontSize(e){let t=document.createElement(`canvas`).getContext(`2d`);t.font=e;let n=t.measureText(`W`);return[Math.floor(n.width),Math.round(n.fontBoundingBoxAscent+textOffsetY+(n.emHeightDescent||0)),Math.round(n.fontBoundingBoxAscent+textOffsetY)]}function drawBlock({ctx:e,x:t,y:n,width:r,height:i,style:a}){e.fillStyle=a,e.fillRect(t,n,r,i)}function drawText({ctx:e,x:t,y:n,text:r,font:i,style:a,option:o}){n+=Math.round(textOffsetY),e.fillStyle=a,e.font=i,e.textBaseline=`top`;for(let i of r)isWideChar(i)?(e.fillText(i,t,n,o.fontWidth*2),t+=o.fontWidth*2):(e.fillText(i,t,n,o.fontWidth),t+=o.fontWidth)}function drawHorizontalLine({ctx:e,x:t,y:n,width:r,style:i,lineWidth:a=1}){e.strokeStyle=i,e.lineWidth=a,e.setLineDash=[],e.beginPath(),e.moveTo(t,n),e.lineTo(t+r,n),e.stroke()}var Option=class{constructor({fontName:e,fontSize:t}){this.setFont(e,t),this.foreground=`#cccccc`,this.background=`#2d2d2d`}setFont(e,t){let n=t+`px `+e,[r,i,a]=computeFontSize(n);this.fontName=e,this.fontSize=t,this.fontWidth=r,this.fontHeight=i,this.fontAscent=a,this.font=n}};function getLemEditorElement(){return document.getElementById(`lem-editor`)}function normalizeWheelDelta(e,t,n,r){switch(n){case 0:return{dx:e/r,dy:t/r};case 2:return{dx:e*20,dy:t*20};default:return{dx:e,dy:t}}}function extractWholeLines(e,t){let n=Math.trunc(e),r=Math.trunc(t);return{scrollX:n,scrollY:r,remainderX:e-n,remainderY:t-r}}function cursorPosition(e,t){let[n,r]=t.getDisplayRectangle(),i=e.clientX-n,a=e.clientY-r;return{pixelX:i,pixelY:a,x:Math.floor(i/t.option.fontWidth),y:Math.floor(a/t.option.fontHeight)}}function makeWheelHandler(e){let t={x:0,y:0},n=!1,r={pixelX:0,pixelY:0,x:0,y:0};return i=>{i.preventDefault(),r=cursorPosition(i,e);let{dx:a,dy:o}=normalizeWheelDelta(i.deltaX,i.deltaY,i.deltaMode,e.option.fontHeight);t={x:t.x+a,y:t.y+o},n||(n=!0,requestAnimationFrame(()=>{n=!1;let{scrollX:i,scrollY:a,remainderX:o,remainderY:s}=extractWholeLines(t.x,t.y);t={x:o,y:s},(i!==0||a!==0)&&e.jsonrpc.notify(`input`,{kind:`wheel`,value:{...r,wheelX:-i,wheelY:-a}})}))}}function addMouseEventListeners({dom:e,editor:t,isDraggable:n,draggableStyle:r}){e.addEventListener(`contextmenu`,e=>{e.preventDefault()});let i=(e,n)=>{e.preventDefault();let[r,i]=t.getDisplayRectangle(),a=e.clientX-r,o=e.clientY-i,s=Math.floor(a/t.option.fontWidth),c=Math.floor(o/t.option.fontHeight);t.jsonrpc.notify(`input`,{kind:n,value:{x:s,y:c,pixelX:a,pixelY:o,button:e.button,clicks:e.detail}})};e.addEventListener(`mousedown`,e=>{n&&(document.body.style.cursor=r),t.focusHiddenInput(),i(e,`mousedown`)}),e.addEventListener(`mouseup`,e=>{n&&(document.body.style.cursor=`default`),i(e,`mouseup`)});let a=0;e.addEventListener(`mousemove`,e=>{e.preventDefault();let n=Date.now();if(n-a>50){a=n;let[r,i]=t.getDisplayRectangle(),o=e.clientX-r,s=e.clientY-i,c=Math.floor(o/t.option.fontWidth),l=Math.floor(s/t.option.fontHeight);t.jsonrpc.notify(`input`,{kind:`mousemove`,value:{x:c,y:l,pixelX:o,pixelY:s,button:e.buttons===0?null:e.buttons-1}})}}),n&&(e.addEventListener(`mouseover`,()=>{document.body.style.cursor=r}),e.addEventListener(`mouseout`,e=>{e.buttons!==1&&(document.body.style.cursor=`default`)})),e.addEventListener(`wheel`,makeWheelHandler(t))}var zIndexTable={"floating-window":200,modeline:100,"vertical-border":100,"horizontal-border":101};function zindex(e){return zIndexTable[e]||0}var borderOffsetX=5,borderOffsetY=10,BaseSurface=class{constructor({editor:e}){this.editor=e,this.mainDOM=null,this.wrapper=null}delete(){this.wrapper?getLemEditorElement().removeChild(this.wrapper):getLemEditorElement().removeChild(this.mainDOM)}setupDOM({dom:e,isFloating:t,border:n,cssClassName:r}){this.mainDOM=e,t&&n?(this.wrapper=document.createElement(`div`),r&&(this.wrapper.className=r),this.wrapper.style.position=`absolute`,this.wrapper.style.backgroundColor=this.editor.option.background,this.wrapper.style.zIndex=zindex(`floating-window`),this.wrapper.appendChild(e),getLemEditorElement().appendChild(this.wrapper)):(r&&(e.className=r),getLemEditorElement().appendChild(e))}move(e,t){let[n,r]=this.editor.getDisplayRectangle(),i=Math.floor(n+e),a=Math.floor(r+t);this.wrapper?(this.wrapper.style.left=i-borderOffsetX+`px`,this.wrapper.style.top=a-borderOffsetY+`px`,this.mainDOM.style.left=borderOffsetX+`px`,this.mainDOM.style.top=borderOffsetY+`px`):(this.mainDOM.style.left=i+`px`,this.mainDOM.style.top=a+`px`)}_resize(e,t){let n=window.devicePixelRatio||1;this.mainDOM.width=e*n,this.mainDOM.height=t*n,this.mainDOM.style.width=e+`px`,this.mainDOM.style.height=t+`px`,this.wrapper&&(this.wrapper.style.width=e+borderOffsetX*2+`px`,this.wrapper.style.height=t+borderOffsetY*2+`px`)}drawBlock(e,t,n,r,i){}drawText(e,t,n,r,i,a,o,s){}drawImage(e,t,n,r,i,a,o){}clearImages(e,t){}clearAllImages(){}touch(){}evalIn(code){return eval(code)}},CanvasSurface=class extends BaseSurface{constructor({editor:e,view:t,pixelX:n,pixelY:r,pixelWidth:i,pixelHeight:a,styles:o,isFloating:s,border:c,cssClassName:l}){super({editor:e});let u=this.setupCanvas(o);this.setupDOM({dom:u,isFloating:s,border:c,cssClassName:l}),this.move(n,r),this.resize(i,a),this.drawingQueue=[],addMouseEventListeners({dom:u,editor:e})}setupCanvas(e){let t=document.createElement(`canvas`);if(t.style.position=`absolute`,e)for(let n in e)t.style[n]=e[n];return t}resize(e,t){this._resize(e,t);let n=window.devicePixelRatio||1;this.mainDOM.getContext(`2d`).scale(n,n)}move(e,t){if(super.move(e,t),this.imageEls)for(let[,e]of this.imageEls)this.positionImage(e)}delete(){this.clearAllImages(),super.delete()}drawBlock(e,t,n,r,i){this.drawingQueue.push(function(a){drawBlock({ctx:a,x:e,y:t,width:n,height:r,style:i})})}drawText(e,t,n,r,i,a,o,s){let c=this.editor.option,l=o??t,u=s??c.fontHeight;this.drawingQueue.push(function(o){if(a=a?`${c.fontSize}px ${a}`:c.font,!i)drawBlock({ctx:o,x:e,y:l,width:r,height:u,style:c.background}),drawText({ctx:o,x:e,y:t,text:n,style:c.foreground,font:a,option:c});else{let{foreground:s,background:d,bold:f,reverse:p,underline:m,cursor:h}=i;if(s||=c.foreground,d||=c.background,p){let e=d;d=s,s=e}h&&(d=c.background),drawBlock({ctx:o,x:e,y:l,width:r,height:u,style:d}),drawText({ctx:o,x:e,y:t,text:n,style:s,font:f?`bold `+a:a,option:c}),m&&drawHorizontalLine({ctx:o,x:e,y:t+c.fontHeight-2,width:r,style:typeof m==`string`?m:s,lineWidth:2})}})}imageBaseLeft(){return parseFloat(this.mainDOM.style.left)||0}imageBaseTop(){return parseFloat(this.mainDOM.style.top)||0}drawImage(e,t,n,r,i,a,o){this.imageEls||=new Map;let s=e+`,`+t,c=this.imageEls.get(s);if(c&&c.url!==o&&(c.el.remove(),this.imageEls.delete(s),c=null),!c){let e=document.createElement(`img`);e.style.position=`absolute`,e.style.pointerEvents=`none`,e.style.zIndex=`1`,e.src=o,this.mainDOM.parentNode.appendChild(e),c={el:e,url:o},this.imageEls.set(s,c)}c.x=e,c.y=t,c.width=n,c.height=r,c.clipWidth=i,c.clipHeight=a,this.positionImage(c)}positionImage(e){e.el.style.left=this.imageBaseLeft()+e.x+`px`,e.el.style.top=this.imageBaseTop()+e.y+`px`,e.el.style.width=e.width+`px`,e.el.style.height=e.height+`px`;let t=e.clipWidth==null?0:Math.max(0,e.width-e.clipWidth),n=e.clipHeight==null?0:Math.max(0,e.height-e.clipHeight);e.el.style.clipPath=t>0||n>0?`inset(0px ${t}px ${n}px 0px)`:``}clearImages(e,t){if(this.imageEls)for(let[n,r]of this.imageEls){let i=r.y+(r.height||0);r.ye&&(r.el.remove(),this.imageEls.delete(n))}}clearAllImages(){if(this.imageEls){for(let[,e]of this.imageEls)e.el.remove();this.imageEls.clear()}}touch(){let e=this.mainDOM.getContext(`2d`);for(let t of this.drawingQueue)t(e);this.drawingQueue=[]}activate(){this.mainDOM.dataset.store=`active`}deactivate(){this.mainDOM.dataset.store=`inactive`}},HTMLSurface=class extends BaseSurface{constructor({editor:e,pixelX:t,pixelY:n,pixelWidth:r,pixelHeight:i,styles:a,option:o,isFloating:s,border:c,html:l}){super({editor:e});let u=document.createElement(`iframe`);this.setupDOM({dom:u,isFloating:s,border:c}),u.style.position=`absolute`,u.style.backgroundColor=o.background,u.setAttribute(`sandbox`,`allow-scripts allow-same-origin`),u.srcdoc=l,u.addEventListener(`load`,()=>{let e=u.contentWindow;e.invokeLem=(e,t)=>parent.postMessage({type:`invoke-lem`,method:e,args:t})}),this.iframe=u,this.move(t,n),this.resize(r,i)}resize(e,t){this._resize(e,t)}update(e){let t=this.iframe.contentWindow.scrollY;this.iframe.srcdoc=e,this.iframe.onload=()=>{this.iframe.onload=null,this.iframe.contentWindow.scrollTo(0,t)}}evalIn(e){return this.iframe.contentWindow.eval(e)}},VerticalBorder=class{constructor({x:e,y:t,height:n,option:r,editor:i}){this.option=r,this.editor=i,this.line=document.createElement(`div`),this.line.className=`lem-editor__vertical-border`,this.line.style.height=n+`px`,this.line.style.position=`absolute`,this.line.style.zIndex=zindex(`vertical-border`),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:i,isDraggable:!0,draggableStyle:`col-resize`})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.line.style.left=Math.floor(n+e-this.option.fontWidth/2)+`px`,this.line.style.top=r+t+`px`}resize(e){this.line.style.height=e+`px`}},HorizontalBorder=class{constructor({x:e,y:t,width:n,option:r,editor:i}){this.option=r,this.editor=i,this.line=document.createElement(`div`),this.line.className=`lem-editor__horizontal-border`,this.line.style.width=n+`px`,this.line.style.position=`absolute`,this.line.style.zIndex=zindex(`horizontal-border`),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:i,isDraggable:!0,draggableStyle:`row-resize`})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.line.style.left=n+e+`px`,this.line.style.top=Math.floor(r+t-4)+`px`}resize(e){this.line.style.width=e+`px`}},viewStyles={header:()=>{},tile:()=>{},floating:e=>({boxSizing:`border-box`,borderColor:e.foreground,backgroundColor:e.background})};function getViewStyle(e,t){return viewStyles[e](t)||{}}var View=class{constructor({id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,useModeline:l,kind:u,type:d,content:f,border:p,borderShape:m,option:h,editor:g}){switch(this.option=h,this.id=e,this.x=t,this.y=n,this.width=r,this.height=i,this.pixelX=a,this.pixelY=o,this.pixelWidth=s,this.pixelHeight=c,this.useModeline=l,this.kind=u,this.type=d,this.border=p,this.borderShape=m,this.editor=g,this.bottomBar=null,this.leftsideBar=null,u){case`tile`:this.mainSurface=this.makeSurface(d,f),this.leftSideBar=new VerticalBorder({x:a,y:o,height:c+(l?h.fontHeight:0),option:h,editor:g}),l||(this.bottomBar=new HorizontalBorder({x:a,y:o+c-h.fontHeight,width:s,option:h,editor:g}));break;case`header`:this.mainSurface=this.makeSurface(d,f);break;case`floating`:this.mainSurface=this.makeSurface(d,f),m===`left-border`&&(this.leftSideBar=new VerticalBorder({x:a,y:o,height:c,option:h,editor:g}));break}this.modelineSurface=l?this.makeModelineSurface():null}delete(){this.mainSurface.delete(),this.modelineSurface&&this.modelineSurface.delete(),this.leftSideBar&&this.leftSideBar.delete(),this.bottomBar&&this.bottomBar.delete()}move(e,t,n,r){this.x=e,this.y=t,this.pixelX=n,this.pixelY=r,this.mainSurface.move(n,r),this.modelineSurface&&this.modelineSurface.move(n,r+this.pixelHeight),this.leftSideBar&&this.leftSideBar.move(n,r),this.bottomBar&&this.bottomBar.move(n,r+this.pixelHeight)}resize(e,t,n,r){this.width=e,this.height=t,this.pixelWidth=n,this.pixelHeight=r,this.mainSurface.resize(n,r),this.modelineSurface&&(this.modelineSurface.move(this.pixelX,this.pixelY+r),this.modelineSurface.resize(n,this.option.fontHeight)),this.leftSideBar&&this.leftSideBar.resize(r+(this.modelineSurface?this.option.fontHeight:0)),this.bottomBar&&this.bottomBar.resize(n)}clear(){this.mainSurface.drawBlock(0,0,this.pixelWidth,this.pixelHeight,this.option.background),this.mainSurface.clearImages(0,this.pixelHeight)}clearEol(e,t,n){n??=this.option.fontHeight,this.mainSurface.drawBlock(e,t,this.pixelWidth-e,n,this.option.background),this.mainSurface.clearImages(t,t+n)}clearEob(e,t){this.mainSurface.drawBlock(e,t,this.pixelWidth,this.pixelHeight-t,this.option.background),this.mainSurface.clearImages(t,this.pixelHeight)}print(e,t,n,r,i,a,o,s){this.mainSurface.drawText(e,t,n,r,i,a,o,s)}drawBlock(e,t,n,r,i){this.mainSurface.drawBlock(e,t,n,r,i||this.option.background)}drawBlockOnModeline(e,t,n,r,i){this.modelineSurface&&this.modelineSurface.drawBlock(e,t,n,r,i||this.option.background)}printImage(e,t,n,r,i,a,o){this.mainSurface.drawImage(e,t,n,r,i,a,o)}printToModeline(e,t,n,r,i,a,o){this.modelineSurface&&this.modelineSurface.drawText(e,t,n,r,i,null,a,o)}touch(e){this.mainSurface.touch(),this.modelineSurface&&(this.modelineSurface.touch(),e?this.modelineSurface.activate():this.modelineSurface.deactivate())}makeSurface(e,t){switch(e){case`html`:return this.makeHTMLSurface(t);case`editor`:return this.makeEditorSurface();default:console.error(`unknown type: ${e}`)}}makeHTMLSurface(e){return new HTMLSurface({editor:this.editor,pixelX:this.pixelX,pixelY:this.pixelY,pixelWidth:this.pixelWidth,pixelHeight:this.pixelHeight,styles:getViewStyle(this.kind,this.option),option:this.option,isFloating:this.kind===`floating`,border:this.border,html:e})}makeEditorSurface(){let e=this.borderShape===`left-border`?0:this.border,t=this.kind===`floating`;return new CanvasSurface({option:this.editor.option,pixelX:this.pixelX,pixelY:this.pixelY,pixelWidth:this.pixelWidth,pixelHeight:this.pixelHeight,styles:getViewStyle(this.kind,this.option),editor:this.editor,border:e,isFloating:t,view:this,cssClassName:t&&e?`lem-editor__floating-window--bordered`:null})}makeModelineSurface(){let e=new CanvasSurface({option:this.editor.option,pixelX:this.pixelX,pixelY:this.pixelY+this.pixelHeight,pixelWidth:this.pixelWidth,pixelHeight:this.option.fontHeight,editor:this.editor,view:this,styles:{zIndex:zindex(`modeline`)},cssClassName:`lem-editor__mode-line`});return addMouseEventListeners({dom:e.mainDOM,editor:this.editor,isDraggable:!0,draggableStyle:`row-resize`}),e}changeToHTMLContent(e){this.mainSurface.constructor.name===`HTMLSurface`?this.mainSurface.update(e):(this.mainSurface.delete(),this.mainSurface=this.makeHTMLSurface(e))}changeToEditorContent(){this.mainSurface.delete(),this.mainSurface=this.makeEditorSurface()}evalIn(e){return this.mainSurface.evalIn(e)}};function isPasteKeyEvent(e){return isMacOS()?e.metaKey&&e.key===`v`:e.ctrlKey&&e.shiftKey&&e.key===`V`}var Input=class{constructor(e){let t=e.option;this.editor=e,this.composition=!1,this.ignoreKeydownAfterCompositionend=!1,this.span=document.createElement(`span`),this.span.style.color=t.foreground,this.span.style.backgroundColor=t.background,this.span.style.position=`absolute`,this.span.style.zIndex=1e6,this.span.style.top=`0`,this.span.style.left=`0`,this.span.style.font=t.font,this.input=document.createElement(`input`),this.input.style.backgroundColor=`transparent`,this.input.style.color=`transparent`,this.input.style.width=`0`,this.input.style.padding=`0`,this.input.style.margin=`0`,this.input.style.border=`none`,this.input.style.position=`absolute`,this.input.style.zIndex=`-10`,this.input.style.top=`0`,this.input.style.left=`0`,this.input.style.font=t.font,this.input.addEventListener(`blur`,e=>{this.input.focus()}),this.input.addEventListener(`input`,e=>{this.composition===!1&&(this.input.value=``,this.span.innerHTML=``,this.input.style.width=`0`,isMacOS()||this.editor.emitInputString(e.data))}),this.input.addEventListener(`paste`,async e=>{e.preventDefault();let t=e.clipboardData||window.Clipboard.data,n=t?.getData(`text`)??t?.getData(`text/plain`);if(n&&n.length>0){this.editor.emitInputString(n);return}try{if(navigator.clipboard?.readText){let e=await navigator.clipboard.readText();if(e&&e.length>0){this.editor.emitInputString(e);return}}}catch(e){console.warn(`clipboard.readText() failed:`,e)}alert(`Paste failed (permission/environment restriction`)}),this.input.addEventListener(`keydown`,e=>{if(!isPasteKeyEvent(e)&&!(e.isComposing||this.composition)&&e.key!==`Process`){if(this.ignoreKeydownAfterCompositionend&&(isSafari||isMacOS())){e.preventDefault(),this.ignoreKeydownAfterCompositionend=!1;return}if(!(!isMacOS()&&!e.ctrlKey&&!e.altKey&&e.key.length===1)&&(e.preventDefault(),e.isComposing!==!0&&e.code!==``))return setTimeout(()=>{this.composition||(this.editor.emitInput(e),this.input.value=``)},0),!1}}),this.input.addEventListener(`compositionstart`,e=>{this.composition=!0,this.span.innerHTML=this.input.value,this.input.style.width=this.span.offsetWidth+`px`}),this.input.addEventListener(`compositionupdate`,e=>{this.span.innerHTML=e.data,this.input.style.width=this.span.offsetWidth+`px`}),this.input.addEventListener(`compositionend`,e=>{this.composition=!1,this.editor.emitInputString(this.input.value),this.input.value=``,this.span.innerHTML=this.input.value,this.input.style.width=`0`,this.ignoreKeydownAfterCompositionend=!0}),document.body.appendChild(this.input),document.body.appendChild(this.span),this.input.focus()}finalize(){document.body.removeChild(this.input),document.body.removeChild(this.span)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.span.style.top=r+t+`px`,this.span.style.left=n+e+`px`,this.input.style.top=this.span.offsetTop+`px`,this.input.style.left=this.span.offsetLeft+`px`}updateForeground(e){this.span.style.color=e}updateBackground(e){this.span.style.backgroundColor=e}},MessageTable=class{constructor(){this.map=new Map}register(e,t){for(let n in t){let r=t[n];this.map.set(n,r),e.on(n,r)}}get(e){return this.map.get(e)}};function getDisplayRectangleDefault(){return[0,0,window.innerWidth,window.innerHeight]}var Editor=class{constructor({getDisplayRectangle:e=getDisplayRectangleDefault,fontName:t,fontSize:n,url:r,onExit:i,onClosed:a}){this.getDisplayRectangle=e,this.option=new Option({fontName:t,fontSize:n}),this.onExit=i,this.input=new Input(this),this.cursors=new Map,this.cursorOverlay=document.createElement(`div`),this.cursorOverlay.className=`lem-cursor`,this.cursorOverlay.style.width=this.option.fontWidth+`px`,this.cursorOverlay.style.height=this.option.fontHeight+`px`,this.cursorOverlay.style.backgroundColor=`#ffffff`,this.cursorType=`box`,this.viewMap=new Map,this.jsonrpc=new JSONRPC(r,{onClosed:()=>{a()}}),this.messageTable=new MessageTable,this.messageTable.register(this.jsonrpc,{"update-foreground":this.updateForeground.bind(this),"update-background":this.updateBackground.bind(this),"make-view":this.makeView.bind(this),"delete-view":this.deleteView.bind(this),"resize-view":this.resize.bind(this),"move-view":this.move.bind(this),"redraw-view-after":this.redrawViewAfter.bind(this),clear:this.clear.bind(this),"clear-eol":this.clearEol.bind(this),"clear-eob":this.clearEob.bind(this),put:this.put.bind(this),"put-image":this.putImage.bind(this),"modeline-put":this.modelinePut.bind(this),"draw-block":this.drawBlock.bind(this),"modeline-draw-block":this.modelineDrawBlock.bind(this),"update-display":this.updateDisplay.bind(this),"move-cursor":this.moveCursor.bind(this),"change-view":this.changeView.bind(this),"resize-display":this.resizeDisplay.bind(this),bulk:this.bulk.bind(this),exit:this.exitEditor.bind(this),"get-clipboard-text":this.getClipboardText.bind(this),"set-clipboard-text":this.setClipboardText.bind(this),"js-eval":this.jsEval.bind(this),"set-font":this.setFont.bind(this),"get-font":this.getFont.bind(this),"get-display-size":this.getDisplaySize.bind(this),"load-css":this.loadCSS.bind(this),"update-cursor-shape":this.updateCursorShape.bind(this)}),this.login(),this.boundedHandleResize=this.handleResize.bind(this),this.focusHiddenInput=this.focusHiddenInput.bind(this)}init(){window.addEventListener(`resize`,this.boundedHandleResize),document.getElementsByTagName(`html`)[0].style[`background-color`]=`#333`,getLemEditorElement().appendChild(this.cursorOverlay)}finalize(){window.removeEventListener(`resize`,this.boundedHandleResize),this.input.finalize(),this.cursorOverlay.parentNode&&this.cursorOverlay.parentNode.removeChild(this.cursorOverlay)}closeConnection(){this.jsonrpc.close()}emitInput(e){let t=convertKeyEvent(e);if(t){if(t.key===`]`&&t.ctrl&&!t.meta&&!t.super&&!t.shift){this.jsonrpc.notify(`input`,{kind:`abort`});return}t.key!==`Unidentified`&&this.jsonrpc.notify(`input`,{kind:`key`,value:t})}}emitInputString(e){e?this.jsonrpc.notify(`input`,{kind:`input-string`,value:e}):console.error(`unexpected argument`,e)}redrawParams(){return{size:this.getDisplaySize(),fontWidth:this.option.fontWidth,fontHeight:this.option.fontHeight,fontAscent:this.option.fontAscent}}handleResize(e){this.jsonrpc.notify(`redraw`,this.redrawParams())}focusHiddenInput(){let e=this.input?.input;if(e){try{window.focus()}catch{}requestAnimationFrame(()=>{setTimeout(()=>{e.focus({preventScroll:!0})},0)})}}sendNotification(e,t){this.jsonrpc.notify(e,t)}request(e,t,n){this.jsonrpc.request(e,t,n)}getDisplaySize(){let[e,t,n,r]=this.getDisplayRectangle();return{width:Math.floor(n/this.option.fontWidth),height:Math.floor(r/this.option.fontHeight)}}callMessage(e,t){this.messageTable.get(e)(t)}findViewById(e){return this.viewMap.get(e)}login(){this.jsonrpc.request(`login`,{size:this.getDisplaySize(),foreground:this.option.foreground,background:this.option.background,fontWidth:this.option.fontWidth,fontHeight:this.option.fontHeight,fontAscent:this.option.fontAscent},e=>{if(this.updateForeground(e.foreground),this.updateBackground(e.background),e.views)for(let t of e.views)this.makeView(t);this.jsonrpc.notify(`redraw`,this.redrawParams())})}updateForeground(e){e!=null&&(this.option.foreground=e,this.input.updateForeground(e))}updateBackground(e){if(e==null)return;this.option.background=e,this.input.updateBackground(e);let t=getLemEditorElement();t.style.backgroundColor=e}makeView({id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,use_modeline:l,kind:u,type:d,content:f,border:p,border_shape:m}){let h=new View({option:this.option,id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,useModeline:l,kind:u,type:d,content:f,border:p,borderShape:m,editor:this});this.viewMap.set(e,h)}deleteView({viewInfo:{id:e}}){this.findViewById(e).delete(),this.viewMap.delete(e)}resize({viewInfo:{id:e},width:t,height:n,pixelWidth:r,pixelHeight:i}){let a=this.findViewById(e);a?a.resize(t,n,r,i):console.warn(`resize: view not found for id ${e}`)}move({viewInfo:{id:e},x:t,y:n,pixelX:r,pixelY:i}){let a=this.findViewById(e);a?a.move(t,n,r,i):console.warn(`move: view not found for id ${e}`)}redrawViewAfter({viewInfo:{id:e},isActive:t}){this.findViewById(e).touch(t)}clear({viewInfo:{id:e}}){this.findViewById(e).clear()}clearEol({viewInfo:{id:e},x:t,y:n,height:r}){this.findViewById(e).clearEol(t,n,r)}clearEob({viewInfo:{id:e},x:t,y:n}){this.findViewById(e).clearEob(t,n)}put({viewInfo:{id:e},x:t,y:n,text:r,textWidth:i,attribute:a,font:o,backgroundY:s,backgroundHeight:c}){this.findViewById(e).print(t,n,r,i,a,o,s,c)}drawBlock({viewInfo:{id:e},x:t,y:n,width:r,height:i,color:a}){this.findViewById(e).drawBlock(t,n,r,i,a)}modelineDrawBlock({viewInfo:{id:e},x:t,y:n,width:r,height:i,color:a}){this.findViewById(e).drawBlockOnModeline(t,n,r,i,a)}putImage({viewInfo:{id:e},x:t,y:n,pixelWidth:r,pixelHeight:i,clipWidth:a,clipHeight:o,url:s}){this.findViewById(e).printImage(t,n,r,i,a,o,s)}modelinePut({viewInfo:{id:e},x:t,y:n,text:r,textWidth:i,attribute:a,backgroundY:o,backgroundHeight:s}){this.findViewById(e).printToModeline(t,n,r,i,a,o,s)}updateDisplay(){}moveCursor({viewInfo:{id:e},x:t,y:n,color:r,cursorText:i,cursorForeground:a}){let o=this.findViewById(e),[s,c]=this.getDisplayRectangle(),l=o.pixelX+t,u=o.pixelY+n;this.input.move(l,u);let d=r||this.option.foreground,f=a||this.option.background,p=this.cursorOverlay;switch(this.cursorType){case`bar`:p.style.left=s+l+`px`,p.style.top=c+u+`px`,p.style.width=`2px`,p.style.height=this.option.fontHeight+`px`,p.style.backgroundColor=d,p.textContent=``,p.style.color=``,p.style.font=``,p.style.paddingTop=``;break;case`underline`:p.style.left=s+l+`px`,p.style.top=c+u+this.option.fontHeight-2+`px`,p.style.width=this.option.fontWidth+`px`,p.style.height=`2px`,p.style.backgroundColor=d,p.textContent=``,p.style.color=``,p.style.font=``,p.style.paddingTop=``;break;default:p.style.left=s+l+`px`,p.style.top=c+u+`px`,p.style.width=this.option.fontWidth+`px`,p.style.height=this.option.fontHeight+`px`,p.style.backgroundColor=d,p.style.font=this.option.font,p.style.paddingTop=textOffsetY+`px`,p.textContent=i||``,p.style.color=f;break}p.style.animation=`none`,p.offsetHeight,p.style.animation=``}updateCursorShape({cursorType:e}){this.cursorType=e||`box`}changeView({viewInfo:{id:e},type:t,content:n}){let r=this.findViewById(e);switch(t){case`html`:r.changeToHTMLContent(n);break;case`editor`:r.changeToEditorContent();break}}resizeDisplay({width:e,height:t}){let n=getLemEditorElement();n.style.width=Math.floor(e*this.option.fontWidth)+`px`,n.style.height=Math.floor(t*this.option.fontHeight)+`px`}bulk(e){for(let{method:t,argument:n}of e)this.callMessage(t,n)}exitEditor(){this.onExit&&this.onExit()}getClipboardText(){navigator.clipboard?.readText().then(e=>{this.jsonrpc.notify(`got-clipboard-text`,{text:e})})}setClipboardText({text:e}){navigator.clipboard&&navigator.clipboard.writeText(e)}jsEval({viewInfo:{id:e},code:t}){let n=this.findViewById(e).evalIn(t);return n&&n.toString()}setFont({fontName:e,fontSize:t}){this.option.setFont(e||this.option.fontName,t||this.option.fontSize),this.cursorOverlay.style.width=this.option.fontWidth+`px`,this.cursorOverlay.style.height=this.option.fontHeight+`px`,this.jsonrpc.notify(`redraw`,this.redrawParams())}getFont(){return{name:this.option.fontName,size:this.option.fontSize}}loadCSS({content:e}){let t=document.createElement(`style`);t.textContent=e,document.head.appendChild(t)}notifyToServer(e,t){this.jsonrpc.notify(`invoke`,{method:e,args:t})}},canvas=document.querySelector(`#editor`);async function main(){await Promise.all([document.fonts.load(`19px file-icons`),document.fonts.load(`19px AllTheIcons`),document.fonts.load(`19px fontawesome`),document.fonts.load(`19px material-design-icons`),document.fonts.load(`19px octicons`)]),await document.fonts.ready;let e=new Editor({canvas,fontName:`Monospace`,fontSize:18,url:`${window.location.protocol===`https:`?`wss`:`ws`}://${window.location.hostname}:${window.location.port}`,onExit:null,onClosed:null});window.addEventListener(`message`,t=>{t.data.type===`invoke-lem`&&e.notifyToServer(t.data.method,t.data.args)}),e.init()}main(); \ No newline at end of file diff --git a/frontends/server/frontend/editor.js b/frontends/server/frontend/editor.js index 99f213b91..1eaf87557 100644 --- a/frontends/server/frontend/editor.js +++ b/frontends/server/frontend/editor.js @@ -345,7 +345,7 @@ class BaseSurface { // drawing coordinates are relative to the surface's own top-left corner. drawBlock(x, y, width, height, color) { } - drawText(x, y, text, textWidth, attribute, font) { } + drawText(x, y, text, textWidth, attribute, font, backgroundY, backgroundHeight) { } drawImage(x, y, width, height, clipWidth, clipHeight, url) { } clearImages(yStart, yEnd) { } @@ -411,18 +411,19 @@ class CanvasSurface extends BaseSurface { }); } - // the background is filled first, textWidth by one line of text, then the text drawn over it. a - // rectangle taller than that is a `drawBlock', not a text-less draw through here. - drawText(x, y, text, textWidth, attribute, font) { + // the background is filled first, then the text over it. it covers the row the text sits on, + // which an image can make taller, and defaults to one line at the text's own y. + drawText(x, y, text, textWidth, attribute, font, backgroundY, backgroundHeight) { const option = this.editor.option; - const blockHeight = option.fontHeight; + const blockY = backgroundY == null ? y : backgroundY; + const blockHeight = backgroundHeight == null ? option.fontHeight : backgroundHeight; this.drawingQueue.push(function(ctx) { font = font ? `${option.fontSize}px ${font}` : option.font; if (!attribute) { drawBlock({ ctx, x: x, - y: y, + y: blockY, width: textWidth, height: blockHeight, style: option.background, @@ -457,7 +458,7 @@ class CanvasSurface extends BaseSurface { drawBlock({ ctx, x: x, - y: y, + y: blockY, width: textWidth, height: blockHeight, style: background, @@ -867,7 +868,7 @@ class View { this.mainSurface.clearImages(y, this.pixelHeight); } - print(x, y, text, textWidth, attribute, font) { + print(x, y, text, textWidth, attribute, font, backgroundY, backgroundHeight) { this.mainSurface.drawText( x, y, @@ -875,6 +876,8 @@ class View { textWidth, attribute, font, + backgroundY, + backgroundHeight, ); } @@ -894,7 +897,7 @@ class View { this.mainSurface.drawImage(x, y, pixelWidth, pixelHeight, clipWidth, clipHeight, url); } - printToModeline(x, y, text, textWidth, attribute) { + printToModeline(x, y, text, textWidth, attribute, backgroundY, backgroundHeight) { if (this.modelineSurface) { this.modelineSurface.drawText( x, @@ -903,6 +906,8 @@ class View { textWidth, attribute, null, + backgroundY, + backgroundHeight, ); } } @@ -1472,9 +1477,9 @@ export class Editor { view.clearEob(x, y); } - put({ viewInfo: { id }, x, y, text, textWidth, attribute, font }) { + put({ viewInfo: { id }, x, y, text, textWidth, attribute, font, backgroundY, backgroundHeight }) { const view = this.findViewById(id); - view.print(x, y, text, textWidth, attribute, font); + view.print(x, y, text, textWidth, attribute, font, backgroundY, backgroundHeight); } drawBlock({ viewInfo: { id }, x, y, width, height, color }) { @@ -1492,9 +1497,9 @@ export class Editor { view.printImage(x, y, pixelWidth, pixelHeight, clipWidth, clipHeight, url); } - modelinePut({ viewInfo: { id }, x, y, text, textWidth, attribute }) { + modelinePut({ viewInfo: { id }, x, y, text, textWidth, attribute, backgroundY, backgroundHeight }) { const view = this.findViewById(id); - view.printToModeline(x, y, text, textWidth, attribute); + view.printToModeline(x, y, text, textWidth, attribute, backgroundY, backgroundHeight); } updateDisplay() { diff --git a/frontends/server/main.lisp b/frontends/server/main.lisp index 56170190e..316b531a2 100644 --- a/frontends/server/main.lisp +++ b/frontends/server/main.lisp @@ -608,12 +608,13 @@ returns true when one of them changed, since nothing already measured survives a ;;; drawing -(defgeneric draw-object (jsonrpc object x y view) +(defgeneric draw-object (jsonrpc object x y view row) (:documentation "draw OBJECT into VIEW with its top-left corner at pixel position X, Y. -`lem-core/display:layout-row' already chose Y as the row's baseline minus this object's ascent, so -a method never needs to know the row's own top or height.")) +`lem-core/display:layout-row' already positioned it, so ROW is only for what an object shares with +the rest of its row: the full height a background fills and where the row's text sits, which is +where the caret goes.")) -(defmethod draw-object (jsonrpc (object display:void-object) x y view) +(defmethod draw-object (jsonrpc (object display:void-object) x y view row) (values)) (defvar *put-target* :edit-area) @@ -651,20 +652,28 @@ same hash." (setf attribute (lem:make-attribute :background lem-if:*background-color-of-drawing-window*))) (attribute-to-hash attribute))) -(defun put (jsonrpc view x y string attribute &key font text-width) - "draw STRING at pixel position X, Y in VIEW, over a TEXT-WIDTH background one line of text tall." +(defun taller-than-text-p (jsonrpc row) + "whether ROW is taller than a line of text, which an image on it can make it." + (and row (> (display:row-height row) (jsonrpc-cell-height jsonrpc)))) + +(defun put (jsonrpc view x y string attribute &key font text-width row) + "draw STRING at pixel position X, Y in VIEW, over a background TEXT-WIDTH wide and as tall as ROW." (with-error-handler () - (notify* jsonrpc - (ecase *put-target* - (:edit-area "put") - (:modeline "modeline-put")) - (hash "viewInfo" (view-id-hash view) - "x" x - "y" y - "text" string - "textWidth" (or text-width (* (lem:string-width string) (jsonrpc-cell-width jsonrpc))) - "attribute" (ensure-attribute attribute) - "font" font)))) + (let ((tall (taller-than-text-p jsonrpc row))) + (notify* jsonrpc + (ecase *put-target* + (:edit-area "put") + (:modeline "modeline-put")) + (hash "viewInfo" (view-id-hash view) + "x" x + "y" y + "text" string + "textWidth" (or text-width + (* (lem:string-width string) (jsonrpc-cell-width jsonrpc))) + "backgroundY" (and tall (display:row-top row)) + "backgroundHeight" (and tall (display:row-height row)) + "attribute" (ensure-attribute attribute) + "font" font))))) (defun draw-block (jsonrpc view x y width height color) "fill the WIDTH by HEIGHT rectangle at pixel position X, Y in VIEW with COLOR. @@ -682,7 +691,7 @@ leaves the client to use its default background." "height" height "color" (and color (lem:color-to-hex-string color)))))) -(defmethod draw-object (jsonrpc (object display:text-object) x y view) +(defmethod draw-object (jsonrpc (object display:text-object) x y view row) (let* ((string (display:text-object-string object)) (attribute (display:text-object-attribute object)) (type (display:text-object-type object)) @@ -695,9 +704,10 @@ leaves the client to use its default background." y string attribute - :text-width width))) + :text-width width + :row row))) -(defmethod draw-object (jsonrpc (object display:icon-object) x y view) +(defmethod draw-object (jsonrpc (object display:icon-object) x y view row) (let* ((string (display:text-object-string object)) (attribute (display:text-object-attribute object)) (type (display:text-object-type object)) @@ -711,10 +721,11 @@ leaves the client to use its default background." string attribute :text-width width + :row row :font (lem:icon-value (char-code (char string 0)) :font)))) -(defmethod draw-object (jsonrpc (object display:eol-cursor-object) x y view) +(defmethod draw-object (jsonrpc (object display:eol-cursor-object) x y view row) (lem-core:set-last-print-cursor (view-window view) x y) (let ((attr (lem:make-attribute :background @@ -722,7 +733,7 @@ leaves the client to use its default background." (lem-core:set-cursor-attribute attr) (put jsonrpc view x y " " attr :text-width (jsonrpc-cell-width jsonrpc)))) -(defmethod draw-object (jsonrpc (object display:line-end-object) x y view) +(defmethod draw-object (jsonrpc (object display:line-end-object) x y view row) (let ((string (display:text-object-string object)) (attribute (display:text-object-attribute object)) (width (lem-if:object-width jsonrpc object))) @@ -733,7 +744,8 @@ leaves the client to use its default background." y string attribute - :text-width width))) + :text-width width + :row row))) (defun image-object-url (object) "return a URL the JS client can load for OBJECT's image, or NIL. @@ -749,7 +761,32 @@ a string already carrying a data:/https: URL is passed through unchanged." (format nil "/local~A" image))) (t nil)))) -(defmethod draw-object (jsonrpc (object display:image-object) x y view) +(defun attribute-own-background (attribute) + "the background ATTRIBUTE asks for as a color, or NIL when it asks for none. +not `lem:attribute-background-with-reverse', which answers with the default background rather than NIL." + (alexandria:when-let ((background (if (lem:attribute-reverse attribute) + (lem:attribute-foreground attribute) + (lem:attribute-background attribute)))) + (typecase background + (lem:color background) + (string (lem:parse-color background))))) + +(defun row-text-top (jsonrpc row) + "the top of a line of text on ROW: its baseline less the font's ascent." + (- (display:row-baseline row) + (or (jsonrpc-cell-ascent jsonrpc) (jsonrpc-cell-height jsonrpc)))) + +(defmethod draw-object (jsonrpc (object display:image-object) x y view row) + (alexandria:when-let ((attribute (lem:ensure-attribute (display:image-object-attribute object) + nil))) + ;; the image carries the attribute of the text it replaced, so selecting the line reaches it too + (alexandria:when-let ((color (attribute-own-background attribute))) + (draw-block jsonrpc view x (display:row-top row) (lem-if:object-width jsonrpc object) + (display:row-height row) color)) + ;; the cursor can sit on an image. Y is the image's top, which can be far above the row's text, + ;; so report the text's top instead and the caret aligns with the text. + (when (lem-core:cursor-attribute-p attribute) + (lem-core:set-last-print-cursor (view-window view) x (row-text-top jsonrpc row)))) (let ((url (image-object-url object))) (when url (with-error-handler () @@ -793,7 +830,8 @@ text line's, so it goes as a `draw-block' rather than a put's background." (display:placement-object placement) (display:placement-x placement) (display:placement-top placement) - view))) + view + row))) (defmethod lem-if:render-row ((jsonrpc jsonrpc) view row) (with-error-handler () From a58735ac3d0a6d7974ccbe643ad8198f86aea1c4 Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Mon, 3 Aug 2026 23:20:15 +0300 Subject: [PATCH 23/26] let frontends report the font's em size --- frontends/sdl2/main.lisp | 7 +++++++ frontends/server/frontend/dist/assets/index.js | 2 +- frontends/server/frontend/editor.js | 2 ++ frontends/server/main.lisp | 11 +++++++++-- src/interface.lisp | 6 ++++++ src/internal-packages.lisp | 1 + 6 files changed, 26 insertions(+), 3 deletions(-) diff --git a/frontends/sdl2/main.lisp b/frontends/sdl2/main.lisp index d218d664c..5f46e8071 100644 --- a/frontends/sdl2/main.lisp +++ b/frontends/sdl2/main.lisp @@ -484,6 +484,13 @@ (display:display-char-height display) (display:display-font-ascent display)))) +(defmethod lem-if:font-em-pixels ((implementation sdl2)) + (display:with-display (display) + ;; a high dpi display opens the font at a multiple of the configured size, and the cell + ;; metrics are measured from the font as opened, so this has to be that size and not the + ;; configured one. + (font-config-size (display:display-font-config display)))) + (defmethod lem-if:view-width ((implementation sdl2) view) (display:with-display (display) (* (display:display-char-width display) diff --git a/frontends/server/frontend/dist/assets/index.js b/frontends/server/frontend/dist/assets/index.js index 7cdb66a8b..1c50a8893 100644 --- a/frontends/server/frontend/dist/assets/index.js +++ b/frontends/server/frontend/dist/assets/index.js @@ -1 +1 @@ -var __defProp=Object.defineProperty,__commonJSMin=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),__exportAll=(e,t)=>{let n={};for(var r in e)__defProp(n,r,{get:e[r],enumerable:!0});return t||__defProp(n,Symbol.toStringTag,{value:`Module`}),n};(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var require_models=__commonJSMin((e=>{var t=e&&e.__extends||(function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};return function(t,n){if(typeof n!=`function`&&n!==null)throw TypeError(`Class extends value `+String(n)+` is not a constructor or null`);e(t,n);function r(){this.constructor=t}t.prototype=n===null?Object.create(n):(r.prototype=n.prototype,new r)}})();Object.defineProperty(e,"__esModule",{value:!0}),e.createJSONRPCNotification=e.createJSONRPCRequest=e.createJSONRPCSuccessResponse=e.createJSONRPCErrorResponse=e.JSONRPCErrorCode=e.JSONRPCErrorException=e.isJSONRPCResponses=e.isJSONRPCResponse=e.isJSONRPCRequests=e.isJSONRPCRequest=e.isJSONRPCID=e.JSONRPC=void 0,e.JSONRPC=`2.0`,e.isJSONRPCID=function(e){return typeof e==`string`||typeof e==`number`||e===null},e.isJSONRPCRequest=function(t){return t.jsonrpc===e.JSONRPC&&t.method!==void 0&&t.result===void 0&&t.error===void 0},e.isJSONRPCRequests=function(t){return Array.isArray(t)&&t.every(e.isJSONRPCRequest)},e.isJSONRPCResponse=function(t){return t.jsonrpc===e.JSONRPC&&t.id!==void 0&&(t.result!==void 0||t.error!==void 0)},e.isJSONRPCResponses=function(t){return Array.isArray(t)&&t.every(e.isJSONRPCResponse)};var n=function(e,t,n){var r={code:e,message:t};return n!=null&&(r.data=n),r};e.JSONRPCErrorException=function(e){t(r,e);function r(t,n,i){var a=e.call(this,t)||this;return Object.setPrototypeOf(a,r.prototype),a.code=n,a.data=i,a}return r.prototype.toObject=function(){return n(this.code,this.message,this.data)},r}(Error),(function(e){e[e.ParseError=-32700]=`ParseError`,e[e.InvalidRequest=-32600]=`InvalidRequest`,e[e.MethodNotFound=-32601]=`MethodNotFound`,e[e.InvalidParams=-32602]=`InvalidParams`,e[e.InternalError=-32603]=`InternalError`})(e.JSONRPCErrorCode||={}),e.createJSONRPCErrorResponse=function(t,r,i,a){return{jsonrpc:e.JSONRPC,id:t,error:n(r,i,a)}},e.createJSONRPCSuccessResponse=function(t,n){return{jsonrpc:e.JSONRPC,id:t,result:n??null}},e.createJSONRPCRequest=function(t,n,r){return{jsonrpc:e.JSONRPC,id:t,method:n,params:r}},e.createJSONRPCNotification=function(t,n){return{jsonrpc:e.JSONRPC,method:t,params:n}}})),require_internal=__commonJSMin((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.DefaultErrorCode=void 0,e.DefaultErrorCode=0})),require_client=__commonJSMin((e=>{var t=e&&e.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||=Promise)(function(n,a){function o(e){try{c(r.next(e))}catch(e){a(e)}}function s(e){try{c(r.throw(e))}catch(e){a(e)}}function c(e){e.done?n(e.value):i(e.value).then(o,s)}c((r=r.apply(e,t||[])).next())})},n=e&&e.__generator||function(e,t){var n={label:0,sent:function(){if(a[0]&1)throw a[1];return a[1]},trys:[],ops:[]},r,i,a,o;return o={next:s(0),throw:s(1),return:s(2)},typeof Symbol==`function`&&(o[Symbol.iterator]=function(){return this}),o;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw TypeError(`Generator is already executing.`);for(;o&&(o=0,s[0]&&(n=0)),n;)try{if(r=1,i&&(a=s[0]&2?i.return:s[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,s[1])).done)return a;switch(i=0,a&&(s=[s[0]&2,a.value]),s[0]){case 0:case 1:a=s;break;case 4:return n.label++,{value:s[1],done:!1};case 5:n.label++,i=s[1],s=[0];continue;case 7:s=n.ops.pop(),n.trys.pop();continue;default:if((a=n.trys,!(a=a.length>0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{Object.defineProperty(e,"__esModule",{value:!0})})),require_server=__commonJSMin((e=>{var t=e&&e.__assign||function(){return t=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{var t=e&&e.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||=Promise)(function(n,a){function o(e){try{c(r.next(e))}catch(e){a(e)}}function s(e){try{c(r.throw(e))}catch(e){a(e)}}function c(e){e.done?n(e.value):i(e.value).then(o,s)}c((r=r.apply(e,t||[])).next())})},n=e&&e.__generator||function(e,t){var n={label:0,sent:function(){if(a[0]&1)throw a[1];return a[1]},trys:[],ops:[]},r,i,a,o;return o={next:s(0),throw:s(1),return:s(2)},typeof Symbol==`function`&&(o[Symbol.iterator]=function(){return this}),o;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw TypeError(`Generator is already executing.`);for(;o&&(o=0,s[0]&&(n=0)),n;)try{if(r=1,i&&(a=s[0]&2?i.return:s[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,s[1])).done)return a;switch(i=0,a&&(s=[s[0]&2,a.value]),s[0]){case 0:case 1:a=s;break;case 4:return n.label++,{value:s[1],done:!1};case 5:n.label++,i=s[1],s=[0];continue;case 7:s=n.ops.pop(),n.trys.pop();continue;default:if((a=n.trys,!(a=a.length>0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{var t=e&&e.__createBinding||(Object.create?(function(e,t,n,r){r===void 0&&(r=n);var i=Object.getOwnPropertyDescriptor(t,n);(!i||(`get`in i?!t.__esModule:i.writable||i.configurable))&&(i={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,r,i)}):(function(e,t,n,r){r===void 0&&(r=n),e[r]=t[n]})),n=e&&e.__exportStar||function(e,n){for(var r in e)r!=="default"&&!Object.prototype.hasOwnProperty.call(n,r)&&t(n,e,r)};Object.defineProperty(e,"__esModule",{value:!0}),n(require_client(),e),n(require_interfaces(),e),n(require_models(),e),n(require_server(),e),n(require_server_and_client(),e)})),import_dist=require_dist(),JSONRPC=class{constructor(e,{onConnected:t,onClosed:n}){this.url=e,this.onConnected=t,this.onClosed=n,this.messageQueue=[],this.serverAndClient=null,this.connect(),this.connectionEstablished=!1,this.timerId=null,this.closed=!1}close(){this.timerId&&clearTimeout(this.timerId),this.webSocket.close(),this.closed=!0}on(e,t){this.serverAndClient.addMethod(e,t)}async requestInternal(e,t,n){let r=await this.serverAndClient.request(e,t);n&&n(r)}requestMessageQueue(){this.messageQueue.forEach(e=>{let[t,n,r]=e;this.requestInternal(t,n,r)}),this.messageQueue=[]}request(e,t,n){this.webSocket.readyState===WebSocket.OPEN?this.requestInternal(e,t,n):this.messageQueue.push([e,t,n])}notify(e,t){switch(this.webSocket.readyState){case WebSocket.OPEN:this.serverAndClient.notify(e,t);break;case WebSocket.CLOSED:break}}connect(e){this.closed||(console.log(`connect`,this.url),this.webSocket=new WebSocket(this.url),this.serverAndClient||=new import_dist.JSONRPCServerAndClient(new import_dist.JSONRPCServer,new import_dist.JSONRPCClient(e=>{try{return this.webSocket.send(JSON.stringify(e)),Promise.resolve()}catch(e){return Promise.reject(e)}})),this.webSocket.onmessage=e=>{this.serverAndClient.receiveAndSend(JSON.parse(e.data.toString()))},this.webSocket.onopen=()=>{console.log(`WebSocket connection established`),this.connectionEstablished=!0,this.onConnected&&this.onConnected(),this.requestMessageQueue()},this.webSocket.onclose=e=>{console.error(`WebScoket closed`,e),this.serverAndClient.rejectAllPendingRequests(`Connection is closed (${e.reason}).`),this.connectionEstablished&&this.onClosed(),this.timerId=setTimeout(()=>{this.connect()},3e3)},this.webSocket.onerror=e=>{console.error(`WebSocket error:`,e),this.webSocket.close()})}},keyevent_exports=__exportAll({convertKeyEvent:()=>convertKeyEvent}),modifierKeys=[`Shift`,`Control`,`Alt`,`Meta`,`CapsLock`],convertKeyTable={Enter:`Return`,ArrowRight:`Right`,ArrowLeft:`Left`,ArrowUp:`Up`,ArrowDown:`Down`,"¡":`1`,"™":`2`,"£":`3`,"¢":`4`,"∞":`5`,"§":`6`,"¶":`7`,"•":`8`,ª:`9`,º:`0`,"–":`-`,"≠":`=`,"“":`[`,"‘":`]`,"«":`\\`,"…":`;`,æ:`'`,"≤":`,`,"≥":`.`,"÷":`/`,"⁄":`!`,"€":`@`,"‹":`#`,"›":`$`,fi:`%`,fl:`^`,"‡":`&`,"°":`*`,"·":`(`,"‚":`)`,"—":`_`,"±":`+`,"”":`{`,"’":`}`,"»":`|`,Ú:`:`,Æ:`"`,"¯":`<`,"˘":`>`,"¿":`?`,œ:`q`,"∑":`w`,"´":`e`,"®":`r`,"†":`t`,"¥":`y`,"¨":`u`,ˆ:`i`,ø:`o`,π:`p`,å:`a`,ß:`s`,"∂":`d`,ƒ:`f`,"©":`g`,"˙":`h`,"∆":`j`,"˚":`k`,"¬":`l`,Ω:`z`,"≈":`x`,ç:`c`,"√":`v`,"∫":`b`,"˜":`n`,µ:`m`,Œ:`Q`,"„":`W`,"´":`E`,"‰":`R`,ˇ:`T`,Á:`Y`,"¨":`U`,ˆ:`I`,Ø:`O`,"∏":`P`,Å:`A`,Í:`S`,Î:`D`,Ï:`F`,"˝":`G`,Ó:`H`,Ô:`J`,"":`K`,Ò:`L`,"¸":`Z`,"˛":`X`,Ç:`C`,"◊":`V`,ı:`B`,"˜":`N`,Â:`M`};function getKey(e){return e.altKey?convertKeyTable[e.key]||(e.code.startsWith(`Key`)?e.code[3].toLowerCase():null)||e.key:convertKeyTable[e.key]||e.key}function convertKeyEvent(e){return modifierKeys.indexOf(e.key)===-1?{key:getKey(e),ctrl:e.ctrlKey,meta:e.altKey,super:e.metaKey,shift:e.shiftKey}:null}var lib_exports=__exportAll({computeWidth:()=>computeWidth,eawVersion:()=>version,getEAW:()=>getEAW}),defs=[[0,31,`N`],[32,126,`Na`],[127,160,`N`],[161,161,`A`],[162,163,`Na`],[164,164,`A`],[165,166,`Na`],[167,168,`A`],[169,169,`N`],[170,170,`A`],[171,171,`N`],[172,172,`Na`],[173,174,`A`],[175,175,`Na`],[176,180,`A`],[181,181,`N`],[182,186,`A`],[187,187,`N`],[188,191,`A`],[192,197,`N`],[198,198,`A`],[199,207,`N`],[208,208,`A`],[209,214,`N`],[215,216,`A`],[217,221,`N`],[222,225,`A`],[226,229,`N`],[230,230,`A`],[231,231,`N`],[232,234,`A`],[235,235,`N`],[236,237,`A`],[238,239,`N`],[240,240,`A`],[241,241,`N`],[242,243,`A`],[244,246,`N`],[247,250,`A`],[251,251,`N`],[252,252,`A`],[253,253,`N`],[254,254,`A`],[255,256,`N`],[257,257,`A`],[258,272,`N`],[273,273,`A`],[274,274,`N`],[275,275,`A`],[276,282,`N`],[283,283,`A`],[284,293,`N`],[294,295,`A`],[296,298,`N`],[299,299,`A`],[300,304,`N`],[305,307,`A`],[308,311,`N`],[312,312,`A`],[313,318,`N`],[319,322,`A`],[323,323,`N`],[324,324,`A`],[325,327,`N`],[328,331,`A`],[332,332,`N`],[333,333,`A`],[334,337,`N`],[338,339,`A`],[340,357,`N`],[358,359,`A`],[360,362,`N`],[363,363,`A`],[364,461,`N`],[462,462,`A`],[463,463,`N`],[464,464,`A`],[465,465,`N`],[466,466,`A`],[467,467,`N`],[468,468,`A`],[469,469,`N`],[470,470,`A`],[471,471,`N`],[472,472,`A`],[473,473,`N`],[474,474,`A`],[475,475,`N`],[476,476,`A`],[477,592,`N`],[593,593,`A`],[594,608,`N`],[609,609,`A`],[610,707,`N`],[708,708,`A`],[709,710,`N`],[711,711,`A`],[712,712,`N`],[713,715,`A`],[716,716,`N`],[717,717,`A`],[718,719,`N`],[720,720,`A`],[721,727,`N`],[728,731,`A`],[732,732,`N`],[733,733,`A`],[734,734,`N`],[735,735,`A`],[736,767,`N`],[768,879,`A`],[880,912,`N`],[913,929,`A`],[930,930,`N`],[931,937,`A`],[938,944,`N`],[945,961,`A`],[962,962,`N`],[963,969,`A`],[970,1024,`N`],[1025,1025,`A`],[1026,1039,`N`],[1040,1103,`A`],[1104,1104,`N`],[1105,1105,`A`],[1106,4351,`N`],[4352,4447,`W`],[4448,8207,`N`],[8208,8208,`A`],[8209,8210,`N`],[8211,8214,`A`],[8215,8215,`N`],[8216,8217,`A`],[8218,8219,`N`],[8220,8221,`A`],[8222,8223,`N`],[8224,8226,`A`],[8227,8227,`N`],[8228,8231,`A`],[8232,8239,`N`],[8240,8240,`A`],[8241,8241,`N`],[8242,8243,`A`],[8244,8244,`N`],[8245,8245,`A`],[8246,8250,`N`],[8251,8251,`A`],[8252,8253,`N`],[8254,8254,`A`],[8255,8307,`N`],[8308,8308,`A`],[8309,8318,`N`],[8319,8319,`A`],[8320,8320,`N`],[8321,8324,`A`],[8325,8360,`N`],[8361,8361,`H`],[8362,8363,`N`],[8364,8364,`A`],[8365,8450,`N`],[8451,8451,`A`],[8452,8452,`N`],[8453,8453,`A`],[8454,8456,`N`],[8457,8457,`A`],[8458,8466,`N`],[8467,8467,`A`],[8468,8469,`N`],[8470,8470,`A`],[8471,8480,`N`],[8481,8482,`A`],[8483,8485,`N`],[8486,8486,`A`],[8487,8490,`N`],[8491,8491,`A`],[8492,8530,`N`],[8531,8532,`A`],[8533,8538,`N`],[8539,8542,`A`],[8543,8543,`N`],[8544,8555,`A`],[8556,8559,`N`],[8560,8569,`A`],[8570,8584,`N`],[8585,8585,`A`],[8586,8591,`N`],[8592,8601,`A`],[8602,8631,`N`],[8632,8633,`A`],[8634,8657,`N`],[8658,8658,`A`],[8659,8659,`N`],[8660,8660,`A`],[8661,8678,`N`],[8679,8679,`A`],[8680,8703,`N`],[8704,8704,`A`],[8705,8705,`N`],[8706,8707,`A`],[8708,8710,`N`],[8711,8712,`A`],[8713,8714,`N`],[8715,8715,`A`],[8716,8718,`N`],[8719,8719,`A`],[8720,8720,`N`],[8721,8721,`A`],[8722,8724,`N`],[8725,8725,`A`],[8726,8729,`N`],[8730,8730,`A`],[8731,8732,`N`],[8733,8736,`A`],[8737,8738,`N`],[8739,8739,`A`],[8740,8740,`N`],[8741,8741,`A`],[8742,8742,`N`],[8743,8748,`A`],[8749,8749,`N`],[8750,8750,`A`],[8751,8755,`N`],[8756,8759,`A`],[8760,8763,`N`],[8764,8765,`A`],[8766,8775,`N`],[8776,8776,`A`],[8777,8779,`N`],[8780,8780,`A`],[8781,8785,`N`],[8786,8786,`A`],[8787,8799,`N`],[8800,8801,`A`],[8802,8803,`N`],[8804,8807,`A`],[8808,8809,`N`],[8810,8811,`A`],[8812,8813,`N`],[8814,8815,`A`],[8816,8833,`N`],[8834,8835,`A`],[8836,8837,`N`],[8838,8839,`A`],[8840,8852,`N`],[8853,8853,`A`],[8854,8856,`N`],[8857,8857,`A`],[8858,8868,`N`],[8869,8869,`A`],[8870,8894,`N`],[8895,8895,`A`],[8896,8977,`N`],[8978,8978,`A`],[8979,8985,`N`],[8986,8987,`W`],[8988,9e3,`N`],[9001,9002,`W`],[9003,9192,`N`],[9193,9196,`W`],[9197,9199,`N`],[9200,9200,`W`],[9201,9202,`N`],[9203,9203,`W`],[9204,9311,`N`],[9312,9449,`A`],[9450,9450,`N`],[9451,9547,`A`],[9548,9551,`N`],[9552,9587,`A`],[9588,9599,`N`],[9600,9615,`A`],[9616,9617,`N`],[9618,9621,`A`],[9622,9631,`N`],[9632,9633,`A`],[9634,9634,`N`],[9635,9641,`A`],[9642,9649,`N`],[9650,9651,`A`],[9652,9653,`N`],[9654,9655,`A`],[9656,9659,`N`],[9660,9661,`A`],[9662,9663,`N`],[9664,9665,`A`],[9666,9669,`N`],[9670,9672,`A`],[9673,9674,`N`],[9675,9675,`A`],[9676,9677,`N`],[9678,9681,`A`],[9682,9697,`N`],[9698,9701,`A`],[9702,9710,`N`],[9711,9711,`A`],[9712,9724,`N`],[9725,9726,`W`],[9727,9732,`N`],[9733,9734,`A`],[9735,9736,`N`],[9737,9737,`A`],[9738,9741,`N`],[9742,9743,`A`],[9744,9747,`N`],[9748,9749,`W`],[9750,9755,`N`],[9756,9756,`A`],[9757,9757,`N`],[9758,9758,`A`],[9759,9791,`N`],[9792,9792,`A`],[9793,9793,`N`],[9794,9794,`A`],[9795,9799,`N`],[9800,9811,`W`],[9812,9823,`N`],[9824,9825,`A`],[9826,9826,`N`],[9827,9829,`A`],[9830,9830,`N`],[9831,9834,`A`],[9835,9835,`N`],[9836,9837,`A`],[9838,9838,`N`],[9839,9839,`A`],[9840,9854,`N`],[9855,9855,`W`],[9856,9874,`N`],[9875,9875,`W`],[9876,9885,`N`],[9886,9887,`A`],[9888,9888,`N`],[9889,9889,`W`],[9890,9897,`N`],[9898,9899,`W`],[9900,9916,`N`],[9917,9918,`W`],[9919,9919,`A`],[9920,9923,`N`],[9924,9925,`W`],[9926,9933,`A`],[9934,9934,`W`],[9935,9939,`A`],[9940,9940,`W`],[9941,9953,`A`],[9954,9954,`N`],[9955,9955,`A`],[9956,9959,`N`],[9960,9961,`A`],[9962,9962,`W`],[9963,9969,`A`],[9970,9971,`W`],[9972,9972,`A`],[9973,9973,`W`],[9974,9977,`A`],[9978,9978,`W`],[9979,9980,`A`],[9981,9981,`W`],[9982,9983,`A`],[9984,9988,`N`],[9989,9989,`W`],[9990,9993,`N`],[9994,9995,`W`],[9996,10023,`N`],[10024,10024,`W`],[10025,10044,`N`],[10045,10045,`A`],[10046,10059,`N`],[10060,10060,`W`],[10061,10061,`N`],[10062,10062,`W`],[10063,10066,`N`],[10067,10069,`W`],[10070,10070,`N`],[10071,10071,`W`],[10072,10101,`N`],[10102,10111,`A`],[10112,10132,`N`],[10133,10135,`W`],[10136,10159,`N`],[10160,10160,`W`],[10161,10174,`N`],[10175,10175,`W`],[10176,10213,`N`],[10214,10221,`Na`],[10222,10628,`N`],[10629,10630,`Na`],[10631,11034,`N`],[11035,11036,`W`],[11037,11087,`N`],[11088,11088,`W`],[11089,11092,`N`],[11093,11093,`W`],[11094,11097,`A`],[11098,11903,`N`],[11904,11929,`W`],[11930,11930,`N`],[11931,12019,`W`],[12020,12031,`N`],[12032,12245,`W`],[12246,12271,`N`],[12272,12287,`W`],[12288,12288,`F`],[12289,12350,`W`],[12351,12352,`N`],[12353,12438,`W`],[12439,12440,`N`],[12441,12543,`W`],[12544,12548,`N`],[12549,12591,`W`],[12592,12592,`N`],[12593,12686,`W`],[12687,12687,`N`],[12688,12771,`W`],[12772,12782,`N`],[12783,12830,`W`],[12831,12831,`N`],[12832,12871,`W`],[12872,12879,`A`],[12880,19903,`W`],[19904,19967,`N`],[19968,42124,`W`],[42125,42127,`N`],[42128,42182,`W`],[42183,43359,`N`],[43360,43388,`W`],[43389,44031,`N`],[44032,55203,`W`],[55204,57343,`N`],[57344,63743,`A`],[63744,64255,`W`],[64256,65023,`N`],[65024,65039,`A`],[65040,65049,`W`],[65050,65071,`N`],[65072,65106,`W`],[65107,65107,`N`],[65108,65126,`W`],[65127,65127,`N`],[65128,65131,`W`],[65132,65280,`N`],[65281,65376,`F`],[65377,65470,`H`],[65471,65473,`N`],[65474,65479,`H`],[65480,65481,`N`],[65482,65487,`H`],[65488,65489,`N`],[65490,65495,`H`],[65496,65497,`N`],[65498,65500,`H`],[65501,65503,`N`],[65504,65510,`F`],[65511,65511,`N`],[65512,65518,`H`],[65519,65532,`N`],[65533,65533,`A`],[65534,94175,`N`],[94176,94180,`W`],[94181,94191,`N`],[94192,94193,`W`],[94194,94207,`N`],[94208,100343,`W`],[100344,100351,`N`],[100352,101589,`W`],[101590,101631,`N`],[101632,101640,`W`],[101641,110575,`N`],[110576,110579,`W`],[110580,110580,`N`],[110581,110587,`W`],[110588,110588,`N`],[110589,110590,`W`],[110591,110591,`N`],[110592,110882,`W`],[110883,110897,`N`],[110898,110898,`W`],[110899,110927,`N`],[110928,110930,`W`],[110931,110932,`N`],[110933,110933,`W`],[110934,110947,`N`],[110948,110951,`W`],[110952,110959,`N`],[110960,111355,`W`],[111356,126979,`N`],[126980,126980,`W`],[126981,127182,`N`],[127183,127183,`W`],[127184,127231,`N`],[127232,127242,`A`],[127243,127247,`N`],[127248,127277,`A`],[127278,127279,`N`],[127280,127337,`A`],[127338,127343,`N`],[127344,127373,`A`],[127374,127374,`W`],[127375,127376,`A`],[127377,127386,`W`],[127387,127404,`A`],[127405,127487,`N`],[127488,127490,`W`],[127491,127503,`N`],[127504,127547,`W`],[127548,127551,`N`],[127552,127560,`W`],[127561,127567,`N`],[127568,127569,`W`],[127570,127583,`N`],[127584,127589,`W`],[127590,127743,`N`],[127744,127776,`W`],[127777,127788,`N`],[127789,127797,`W`],[127798,127798,`N`],[127799,127868,`W`],[127869,127869,`N`],[127870,127891,`W`],[127892,127903,`N`],[127904,127946,`W`],[127947,127950,`N`],[127951,127955,`W`],[127956,127967,`N`],[127968,127984,`W`],[127985,127987,`N`],[127988,127988,`W`],[127989,127991,`N`],[127992,128062,`W`],[128063,128063,`N`],[128064,128064,`W`],[128065,128065,`N`],[128066,128252,`W`],[128253,128254,`N`],[128255,128317,`W`],[128318,128330,`N`],[128331,128334,`W`],[128335,128335,`N`],[128336,128359,`W`],[128360,128377,`N`],[128378,128378,`W`],[128379,128404,`N`],[128405,128406,`W`],[128407,128419,`N`],[128420,128420,`W`],[128421,128506,`N`],[128507,128591,`W`],[128592,128639,`N`],[128640,128709,`W`],[128710,128715,`N`],[128716,128716,`W`],[128717,128719,`N`],[128720,128722,`W`],[128723,128724,`N`],[128725,128727,`W`],[128728,128731,`N`],[128732,128735,`W`],[128736,128746,`N`],[128747,128748,`W`],[128749,128755,`N`],[128756,128764,`W`],[128765,128991,`N`],[128992,129003,`W`],[129004,129007,`N`],[129008,129008,`W`],[129009,129291,`N`],[129292,129338,`W`],[129339,129339,`N`],[129340,129349,`W`],[129350,129350,`N`],[129351,129535,`W`],[129536,129647,`N`],[129648,129660,`W`],[129661,129663,`N`],[129664,129672,`W`],[129673,129679,`N`],[129680,129725,`W`],[129726,129726,`N`],[129727,129733,`W`],[129734,129741,`N`],[129742,129755,`W`],[129756,129759,`N`],[129760,129768,`W`],[129769,129775,`N`],[129776,129784,`W`],[129785,131071,`N`],[131072,196605,`W`],[196606,196607,`N`],[196608,262141,`W`],[262142,917759,`N`],[917760,917999,`A`],[918e3,983039,`N`],[983040,1048573,`A`],[1048574,1048575,`N`],[1048576,1114109,`A`],[1114110,1114111,`N`]],version=`15.1.0`;function getEAWOfCodePoint(e){let t=0,n=defs.length-1;for(;t!==n;){let r=t+(n-t>>1),[i,a,o]=defs[r];if(ea)t=r+1;else return o}return defs[t][2]}function getEAW(e,t=0){let n=e.codePointAt(t);if(n!==void 0)return getEAWOfCodePoint(n)}var defaultWidths={N:1,Na:1,W:2,F:2,H:1,A:1};function computeWidth(e,t){let n=0;for(let r of e){let e=getEAW(r);n+=t&&t[e]||defaultWidths[e]}return n}var textOffsetY=5,isSafari=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);function isWideChar(e){switch(getEAW(e)){case`F`:case`W`:return!0;default:return!1}}function isMacOS(){return window.navigator.userAgent.indexOf(`Mac OS X`)!==-1}function computeFontSize(e){let t=document.createElement(`canvas`).getContext(`2d`);t.font=e;let n=t.measureText(`W`);return[Math.floor(n.width),Math.round(n.fontBoundingBoxAscent+textOffsetY+(n.emHeightDescent||0)),Math.round(n.fontBoundingBoxAscent+textOffsetY)]}function drawBlock({ctx:e,x:t,y:n,width:r,height:i,style:a}){e.fillStyle=a,e.fillRect(t,n,r,i)}function drawText({ctx:e,x:t,y:n,text:r,font:i,style:a,option:o}){n+=Math.round(textOffsetY),e.fillStyle=a,e.font=i,e.textBaseline=`top`;for(let i of r)isWideChar(i)?(e.fillText(i,t,n,o.fontWidth*2),t+=o.fontWidth*2):(e.fillText(i,t,n,o.fontWidth),t+=o.fontWidth)}function drawHorizontalLine({ctx:e,x:t,y:n,width:r,style:i,lineWidth:a=1}){e.strokeStyle=i,e.lineWidth=a,e.setLineDash=[],e.beginPath(),e.moveTo(t,n),e.lineTo(t+r,n),e.stroke()}var Option=class{constructor({fontName:e,fontSize:t}){this.setFont(e,t),this.foreground=`#cccccc`,this.background=`#2d2d2d`}setFont(e,t){let n=t+`px `+e,[r,i,a]=computeFontSize(n);this.fontName=e,this.fontSize=t,this.fontWidth=r,this.fontHeight=i,this.fontAscent=a,this.font=n}};function getLemEditorElement(){return document.getElementById(`lem-editor`)}function normalizeWheelDelta(e,t,n,r){switch(n){case 0:return{dx:e/r,dy:t/r};case 2:return{dx:e*20,dy:t*20};default:return{dx:e,dy:t}}}function extractWholeLines(e,t){let n=Math.trunc(e),r=Math.trunc(t);return{scrollX:n,scrollY:r,remainderX:e-n,remainderY:t-r}}function cursorPosition(e,t){let[n,r]=t.getDisplayRectangle(),i=e.clientX-n,a=e.clientY-r;return{pixelX:i,pixelY:a,x:Math.floor(i/t.option.fontWidth),y:Math.floor(a/t.option.fontHeight)}}function makeWheelHandler(e){let t={x:0,y:0},n=!1,r={pixelX:0,pixelY:0,x:0,y:0};return i=>{i.preventDefault(),r=cursorPosition(i,e);let{dx:a,dy:o}=normalizeWheelDelta(i.deltaX,i.deltaY,i.deltaMode,e.option.fontHeight);t={x:t.x+a,y:t.y+o},n||(n=!0,requestAnimationFrame(()=>{n=!1;let{scrollX:i,scrollY:a,remainderX:o,remainderY:s}=extractWholeLines(t.x,t.y);t={x:o,y:s},(i!==0||a!==0)&&e.jsonrpc.notify(`input`,{kind:`wheel`,value:{...r,wheelX:-i,wheelY:-a}})}))}}function addMouseEventListeners({dom:e,editor:t,isDraggable:n,draggableStyle:r}){e.addEventListener(`contextmenu`,e=>{e.preventDefault()});let i=(e,n)=>{e.preventDefault();let[r,i]=t.getDisplayRectangle(),a=e.clientX-r,o=e.clientY-i,s=Math.floor(a/t.option.fontWidth),c=Math.floor(o/t.option.fontHeight);t.jsonrpc.notify(`input`,{kind:n,value:{x:s,y:c,pixelX:a,pixelY:o,button:e.button,clicks:e.detail}})};e.addEventListener(`mousedown`,e=>{n&&(document.body.style.cursor=r),t.focusHiddenInput(),i(e,`mousedown`)}),e.addEventListener(`mouseup`,e=>{n&&(document.body.style.cursor=`default`),i(e,`mouseup`)});let a=0;e.addEventListener(`mousemove`,e=>{e.preventDefault();let n=Date.now();if(n-a>50){a=n;let[r,i]=t.getDisplayRectangle(),o=e.clientX-r,s=e.clientY-i,c=Math.floor(o/t.option.fontWidth),l=Math.floor(s/t.option.fontHeight);t.jsonrpc.notify(`input`,{kind:`mousemove`,value:{x:c,y:l,pixelX:o,pixelY:s,button:e.buttons===0?null:e.buttons-1}})}}),n&&(e.addEventListener(`mouseover`,()=>{document.body.style.cursor=r}),e.addEventListener(`mouseout`,e=>{e.buttons!==1&&(document.body.style.cursor=`default`)})),e.addEventListener(`wheel`,makeWheelHandler(t))}var zIndexTable={"floating-window":200,modeline:100,"vertical-border":100,"horizontal-border":101};function zindex(e){return zIndexTable[e]||0}var borderOffsetX=5,borderOffsetY=10,BaseSurface=class{constructor({editor:e}){this.editor=e,this.mainDOM=null,this.wrapper=null}delete(){this.wrapper?getLemEditorElement().removeChild(this.wrapper):getLemEditorElement().removeChild(this.mainDOM)}setupDOM({dom:e,isFloating:t,border:n,cssClassName:r}){this.mainDOM=e,t&&n?(this.wrapper=document.createElement(`div`),r&&(this.wrapper.className=r),this.wrapper.style.position=`absolute`,this.wrapper.style.backgroundColor=this.editor.option.background,this.wrapper.style.zIndex=zindex(`floating-window`),this.wrapper.appendChild(e),getLemEditorElement().appendChild(this.wrapper)):(r&&(e.className=r),getLemEditorElement().appendChild(e))}move(e,t){let[n,r]=this.editor.getDisplayRectangle(),i=Math.floor(n+e),a=Math.floor(r+t);this.wrapper?(this.wrapper.style.left=i-borderOffsetX+`px`,this.wrapper.style.top=a-borderOffsetY+`px`,this.mainDOM.style.left=borderOffsetX+`px`,this.mainDOM.style.top=borderOffsetY+`px`):(this.mainDOM.style.left=i+`px`,this.mainDOM.style.top=a+`px`)}_resize(e,t){let n=window.devicePixelRatio||1;this.mainDOM.width=e*n,this.mainDOM.height=t*n,this.mainDOM.style.width=e+`px`,this.mainDOM.style.height=t+`px`,this.wrapper&&(this.wrapper.style.width=e+borderOffsetX*2+`px`,this.wrapper.style.height=t+borderOffsetY*2+`px`)}drawBlock(e,t,n,r,i){}drawText(e,t,n,r,i,a,o,s){}drawImage(e,t,n,r,i,a,o){}clearImages(e,t){}clearAllImages(){}touch(){}evalIn(code){return eval(code)}},CanvasSurface=class extends BaseSurface{constructor({editor:e,view:t,pixelX:n,pixelY:r,pixelWidth:i,pixelHeight:a,styles:o,isFloating:s,border:c,cssClassName:l}){super({editor:e});let u=this.setupCanvas(o);this.setupDOM({dom:u,isFloating:s,border:c,cssClassName:l}),this.move(n,r),this.resize(i,a),this.drawingQueue=[],addMouseEventListeners({dom:u,editor:e})}setupCanvas(e){let t=document.createElement(`canvas`);if(t.style.position=`absolute`,e)for(let n in e)t.style[n]=e[n];return t}resize(e,t){this._resize(e,t);let n=window.devicePixelRatio||1;this.mainDOM.getContext(`2d`).scale(n,n)}move(e,t){if(super.move(e,t),this.imageEls)for(let[,e]of this.imageEls)this.positionImage(e)}delete(){this.clearAllImages(),super.delete()}drawBlock(e,t,n,r,i){this.drawingQueue.push(function(a){drawBlock({ctx:a,x:e,y:t,width:n,height:r,style:i})})}drawText(e,t,n,r,i,a,o,s){let c=this.editor.option,l=o??t,u=s??c.fontHeight;this.drawingQueue.push(function(o){if(a=a?`${c.fontSize}px ${a}`:c.font,!i)drawBlock({ctx:o,x:e,y:l,width:r,height:u,style:c.background}),drawText({ctx:o,x:e,y:t,text:n,style:c.foreground,font:a,option:c});else{let{foreground:s,background:d,bold:f,reverse:p,underline:m,cursor:h}=i;if(s||=c.foreground,d||=c.background,p){let e=d;d=s,s=e}h&&(d=c.background),drawBlock({ctx:o,x:e,y:l,width:r,height:u,style:d}),drawText({ctx:o,x:e,y:t,text:n,style:s,font:f?`bold `+a:a,option:c}),m&&drawHorizontalLine({ctx:o,x:e,y:t+c.fontHeight-2,width:r,style:typeof m==`string`?m:s,lineWidth:2})}})}imageBaseLeft(){return parseFloat(this.mainDOM.style.left)||0}imageBaseTop(){return parseFloat(this.mainDOM.style.top)||0}drawImage(e,t,n,r,i,a,o){this.imageEls||=new Map;let s=e+`,`+t,c=this.imageEls.get(s);if(c&&c.url!==o&&(c.el.remove(),this.imageEls.delete(s),c=null),!c){let e=document.createElement(`img`);e.style.position=`absolute`,e.style.pointerEvents=`none`,e.style.zIndex=`1`,e.src=o,this.mainDOM.parentNode.appendChild(e),c={el:e,url:o},this.imageEls.set(s,c)}c.x=e,c.y=t,c.width=n,c.height=r,c.clipWidth=i,c.clipHeight=a,this.positionImage(c)}positionImage(e){e.el.style.left=this.imageBaseLeft()+e.x+`px`,e.el.style.top=this.imageBaseTop()+e.y+`px`,e.el.style.width=e.width+`px`,e.el.style.height=e.height+`px`;let t=e.clipWidth==null?0:Math.max(0,e.width-e.clipWidth),n=e.clipHeight==null?0:Math.max(0,e.height-e.clipHeight);e.el.style.clipPath=t>0||n>0?`inset(0px ${t}px ${n}px 0px)`:``}clearImages(e,t){if(this.imageEls)for(let[n,r]of this.imageEls){let i=r.y+(r.height||0);r.ye&&(r.el.remove(),this.imageEls.delete(n))}}clearAllImages(){if(this.imageEls){for(let[,e]of this.imageEls)e.el.remove();this.imageEls.clear()}}touch(){let e=this.mainDOM.getContext(`2d`);for(let t of this.drawingQueue)t(e);this.drawingQueue=[]}activate(){this.mainDOM.dataset.store=`active`}deactivate(){this.mainDOM.dataset.store=`inactive`}},HTMLSurface=class extends BaseSurface{constructor({editor:e,pixelX:t,pixelY:n,pixelWidth:r,pixelHeight:i,styles:a,option:o,isFloating:s,border:c,html:l}){super({editor:e});let u=document.createElement(`iframe`);this.setupDOM({dom:u,isFloating:s,border:c}),u.style.position=`absolute`,u.style.backgroundColor=o.background,u.setAttribute(`sandbox`,`allow-scripts allow-same-origin`),u.srcdoc=l,u.addEventListener(`load`,()=>{let e=u.contentWindow;e.invokeLem=(e,t)=>parent.postMessage({type:`invoke-lem`,method:e,args:t})}),this.iframe=u,this.move(t,n),this.resize(r,i)}resize(e,t){this._resize(e,t)}update(e){let t=this.iframe.contentWindow.scrollY;this.iframe.srcdoc=e,this.iframe.onload=()=>{this.iframe.onload=null,this.iframe.contentWindow.scrollTo(0,t)}}evalIn(e){return this.iframe.contentWindow.eval(e)}},VerticalBorder=class{constructor({x:e,y:t,height:n,option:r,editor:i}){this.option=r,this.editor=i,this.line=document.createElement(`div`),this.line.className=`lem-editor__vertical-border`,this.line.style.height=n+`px`,this.line.style.position=`absolute`,this.line.style.zIndex=zindex(`vertical-border`),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:i,isDraggable:!0,draggableStyle:`col-resize`})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.line.style.left=Math.floor(n+e-this.option.fontWidth/2)+`px`,this.line.style.top=r+t+`px`}resize(e){this.line.style.height=e+`px`}},HorizontalBorder=class{constructor({x:e,y:t,width:n,option:r,editor:i}){this.option=r,this.editor=i,this.line=document.createElement(`div`),this.line.className=`lem-editor__horizontal-border`,this.line.style.width=n+`px`,this.line.style.position=`absolute`,this.line.style.zIndex=zindex(`horizontal-border`),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:i,isDraggable:!0,draggableStyle:`row-resize`})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.line.style.left=n+e+`px`,this.line.style.top=Math.floor(r+t-4)+`px`}resize(e){this.line.style.width=e+`px`}},viewStyles={header:()=>{},tile:()=>{},floating:e=>({boxSizing:`border-box`,borderColor:e.foreground,backgroundColor:e.background})};function getViewStyle(e,t){return viewStyles[e](t)||{}}var View=class{constructor({id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,useModeline:l,kind:u,type:d,content:f,border:p,borderShape:m,option:h,editor:g}){switch(this.option=h,this.id=e,this.x=t,this.y=n,this.width=r,this.height=i,this.pixelX=a,this.pixelY=o,this.pixelWidth=s,this.pixelHeight=c,this.useModeline=l,this.kind=u,this.type=d,this.border=p,this.borderShape=m,this.editor=g,this.bottomBar=null,this.leftsideBar=null,u){case`tile`:this.mainSurface=this.makeSurface(d,f),this.leftSideBar=new VerticalBorder({x:a,y:o,height:c+(l?h.fontHeight:0),option:h,editor:g}),l||(this.bottomBar=new HorizontalBorder({x:a,y:o+c-h.fontHeight,width:s,option:h,editor:g}));break;case`header`:this.mainSurface=this.makeSurface(d,f);break;case`floating`:this.mainSurface=this.makeSurface(d,f),m===`left-border`&&(this.leftSideBar=new VerticalBorder({x:a,y:o,height:c,option:h,editor:g}));break}this.modelineSurface=l?this.makeModelineSurface():null}delete(){this.mainSurface.delete(),this.modelineSurface&&this.modelineSurface.delete(),this.leftSideBar&&this.leftSideBar.delete(),this.bottomBar&&this.bottomBar.delete()}move(e,t,n,r){this.x=e,this.y=t,this.pixelX=n,this.pixelY=r,this.mainSurface.move(n,r),this.modelineSurface&&this.modelineSurface.move(n,r+this.pixelHeight),this.leftSideBar&&this.leftSideBar.move(n,r),this.bottomBar&&this.bottomBar.move(n,r+this.pixelHeight)}resize(e,t,n,r){this.width=e,this.height=t,this.pixelWidth=n,this.pixelHeight=r,this.mainSurface.resize(n,r),this.modelineSurface&&(this.modelineSurface.move(this.pixelX,this.pixelY+r),this.modelineSurface.resize(n,this.option.fontHeight)),this.leftSideBar&&this.leftSideBar.resize(r+(this.modelineSurface?this.option.fontHeight:0)),this.bottomBar&&this.bottomBar.resize(n)}clear(){this.mainSurface.drawBlock(0,0,this.pixelWidth,this.pixelHeight,this.option.background),this.mainSurface.clearImages(0,this.pixelHeight)}clearEol(e,t,n){n??=this.option.fontHeight,this.mainSurface.drawBlock(e,t,this.pixelWidth-e,n,this.option.background),this.mainSurface.clearImages(t,t+n)}clearEob(e,t){this.mainSurface.drawBlock(e,t,this.pixelWidth,this.pixelHeight-t,this.option.background),this.mainSurface.clearImages(t,this.pixelHeight)}print(e,t,n,r,i,a,o,s){this.mainSurface.drawText(e,t,n,r,i,a,o,s)}drawBlock(e,t,n,r,i){this.mainSurface.drawBlock(e,t,n,r,i||this.option.background)}drawBlockOnModeline(e,t,n,r,i){this.modelineSurface&&this.modelineSurface.drawBlock(e,t,n,r,i||this.option.background)}printImage(e,t,n,r,i,a,o){this.mainSurface.drawImage(e,t,n,r,i,a,o)}printToModeline(e,t,n,r,i,a,o){this.modelineSurface&&this.modelineSurface.drawText(e,t,n,r,i,null,a,o)}touch(e){this.mainSurface.touch(),this.modelineSurface&&(this.modelineSurface.touch(),e?this.modelineSurface.activate():this.modelineSurface.deactivate())}makeSurface(e,t){switch(e){case`html`:return this.makeHTMLSurface(t);case`editor`:return this.makeEditorSurface();default:console.error(`unknown type: ${e}`)}}makeHTMLSurface(e){return new HTMLSurface({editor:this.editor,pixelX:this.pixelX,pixelY:this.pixelY,pixelWidth:this.pixelWidth,pixelHeight:this.pixelHeight,styles:getViewStyle(this.kind,this.option),option:this.option,isFloating:this.kind===`floating`,border:this.border,html:e})}makeEditorSurface(){let e=this.borderShape===`left-border`?0:this.border,t=this.kind===`floating`;return new CanvasSurface({option:this.editor.option,pixelX:this.pixelX,pixelY:this.pixelY,pixelWidth:this.pixelWidth,pixelHeight:this.pixelHeight,styles:getViewStyle(this.kind,this.option),editor:this.editor,border:e,isFloating:t,view:this,cssClassName:t&&e?`lem-editor__floating-window--bordered`:null})}makeModelineSurface(){let e=new CanvasSurface({option:this.editor.option,pixelX:this.pixelX,pixelY:this.pixelY+this.pixelHeight,pixelWidth:this.pixelWidth,pixelHeight:this.option.fontHeight,editor:this.editor,view:this,styles:{zIndex:zindex(`modeline`)},cssClassName:`lem-editor__mode-line`});return addMouseEventListeners({dom:e.mainDOM,editor:this.editor,isDraggable:!0,draggableStyle:`row-resize`}),e}changeToHTMLContent(e){this.mainSurface.constructor.name===`HTMLSurface`?this.mainSurface.update(e):(this.mainSurface.delete(),this.mainSurface=this.makeHTMLSurface(e))}changeToEditorContent(){this.mainSurface.delete(),this.mainSurface=this.makeEditorSurface()}evalIn(e){return this.mainSurface.evalIn(e)}};function isPasteKeyEvent(e){return isMacOS()?e.metaKey&&e.key===`v`:e.ctrlKey&&e.shiftKey&&e.key===`V`}var Input=class{constructor(e){let t=e.option;this.editor=e,this.composition=!1,this.ignoreKeydownAfterCompositionend=!1,this.span=document.createElement(`span`),this.span.style.color=t.foreground,this.span.style.backgroundColor=t.background,this.span.style.position=`absolute`,this.span.style.zIndex=1e6,this.span.style.top=`0`,this.span.style.left=`0`,this.span.style.font=t.font,this.input=document.createElement(`input`),this.input.style.backgroundColor=`transparent`,this.input.style.color=`transparent`,this.input.style.width=`0`,this.input.style.padding=`0`,this.input.style.margin=`0`,this.input.style.border=`none`,this.input.style.position=`absolute`,this.input.style.zIndex=`-10`,this.input.style.top=`0`,this.input.style.left=`0`,this.input.style.font=t.font,this.input.addEventListener(`blur`,e=>{this.input.focus()}),this.input.addEventListener(`input`,e=>{this.composition===!1&&(this.input.value=``,this.span.innerHTML=``,this.input.style.width=`0`,isMacOS()||this.editor.emitInputString(e.data))}),this.input.addEventListener(`paste`,async e=>{e.preventDefault();let t=e.clipboardData||window.Clipboard.data,n=t?.getData(`text`)??t?.getData(`text/plain`);if(n&&n.length>0){this.editor.emitInputString(n);return}try{if(navigator.clipboard?.readText){let e=await navigator.clipboard.readText();if(e&&e.length>0){this.editor.emitInputString(e);return}}}catch(e){console.warn(`clipboard.readText() failed:`,e)}alert(`Paste failed (permission/environment restriction`)}),this.input.addEventListener(`keydown`,e=>{if(!isPasteKeyEvent(e)&&!(e.isComposing||this.composition)&&e.key!==`Process`){if(this.ignoreKeydownAfterCompositionend&&(isSafari||isMacOS())){e.preventDefault(),this.ignoreKeydownAfterCompositionend=!1;return}if(!(!isMacOS()&&!e.ctrlKey&&!e.altKey&&e.key.length===1)&&(e.preventDefault(),e.isComposing!==!0&&e.code!==``))return setTimeout(()=>{this.composition||(this.editor.emitInput(e),this.input.value=``)},0),!1}}),this.input.addEventListener(`compositionstart`,e=>{this.composition=!0,this.span.innerHTML=this.input.value,this.input.style.width=this.span.offsetWidth+`px`}),this.input.addEventListener(`compositionupdate`,e=>{this.span.innerHTML=e.data,this.input.style.width=this.span.offsetWidth+`px`}),this.input.addEventListener(`compositionend`,e=>{this.composition=!1,this.editor.emitInputString(this.input.value),this.input.value=``,this.span.innerHTML=this.input.value,this.input.style.width=`0`,this.ignoreKeydownAfterCompositionend=!0}),document.body.appendChild(this.input),document.body.appendChild(this.span),this.input.focus()}finalize(){document.body.removeChild(this.input),document.body.removeChild(this.span)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.span.style.top=r+t+`px`,this.span.style.left=n+e+`px`,this.input.style.top=this.span.offsetTop+`px`,this.input.style.left=this.span.offsetLeft+`px`}updateForeground(e){this.span.style.color=e}updateBackground(e){this.span.style.backgroundColor=e}},MessageTable=class{constructor(){this.map=new Map}register(e,t){for(let n in t){let r=t[n];this.map.set(n,r),e.on(n,r)}}get(e){return this.map.get(e)}};function getDisplayRectangleDefault(){return[0,0,window.innerWidth,window.innerHeight]}var Editor=class{constructor({getDisplayRectangle:e=getDisplayRectangleDefault,fontName:t,fontSize:n,url:r,onExit:i,onClosed:a}){this.getDisplayRectangle=e,this.option=new Option({fontName:t,fontSize:n}),this.onExit=i,this.input=new Input(this),this.cursors=new Map,this.cursorOverlay=document.createElement(`div`),this.cursorOverlay.className=`lem-cursor`,this.cursorOverlay.style.width=this.option.fontWidth+`px`,this.cursorOverlay.style.height=this.option.fontHeight+`px`,this.cursorOverlay.style.backgroundColor=`#ffffff`,this.cursorType=`box`,this.viewMap=new Map,this.jsonrpc=new JSONRPC(r,{onClosed:()=>{a()}}),this.messageTable=new MessageTable,this.messageTable.register(this.jsonrpc,{"update-foreground":this.updateForeground.bind(this),"update-background":this.updateBackground.bind(this),"make-view":this.makeView.bind(this),"delete-view":this.deleteView.bind(this),"resize-view":this.resize.bind(this),"move-view":this.move.bind(this),"redraw-view-after":this.redrawViewAfter.bind(this),clear:this.clear.bind(this),"clear-eol":this.clearEol.bind(this),"clear-eob":this.clearEob.bind(this),put:this.put.bind(this),"put-image":this.putImage.bind(this),"modeline-put":this.modelinePut.bind(this),"draw-block":this.drawBlock.bind(this),"modeline-draw-block":this.modelineDrawBlock.bind(this),"update-display":this.updateDisplay.bind(this),"move-cursor":this.moveCursor.bind(this),"change-view":this.changeView.bind(this),"resize-display":this.resizeDisplay.bind(this),bulk:this.bulk.bind(this),exit:this.exitEditor.bind(this),"get-clipboard-text":this.getClipboardText.bind(this),"set-clipboard-text":this.setClipboardText.bind(this),"js-eval":this.jsEval.bind(this),"set-font":this.setFont.bind(this),"get-font":this.getFont.bind(this),"get-display-size":this.getDisplaySize.bind(this),"load-css":this.loadCSS.bind(this),"update-cursor-shape":this.updateCursorShape.bind(this)}),this.login(),this.boundedHandleResize=this.handleResize.bind(this),this.focusHiddenInput=this.focusHiddenInput.bind(this)}init(){window.addEventListener(`resize`,this.boundedHandleResize),document.getElementsByTagName(`html`)[0].style[`background-color`]=`#333`,getLemEditorElement().appendChild(this.cursorOverlay)}finalize(){window.removeEventListener(`resize`,this.boundedHandleResize),this.input.finalize(),this.cursorOverlay.parentNode&&this.cursorOverlay.parentNode.removeChild(this.cursorOverlay)}closeConnection(){this.jsonrpc.close()}emitInput(e){let t=convertKeyEvent(e);if(t){if(t.key===`]`&&t.ctrl&&!t.meta&&!t.super&&!t.shift){this.jsonrpc.notify(`input`,{kind:`abort`});return}t.key!==`Unidentified`&&this.jsonrpc.notify(`input`,{kind:`key`,value:t})}}emitInputString(e){e?this.jsonrpc.notify(`input`,{kind:`input-string`,value:e}):console.error(`unexpected argument`,e)}redrawParams(){return{size:this.getDisplaySize(),fontWidth:this.option.fontWidth,fontHeight:this.option.fontHeight,fontAscent:this.option.fontAscent}}handleResize(e){this.jsonrpc.notify(`redraw`,this.redrawParams())}focusHiddenInput(){let e=this.input?.input;if(e){try{window.focus()}catch{}requestAnimationFrame(()=>{setTimeout(()=>{e.focus({preventScroll:!0})},0)})}}sendNotification(e,t){this.jsonrpc.notify(e,t)}request(e,t,n){this.jsonrpc.request(e,t,n)}getDisplaySize(){let[e,t,n,r]=this.getDisplayRectangle();return{width:Math.floor(n/this.option.fontWidth),height:Math.floor(r/this.option.fontHeight)}}callMessage(e,t){this.messageTable.get(e)(t)}findViewById(e){return this.viewMap.get(e)}login(){this.jsonrpc.request(`login`,{size:this.getDisplaySize(),foreground:this.option.foreground,background:this.option.background,fontWidth:this.option.fontWidth,fontHeight:this.option.fontHeight,fontAscent:this.option.fontAscent},e=>{if(this.updateForeground(e.foreground),this.updateBackground(e.background),e.views)for(let t of e.views)this.makeView(t);this.jsonrpc.notify(`redraw`,this.redrawParams())})}updateForeground(e){e!=null&&(this.option.foreground=e,this.input.updateForeground(e))}updateBackground(e){if(e==null)return;this.option.background=e,this.input.updateBackground(e);let t=getLemEditorElement();t.style.backgroundColor=e}makeView({id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,use_modeline:l,kind:u,type:d,content:f,border:p,border_shape:m}){let h=new View({option:this.option,id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,useModeline:l,kind:u,type:d,content:f,border:p,borderShape:m,editor:this});this.viewMap.set(e,h)}deleteView({viewInfo:{id:e}}){this.findViewById(e).delete(),this.viewMap.delete(e)}resize({viewInfo:{id:e},width:t,height:n,pixelWidth:r,pixelHeight:i}){let a=this.findViewById(e);a?a.resize(t,n,r,i):console.warn(`resize: view not found for id ${e}`)}move({viewInfo:{id:e},x:t,y:n,pixelX:r,pixelY:i}){let a=this.findViewById(e);a?a.move(t,n,r,i):console.warn(`move: view not found for id ${e}`)}redrawViewAfter({viewInfo:{id:e},isActive:t}){this.findViewById(e).touch(t)}clear({viewInfo:{id:e}}){this.findViewById(e).clear()}clearEol({viewInfo:{id:e},x:t,y:n,height:r}){this.findViewById(e).clearEol(t,n,r)}clearEob({viewInfo:{id:e},x:t,y:n}){this.findViewById(e).clearEob(t,n)}put({viewInfo:{id:e},x:t,y:n,text:r,textWidth:i,attribute:a,font:o,backgroundY:s,backgroundHeight:c}){this.findViewById(e).print(t,n,r,i,a,o,s,c)}drawBlock({viewInfo:{id:e},x:t,y:n,width:r,height:i,color:a}){this.findViewById(e).drawBlock(t,n,r,i,a)}modelineDrawBlock({viewInfo:{id:e},x:t,y:n,width:r,height:i,color:a}){this.findViewById(e).drawBlockOnModeline(t,n,r,i,a)}putImage({viewInfo:{id:e},x:t,y:n,pixelWidth:r,pixelHeight:i,clipWidth:a,clipHeight:o,url:s}){this.findViewById(e).printImage(t,n,r,i,a,o,s)}modelinePut({viewInfo:{id:e},x:t,y:n,text:r,textWidth:i,attribute:a,backgroundY:o,backgroundHeight:s}){this.findViewById(e).printToModeline(t,n,r,i,a,o,s)}updateDisplay(){}moveCursor({viewInfo:{id:e},x:t,y:n,color:r,cursorText:i,cursorForeground:a}){let o=this.findViewById(e),[s,c]=this.getDisplayRectangle(),l=o.pixelX+t,u=o.pixelY+n;this.input.move(l,u);let d=r||this.option.foreground,f=a||this.option.background,p=this.cursorOverlay;switch(this.cursorType){case`bar`:p.style.left=s+l+`px`,p.style.top=c+u+`px`,p.style.width=`2px`,p.style.height=this.option.fontHeight+`px`,p.style.backgroundColor=d,p.textContent=``,p.style.color=``,p.style.font=``,p.style.paddingTop=``;break;case`underline`:p.style.left=s+l+`px`,p.style.top=c+u+this.option.fontHeight-2+`px`,p.style.width=this.option.fontWidth+`px`,p.style.height=`2px`,p.style.backgroundColor=d,p.textContent=``,p.style.color=``,p.style.font=``,p.style.paddingTop=``;break;default:p.style.left=s+l+`px`,p.style.top=c+u+`px`,p.style.width=this.option.fontWidth+`px`,p.style.height=this.option.fontHeight+`px`,p.style.backgroundColor=d,p.style.font=this.option.font,p.style.paddingTop=textOffsetY+`px`,p.textContent=i||``,p.style.color=f;break}p.style.animation=`none`,p.offsetHeight,p.style.animation=``}updateCursorShape({cursorType:e}){this.cursorType=e||`box`}changeView({viewInfo:{id:e},type:t,content:n}){let r=this.findViewById(e);switch(t){case`html`:r.changeToHTMLContent(n);break;case`editor`:r.changeToEditorContent();break}}resizeDisplay({width:e,height:t}){let n=getLemEditorElement();n.style.width=Math.floor(e*this.option.fontWidth)+`px`,n.style.height=Math.floor(t*this.option.fontHeight)+`px`}bulk(e){for(let{method:t,argument:n}of e)this.callMessage(t,n)}exitEditor(){this.onExit&&this.onExit()}getClipboardText(){navigator.clipboard?.readText().then(e=>{this.jsonrpc.notify(`got-clipboard-text`,{text:e})})}setClipboardText({text:e}){navigator.clipboard&&navigator.clipboard.writeText(e)}jsEval({viewInfo:{id:e},code:t}){let n=this.findViewById(e).evalIn(t);return n&&n.toString()}setFont({fontName:e,fontSize:t}){this.option.setFont(e||this.option.fontName,t||this.option.fontSize),this.cursorOverlay.style.width=this.option.fontWidth+`px`,this.cursorOverlay.style.height=this.option.fontHeight+`px`,this.jsonrpc.notify(`redraw`,this.redrawParams())}getFont(){return{name:this.option.fontName,size:this.option.fontSize}}loadCSS({content:e}){let t=document.createElement(`style`);t.textContent=e,document.head.appendChild(t)}notifyToServer(e,t){this.jsonrpc.notify(`invoke`,{method:e,args:t})}},canvas=document.querySelector(`#editor`);async function main(){await Promise.all([document.fonts.load(`19px file-icons`),document.fonts.load(`19px AllTheIcons`),document.fonts.load(`19px fontawesome`),document.fonts.load(`19px material-design-icons`),document.fonts.load(`19px octicons`)]),await document.fonts.ready;let e=new Editor({canvas,fontName:`Monospace`,fontSize:18,url:`${window.location.protocol===`https:`?`wss`:`ws`}://${window.location.hostname}:${window.location.port}`,onExit:null,onClosed:null});window.addEventListener(`message`,t=>{t.data.type===`invoke-lem`&&e.notifyToServer(t.data.method,t.data.args)}),e.init()}main(); \ No newline at end of file +var __defProp=Object.defineProperty,__commonJSMin=(e,t)=>()=>(t||(e((t={exports:{}}).exports,t),e=null),t.exports),__exportAll=(e,t)=>{let n={};for(var r in e)__defProp(n,r,{get:e[r],enumerable:!0});return t||__defProp(n,Symbol.toStringTag,{value:`Module`}),n};(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var require_models=__commonJSMin((e=>{var t=e&&e.__extends||(function(){var e=function(t,n){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},e(t,n)};return function(t,n){if(typeof n!=`function`&&n!==null)throw TypeError(`Class extends value `+String(n)+` is not a constructor or null`);e(t,n);function r(){this.constructor=t}t.prototype=n===null?Object.create(n):(r.prototype=n.prototype,new r)}})();Object.defineProperty(e,"__esModule",{value:!0}),e.createJSONRPCNotification=e.createJSONRPCRequest=e.createJSONRPCSuccessResponse=e.createJSONRPCErrorResponse=e.JSONRPCErrorCode=e.JSONRPCErrorException=e.isJSONRPCResponses=e.isJSONRPCResponse=e.isJSONRPCRequests=e.isJSONRPCRequest=e.isJSONRPCID=e.JSONRPC=void 0,e.JSONRPC=`2.0`,e.isJSONRPCID=function(e){return typeof e==`string`||typeof e==`number`||e===null},e.isJSONRPCRequest=function(t){return t.jsonrpc===e.JSONRPC&&t.method!==void 0&&t.result===void 0&&t.error===void 0},e.isJSONRPCRequests=function(t){return Array.isArray(t)&&t.every(e.isJSONRPCRequest)},e.isJSONRPCResponse=function(t){return t.jsonrpc===e.JSONRPC&&t.id!==void 0&&(t.result!==void 0||t.error!==void 0)},e.isJSONRPCResponses=function(t){return Array.isArray(t)&&t.every(e.isJSONRPCResponse)};var n=function(e,t,n){var r={code:e,message:t};return n!=null&&(r.data=n),r};e.JSONRPCErrorException=function(e){t(r,e);function r(t,n,i){var a=e.call(this,t)||this;return Object.setPrototypeOf(a,r.prototype),a.code=n,a.data=i,a}return r.prototype.toObject=function(){return n(this.code,this.message,this.data)},r}(Error),(function(e){e[e.ParseError=-32700]=`ParseError`,e[e.InvalidRequest=-32600]=`InvalidRequest`,e[e.MethodNotFound=-32601]=`MethodNotFound`,e[e.InvalidParams=-32602]=`InvalidParams`,e[e.InternalError=-32603]=`InternalError`})(e.JSONRPCErrorCode||={}),e.createJSONRPCErrorResponse=function(t,r,i,a){return{jsonrpc:e.JSONRPC,id:t,error:n(r,i,a)}},e.createJSONRPCSuccessResponse=function(t,n){return{jsonrpc:e.JSONRPC,id:t,result:n??null}},e.createJSONRPCRequest=function(t,n,r){return{jsonrpc:e.JSONRPC,id:t,method:n,params:r}},e.createJSONRPCNotification=function(t,n){return{jsonrpc:e.JSONRPC,method:t,params:n}}})),require_internal=__commonJSMin((e=>{Object.defineProperty(e,"__esModule",{value:!0}),e.DefaultErrorCode=void 0,e.DefaultErrorCode=0})),require_client=__commonJSMin((e=>{var t=e&&e.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||=Promise)(function(n,a){function o(e){try{c(r.next(e))}catch(e){a(e)}}function s(e){try{c(r.throw(e))}catch(e){a(e)}}function c(e){e.done?n(e.value):i(e.value).then(o,s)}c((r=r.apply(e,t||[])).next())})},n=e&&e.__generator||function(e,t){var n={label:0,sent:function(){if(a[0]&1)throw a[1];return a[1]},trys:[],ops:[]},r,i,a,o;return o={next:s(0),throw:s(1),return:s(2)},typeof Symbol==`function`&&(o[Symbol.iterator]=function(){return this}),o;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw TypeError(`Generator is already executing.`);for(;o&&(o=0,s[0]&&(n=0)),n;)try{if(r=1,i&&(a=s[0]&2?i.return:s[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,s[1])).done)return a;switch(i=0,a&&(s=[s[0]&2,a.value]),s[0]){case 0:case 1:a=s;break;case 4:return n.label++,{value:s[1],done:!1};case 5:n.label++,i=s[1],s=[0];continue;case 7:s=n.ops.pop(),n.trys.pop();continue;default:if((a=n.trys,!(a=a.length>0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{Object.defineProperty(e,"__esModule",{value:!0})})),require_server=__commonJSMin((e=>{var t=e&&e.__assign||function(){return t=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{var t=e&&e.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||=Promise)(function(n,a){function o(e){try{c(r.next(e))}catch(e){a(e)}}function s(e){try{c(r.throw(e))}catch(e){a(e)}}function c(e){e.done?n(e.value):i(e.value).then(o,s)}c((r=r.apply(e,t||[])).next())})},n=e&&e.__generator||function(e,t){var n={label:0,sent:function(){if(a[0]&1)throw a[1];return a[1]},trys:[],ops:[]},r,i,a,o;return o={next:s(0),throw:s(1),return:s(2)},typeof Symbol==`function`&&(o[Symbol.iterator]=function(){return this}),o;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw TypeError(`Generator is already executing.`);for(;o&&(o=0,s[0]&&(n=0)),n;)try{if(r=1,i&&(a=s[0]&2?i.return:s[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,s[1])).done)return a;switch(i=0,a&&(s=[s[0]&2,a.value]),s[0]){case 0:case 1:a=s;break;case 4:return n.label++,{value:s[1],done:!1};case 5:n.label++,i=s[1],s=[0];continue;case 7:s=n.ops.pop(),n.trys.pop();continue;default:if((a=n.trys,!(a=a.length>0&&a[a.length-1]))&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!a||s[1]>a[0]&&s[1]{var t=e&&e.__createBinding||(Object.create?(function(e,t,n,r){r===void 0&&(r=n);var i=Object.getOwnPropertyDescriptor(t,n);(!i||(`get`in i?!t.__esModule:i.writable||i.configurable))&&(i={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,r,i)}):(function(e,t,n,r){r===void 0&&(r=n),e[r]=t[n]})),n=e&&e.__exportStar||function(e,n){for(var r in e)r!=="default"&&!Object.prototype.hasOwnProperty.call(n,r)&&t(n,e,r)};Object.defineProperty(e,"__esModule",{value:!0}),n(require_client(),e),n(require_interfaces(),e),n(require_models(),e),n(require_server(),e),n(require_server_and_client(),e)})),import_dist=require_dist(),JSONRPC=class{constructor(e,{onConnected:t,onClosed:n}){this.url=e,this.onConnected=t,this.onClosed=n,this.messageQueue=[],this.serverAndClient=null,this.connect(),this.connectionEstablished=!1,this.timerId=null,this.closed=!1}close(){this.timerId&&clearTimeout(this.timerId),this.webSocket.close(),this.closed=!0}on(e,t){this.serverAndClient.addMethod(e,t)}async requestInternal(e,t,n){let r=await this.serverAndClient.request(e,t);n&&n(r)}requestMessageQueue(){this.messageQueue.forEach(e=>{let[t,n,r]=e;this.requestInternal(t,n,r)}),this.messageQueue=[]}request(e,t,n){this.webSocket.readyState===WebSocket.OPEN?this.requestInternal(e,t,n):this.messageQueue.push([e,t,n])}notify(e,t){switch(this.webSocket.readyState){case WebSocket.OPEN:this.serverAndClient.notify(e,t);break;case WebSocket.CLOSED:break}}connect(e){this.closed||(console.log(`connect`,this.url),this.webSocket=new WebSocket(this.url),this.serverAndClient||=new import_dist.JSONRPCServerAndClient(new import_dist.JSONRPCServer,new import_dist.JSONRPCClient(e=>{try{return this.webSocket.send(JSON.stringify(e)),Promise.resolve()}catch(e){return Promise.reject(e)}})),this.webSocket.onmessage=e=>{this.serverAndClient.receiveAndSend(JSON.parse(e.data.toString()))},this.webSocket.onopen=()=>{console.log(`WebSocket connection established`),this.connectionEstablished=!0,this.onConnected&&this.onConnected(),this.requestMessageQueue()},this.webSocket.onclose=e=>{console.error(`WebScoket closed`,e),this.serverAndClient.rejectAllPendingRequests(`Connection is closed (${e.reason}).`),this.connectionEstablished&&this.onClosed(),this.timerId=setTimeout(()=>{this.connect()},3e3)},this.webSocket.onerror=e=>{console.error(`WebSocket error:`,e),this.webSocket.close()})}},keyevent_exports=__exportAll({convertKeyEvent:()=>convertKeyEvent}),modifierKeys=[`Shift`,`Control`,`Alt`,`Meta`,`CapsLock`],convertKeyTable={Enter:`Return`,ArrowRight:`Right`,ArrowLeft:`Left`,ArrowUp:`Up`,ArrowDown:`Down`,"¡":`1`,"™":`2`,"£":`3`,"¢":`4`,"∞":`5`,"§":`6`,"¶":`7`,"•":`8`,ª:`9`,º:`0`,"–":`-`,"≠":`=`,"“":`[`,"‘":`]`,"«":`\\`,"…":`;`,æ:`'`,"≤":`,`,"≥":`.`,"÷":`/`,"⁄":`!`,"€":`@`,"‹":`#`,"›":`$`,fi:`%`,fl:`^`,"‡":`&`,"°":`*`,"·":`(`,"‚":`)`,"—":`_`,"±":`+`,"”":`{`,"’":`}`,"»":`|`,Ú:`:`,Æ:`"`,"¯":`<`,"˘":`>`,"¿":`?`,œ:`q`,"∑":`w`,"´":`e`,"®":`r`,"†":`t`,"¥":`y`,"¨":`u`,ˆ:`i`,ø:`o`,π:`p`,å:`a`,ß:`s`,"∂":`d`,ƒ:`f`,"©":`g`,"˙":`h`,"∆":`j`,"˚":`k`,"¬":`l`,Ω:`z`,"≈":`x`,ç:`c`,"√":`v`,"∫":`b`,"˜":`n`,µ:`m`,Œ:`Q`,"„":`W`,"´":`E`,"‰":`R`,ˇ:`T`,Á:`Y`,"¨":`U`,ˆ:`I`,Ø:`O`,"∏":`P`,Å:`A`,Í:`S`,Î:`D`,Ï:`F`,"˝":`G`,Ó:`H`,Ô:`J`,"":`K`,Ò:`L`,"¸":`Z`,"˛":`X`,Ç:`C`,"◊":`V`,ı:`B`,"˜":`N`,Â:`M`};function getKey(e){return e.altKey?convertKeyTable[e.key]||(e.code.startsWith(`Key`)?e.code[3].toLowerCase():null)||e.key:convertKeyTable[e.key]||e.key}function convertKeyEvent(e){return modifierKeys.indexOf(e.key)===-1?{key:getKey(e),ctrl:e.ctrlKey,meta:e.altKey,super:e.metaKey,shift:e.shiftKey}:null}var lib_exports=__exportAll({computeWidth:()=>computeWidth,eawVersion:()=>version,getEAW:()=>getEAW}),defs=[[0,31,`N`],[32,126,`Na`],[127,160,`N`],[161,161,`A`],[162,163,`Na`],[164,164,`A`],[165,166,`Na`],[167,168,`A`],[169,169,`N`],[170,170,`A`],[171,171,`N`],[172,172,`Na`],[173,174,`A`],[175,175,`Na`],[176,180,`A`],[181,181,`N`],[182,186,`A`],[187,187,`N`],[188,191,`A`],[192,197,`N`],[198,198,`A`],[199,207,`N`],[208,208,`A`],[209,214,`N`],[215,216,`A`],[217,221,`N`],[222,225,`A`],[226,229,`N`],[230,230,`A`],[231,231,`N`],[232,234,`A`],[235,235,`N`],[236,237,`A`],[238,239,`N`],[240,240,`A`],[241,241,`N`],[242,243,`A`],[244,246,`N`],[247,250,`A`],[251,251,`N`],[252,252,`A`],[253,253,`N`],[254,254,`A`],[255,256,`N`],[257,257,`A`],[258,272,`N`],[273,273,`A`],[274,274,`N`],[275,275,`A`],[276,282,`N`],[283,283,`A`],[284,293,`N`],[294,295,`A`],[296,298,`N`],[299,299,`A`],[300,304,`N`],[305,307,`A`],[308,311,`N`],[312,312,`A`],[313,318,`N`],[319,322,`A`],[323,323,`N`],[324,324,`A`],[325,327,`N`],[328,331,`A`],[332,332,`N`],[333,333,`A`],[334,337,`N`],[338,339,`A`],[340,357,`N`],[358,359,`A`],[360,362,`N`],[363,363,`A`],[364,461,`N`],[462,462,`A`],[463,463,`N`],[464,464,`A`],[465,465,`N`],[466,466,`A`],[467,467,`N`],[468,468,`A`],[469,469,`N`],[470,470,`A`],[471,471,`N`],[472,472,`A`],[473,473,`N`],[474,474,`A`],[475,475,`N`],[476,476,`A`],[477,592,`N`],[593,593,`A`],[594,608,`N`],[609,609,`A`],[610,707,`N`],[708,708,`A`],[709,710,`N`],[711,711,`A`],[712,712,`N`],[713,715,`A`],[716,716,`N`],[717,717,`A`],[718,719,`N`],[720,720,`A`],[721,727,`N`],[728,731,`A`],[732,732,`N`],[733,733,`A`],[734,734,`N`],[735,735,`A`],[736,767,`N`],[768,879,`A`],[880,912,`N`],[913,929,`A`],[930,930,`N`],[931,937,`A`],[938,944,`N`],[945,961,`A`],[962,962,`N`],[963,969,`A`],[970,1024,`N`],[1025,1025,`A`],[1026,1039,`N`],[1040,1103,`A`],[1104,1104,`N`],[1105,1105,`A`],[1106,4351,`N`],[4352,4447,`W`],[4448,8207,`N`],[8208,8208,`A`],[8209,8210,`N`],[8211,8214,`A`],[8215,8215,`N`],[8216,8217,`A`],[8218,8219,`N`],[8220,8221,`A`],[8222,8223,`N`],[8224,8226,`A`],[8227,8227,`N`],[8228,8231,`A`],[8232,8239,`N`],[8240,8240,`A`],[8241,8241,`N`],[8242,8243,`A`],[8244,8244,`N`],[8245,8245,`A`],[8246,8250,`N`],[8251,8251,`A`],[8252,8253,`N`],[8254,8254,`A`],[8255,8307,`N`],[8308,8308,`A`],[8309,8318,`N`],[8319,8319,`A`],[8320,8320,`N`],[8321,8324,`A`],[8325,8360,`N`],[8361,8361,`H`],[8362,8363,`N`],[8364,8364,`A`],[8365,8450,`N`],[8451,8451,`A`],[8452,8452,`N`],[8453,8453,`A`],[8454,8456,`N`],[8457,8457,`A`],[8458,8466,`N`],[8467,8467,`A`],[8468,8469,`N`],[8470,8470,`A`],[8471,8480,`N`],[8481,8482,`A`],[8483,8485,`N`],[8486,8486,`A`],[8487,8490,`N`],[8491,8491,`A`],[8492,8530,`N`],[8531,8532,`A`],[8533,8538,`N`],[8539,8542,`A`],[8543,8543,`N`],[8544,8555,`A`],[8556,8559,`N`],[8560,8569,`A`],[8570,8584,`N`],[8585,8585,`A`],[8586,8591,`N`],[8592,8601,`A`],[8602,8631,`N`],[8632,8633,`A`],[8634,8657,`N`],[8658,8658,`A`],[8659,8659,`N`],[8660,8660,`A`],[8661,8678,`N`],[8679,8679,`A`],[8680,8703,`N`],[8704,8704,`A`],[8705,8705,`N`],[8706,8707,`A`],[8708,8710,`N`],[8711,8712,`A`],[8713,8714,`N`],[8715,8715,`A`],[8716,8718,`N`],[8719,8719,`A`],[8720,8720,`N`],[8721,8721,`A`],[8722,8724,`N`],[8725,8725,`A`],[8726,8729,`N`],[8730,8730,`A`],[8731,8732,`N`],[8733,8736,`A`],[8737,8738,`N`],[8739,8739,`A`],[8740,8740,`N`],[8741,8741,`A`],[8742,8742,`N`],[8743,8748,`A`],[8749,8749,`N`],[8750,8750,`A`],[8751,8755,`N`],[8756,8759,`A`],[8760,8763,`N`],[8764,8765,`A`],[8766,8775,`N`],[8776,8776,`A`],[8777,8779,`N`],[8780,8780,`A`],[8781,8785,`N`],[8786,8786,`A`],[8787,8799,`N`],[8800,8801,`A`],[8802,8803,`N`],[8804,8807,`A`],[8808,8809,`N`],[8810,8811,`A`],[8812,8813,`N`],[8814,8815,`A`],[8816,8833,`N`],[8834,8835,`A`],[8836,8837,`N`],[8838,8839,`A`],[8840,8852,`N`],[8853,8853,`A`],[8854,8856,`N`],[8857,8857,`A`],[8858,8868,`N`],[8869,8869,`A`],[8870,8894,`N`],[8895,8895,`A`],[8896,8977,`N`],[8978,8978,`A`],[8979,8985,`N`],[8986,8987,`W`],[8988,9e3,`N`],[9001,9002,`W`],[9003,9192,`N`],[9193,9196,`W`],[9197,9199,`N`],[9200,9200,`W`],[9201,9202,`N`],[9203,9203,`W`],[9204,9311,`N`],[9312,9449,`A`],[9450,9450,`N`],[9451,9547,`A`],[9548,9551,`N`],[9552,9587,`A`],[9588,9599,`N`],[9600,9615,`A`],[9616,9617,`N`],[9618,9621,`A`],[9622,9631,`N`],[9632,9633,`A`],[9634,9634,`N`],[9635,9641,`A`],[9642,9649,`N`],[9650,9651,`A`],[9652,9653,`N`],[9654,9655,`A`],[9656,9659,`N`],[9660,9661,`A`],[9662,9663,`N`],[9664,9665,`A`],[9666,9669,`N`],[9670,9672,`A`],[9673,9674,`N`],[9675,9675,`A`],[9676,9677,`N`],[9678,9681,`A`],[9682,9697,`N`],[9698,9701,`A`],[9702,9710,`N`],[9711,9711,`A`],[9712,9724,`N`],[9725,9726,`W`],[9727,9732,`N`],[9733,9734,`A`],[9735,9736,`N`],[9737,9737,`A`],[9738,9741,`N`],[9742,9743,`A`],[9744,9747,`N`],[9748,9749,`W`],[9750,9755,`N`],[9756,9756,`A`],[9757,9757,`N`],[9758,9758,`A`],[9759,9791,`N`],[9792,9792,`A`],[9793,9793,`N`],[9794,9794,`A`],[9795,9799,`N`],[9800,9811,`W`],[9812,9823,`N`],[9824,9825,`A`],[9826,9826,`N`],[9827,9829,`A`],[9830,9830,`N`],[9831,9834,`A`],[9835,9835,`N`],[9836,9837,`A`],[9838,9838,`N`],[9839,9839,`A`],[9840,9854,`N`],[9855,9855,`W`],[9856,9874,`N`],[9875,9875,`W`],[9876,9885,`N`],[9886,9887,`A`],[9888,9888,`N`],[9889,9889,`W`],[9890,9897,`N`],[9898,9899,`W`],[9900,9916,`N`],[9917,9918,`W`],[9919,9919,`A`],[9920,9923,`N`],[9924,9925,`W`],[9926,9933,`A`],[9934,9934,`W`],[9935,9939,`A`],[9940,9940,`W`],[9941,9953,`A`],[9954,9954,`N`],[9955,9955,`A`],[9956,9959,`N`],[9960,9961,`A`],[9962,9962,`W`],[9963,9969,`A`],[9970,9971,`W`],[9972,9972,`A`],[9973,9973,`W`],[9974,9977,`A`],[9978,9978,`W`],[9979,9980,`A`],[9981,9981,`W`],[9982,9983,`A`],[9984,9988,`N`],[9989,9989,`W`],[9990,9993,`N`],[9994,9995,`W`],[9996,10023,`N`],[10024,10024,`W`],[10025,10044,`N`],[10045,10045,`A`],[10046,10059,`N`],[10060,10060,`W`],[10061,10061,`N`],[10062,10062,`W`],[10063,10066,`N`],[10067,10069,`W`],[10070,10070,`N`],[10071,10071,`W`],[10072,10101,`N`],[10102,10111,`A`],[10112,10132,`N`],[10133,10135,`W`],[10136,10159,`N`],[10160,10160,`W`],[10161,10174,`N`],[10175,10175,`W`],[10176,10213,`N`],[10214,10221,`Na`],[10222,10628,`N`],[10629,10630,`Na`],[10631,11034,`N`],[11035,11036,`W`],[11037,11087,`N`],[11088,11088,`W`],[11089,11092,`N`],[11093,11093,`W`],[11094,11097,`A`],[11098,11903,`N`],[11904,11929,`W`],[11930,11930,`N`],[11931,12019,`W`],[12020,12031,`N`],[12032,12245,`W`],[12246,12271,`N`],[12272,12287,`W`],[12288,12288,`F`],[12289,12350,`W`],[12351,12352,`N`],[12353,12438,`W`],[12439,12440,`N`],[12441,12543,`W`],[12544,12548,`N`],[12549,12591,`W`],[12592,12592,`N`],[12593,12686,`W`],[12687,12687,`N`],[12688,12771,`W`],[12772,12782,`N`],[12783,12830,`W`],[12831,12831,`N`],[12832,12871,`W`],[12872,12879,`A`],[12880,19903,`W`],[19904,19967,`N`],[19968,42124,`W`],[42125,42127,`N`],[42128,42182,`W`],[42183,43359,`N`],[43360,43388,`W`],[43389,44031,`N`],[44032,55203,`W`],[55204,57343,`N`],[57344,63743,`A`],[63744,64255,`W`],[64256,65023,`N`],[65024,65039,`A`],[65040,65049,`W`],[65050,65071,`N`],[65072,65106,`W`],[65107,65107,`N`],[65108,65126,`W`],[65127,65127,`N`],[65128,65131,`W`],[65132,65280,`N`],[65281,65376,`F`],[65377,65470,`H`],[65471,65473,`N`],[65474,65479,`H`],[65480,65481,`N`],[65482,65487,`H`],[65488,65489,`N`],[65490,65495,`H`],[65496,65497,`N`],[65498,65500,`H`],[65501,65503,`N`],[65504,65510,`F`],[65511,65511,`N`],[65512,65518,`H`],[65519,65532,`N`],[65533,65533,`A`],[65534,94175,`N`],[94176,94180,`W`],[94181,94191,`N`],[94192,94193,`W`],[94194,94207,`N`],[94208,100343,`W`],[100344,100351,`N`],[100352,101589,`W`],[101590,101631,`N`],[101632,101640,`W`],[101641,110575,`N`],[110576,110579,`W`],[110580,110580,`N`],[110581,110587,`W`],[110588,110588,`N`],[110589,110590,`W`],[110591,110591,`N`],[110592,110882,`W`],[110883,110897,`N`],[110898,110898,`W`],[110899,110927,`N`],[110928,110930,`W`],[110931,110932,`N`],[110933,110933,`W`],[110934,110947,`N`],[110948,110951,`W`],[110952,110959,`N`],[110960,111355,`W`],[111356,126979,`N`],[126980,126980,`W`],[126981,127182,`N`],[127183,127183,`W`],[127184,127231,`N`],[127232,127242,`A`],[127243,127247,`N`],[127248,127277,`A`],[127278,127279,`N`],[127280,127337,`A`],[127338,127343,`N`],[127344,127373,`A`],[127374,127374,`W`],[127375,127376,`A`],[127377,127386,`W`],[127387,127404,`A`],[127405,127487,`N`],[127488,127490,`W`],[127491,127503,`N`],[127504,127547,`W`],[127548,127551,`N`],[127552,127560,`W`],[127561,127567,`N`],[127568,127569,`W`],[127570,127583,`N`],[127584,127589,`W`],[127590,127743,`N`],[127744,127776,`W`],[127777,127788,`N`],[127789,127797,`W`],[127798,127798,`N`],[127799,127868,`W`],[127869,127869,`N`],[127870,127891,`W`],[127892,127903,`N`],[127904,127946,`W`],[127947,127950,`N`],[127951,127955,`W`],[127956,127967,`N`],[127968,127984,`W`],[127985,127987,`N`],[127988,127988,`W`],[127989,127991,`N`],[127992,128062,`W`],[128063,128063,`N`],[128064,128064,`W`],[128065,128065,`N`],[128066,128252,`W`],[128253,128254,`N`],[128255,128317,`W`],[128318,128330,`N`],[128331,128334,`W`],[128335,128335,`N`],[128336,128359,`W`],[128360,128377,`N`],[128378,128378,`W`],[128379,128404,`N`],[128405,128406,`W`],[128407,128419,`N`],[128420,128420,`W`],[128421,128506,`N`],[128507,128591,`W`],[128592,128639,`N`],[128640,128709,`W`],[128710,128715,`N`],[128716,128716,`W`],[128717,128719,`N`],[128720,128722,`W`],[128723,128724,`N`],[128725,128727,`W`],[128728,128731,`N`],[128732,128735,`W`],[128736,128746,`N`],[128747,128748,`W`],[128749,128755,`N`],[128756,128764,`W`],[128765,128991,`N`],[128992,129003,`W`],[129004,129007,`N`],[129008,129008,`W`],[129009,129291,`N`],[129292,129338,`W`],[129339,129339,`N`],[129340,129349,`W`],[129350,129350,`N`],[129351,129535,`W`],[129536,129647,`N`],[129648,129660,`W`],[129661,129663,`N`],[129664,129672,`W`],[129673,129679,`N`],[129680,129725,`W`],[129726,129726,`N`],[129727,129733,`W`],[129734,129741,`N`],[129742,129755,`W`],[129756,129759,`N`],[129760,129768,`W`],[129769,129775,`N`],[129776,129784,`W`],[129785,131071,`N`],[131072,196605,`W`],[196606,196607,`N`],[196608,262141,`W`],[262142,917759,`N`],[917760,917999,`A`],[918e3,983039,`N`],[983040,1048573,`A`],[1048574,1048575,`N`],[1048576,1114109,`A`],[1114110,1114111,`N`]],version=`15.1.0`;function getEAWOfCodePoint(e){let t=0,n=defs.length-1;for(;t!==n;){let r=t+(n-t>>1),[i,a,o]=defs[r];if(ea)t=r+1;else return o}return defs[t][2]}function getEAW(e,t=0){let n=e.codePointAt(t);if(n!==void 0)return getEAWOfCodePoint(n)}var defaultWidths={N:1,Na:1,W:2,F:2,H:1,A:1};function computeWidth(e,t){let n=0;for(let r of e){let e=getEAW(r);n+=t&&t[e]||defaultWidths[e]}return n}var textOffsetY=5,isSafari=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);function isWideChar(e){switch(getEAW(e)){case`F`:case`W`:return!0;default:return!1}}function isMacOS(){return window.navigator.userAgent.indexOf(`Mac OS X`)!==-1}function computeFontSize(e){let t=document.createElement(`canvas`).getContext(`2d`);t.font=e;let n=t.measureText(`W`);return[Math.floor(n.width),Math.round(n.fontBoundingBoxAscent+textOffsetY+(n.emHeightDescent||0)),Math.round(n.fontBoundingBoxAscent+textOffsetY)]}function drawBlock({ctx:e,x:t,y:n,width:r,height:i,style:a}){e.fillStyle=a,e.fillRect(t,n,r,i)}function drawText({ctx:e,x:t,y:n,text:r,font:i,style:a,option:o}){n+=Math.round(textOffsetY),e.fillStyle=a,e.font=i,e.textBaseline=`top`;for(let i of r)isWideChar(i)?(e.fillText(i,t,n,o.fontWidth*2),t+=o.fontWidth*2):(e.fillText(i,t,n,o.fontWidth),t+=o.fontWidth)}function drawHorizontalLine({ctx:e,x:t,y:n,width:r,style:i,lineWidth:a=1}){e.strokeStyle=i,e.lineWidth=a,e.setLineDash=[],e.beginPath(),e.moveTo(t,n),e.lineTo(t+r,n),e.stroke()}var Option=class{constructor({fontName:e,fontSize:t}){this.setFont(e,t),this.foreground=`#cccccc`,this.background=`#2d2d2d`}setFont(e,t){let n=t+`px `+e,[r,i,a]=computeFontSize(n);this.fontName=e,this.fontSize=t,this.fontWidth=r,this.fontHeight=i,this.fontAscent=a,this.font=n}};function getLemEditorElement(){return document.getElementById(`lem-editor`)}function normalizeWheelDelta(e,t,n,r){switch(n){case 0:return{dx:e/r,dy:t/r};case 2:return{dx:e*20,dy:t*20};default:return{dx:e,dy:t}}}function extractWholeLines(e,t){let n=Math.trunc(e),r=Math.trunc(t);return{scrollX:n,scrollY:r,remainderX:e-n,remainderY:t-r}}function cursorPosition(e,t){let[n,r]=t.getDisplayRectangle(),i=e.clientX-n,a=e.clientY-r;return{pixelX:i,pixelY:a,x:Math.floor(i/t.option.fontWidth),y:Math.floor(a/t.option.fontHeight)}}function makeWheelHandler(e){let t={x:0,y:0},n=!1,r={pixelX:0,pixelY:0,x:0,y:0};return i=>{i.preventDefault(),r=cursorPosition(i,e);let{dx:a,dy:o}=normalizeWheelDelta(i.deltaX,i.deltaY,i.deltaMode,e.option.fontHeight);t={x:t.x+a,y:t.y+o},n||(n=!0,requestAnimationFrame(()=>{n=!1;let{scrollX:i,scrollY:a,remainderX:o,remainderY:s}=extractWholeLines(t.x,t.y);t={x:o,y:s},(i!==0||a!==0)&&e.jsonrpc.notify(`input`,{kind:`wheel`,value:{...r,wheelX:-i,wheelY:-a}})}))}}function addMouseEventListeners({dom:e,editor:t,isDraggable:n,draggableStyle:r}){e.addEventListener(`contextmenu`,e=>{e.preventDefault()});let i=(e,n)=>{e.preventDefault();let[r,i]=t.getDisplayRectangle(),a=e.clientX-r,o=e.clientY-i,s=Math.floor(a/t.option.fontWidth),c=Math.floor(o/t.option.fontHeight);t.jsonrpc.notify(`input`,{kind:n,value:{x:s,y:c,pixelX:a,pixelY:o,button:e.button,clicks:e.detail}})};e.addEventListener(`mousedown`,e=>{n&&(document.body.style.cursor=r),t.focusHiddenInput(),i(e,`mousedown`)}),e.addEventListener(`mouseup`,e=>{n&&(document.body.style.cursor=`default`),i(e,`mouseup`)});let a=0;e.addEventListener(`mousemove`,e=>{e.preventDefault();let n=Date.now();if(n-a>50){a=n;let[r,i]=t.getDisplayRectangle(),o=e.clientX-r,s=e.clientY-i,c=Math.floor(o/t.option.fontWidth),l=Math.floor(s/t.option.fontHeight);t.jsonrpc.notify(`input`,{kind:`mousemove`,value:{x:c,y:l,pixelX:o,pixelY:s,button:e.buttons===0?null:e.buttons-1}})}}),n&&(e.addEventListener(`mouseover`,()=>{document.body.style.cursor=r}),e.addEventListener(`mouseout`,e=>{e.buttons!==1&&(document.body.style.cursor=`default`)})),e.addEventListener(`wheel`,makeWheelHandler(t))}var zIndexTable={"floating-window":200,modeline:100,"vertical-border":100,"horizontal-border":101};function zindex(e){return zIndexTable[e]||0}var borderOffsetX=5,borderOffsetY=10,BaseSurface=class{constructor({editor:e}){this.editor=e,this.mainDOM=null,this.wrapper=null}delete(){this.wrapper?getLemEditorElement().removeChild(this.wrapper):getLemEditorElement().removeChild(this.mainDOM)}setupDOM({dom:e,isFloating:t,border:n,cssClassName:r}){this.mainDOM=e,t&&n?(this.wrapper=document.createElement(`div`),r&&(this.wrapper.className=r),this.wrapper.style.position=`absolute`,this.wrapper.style.backgroundColor=this.editor.option.background,this.wrapper.style.zIndex=zindex(`floating-window`),this.wrapper.appendChild(e),getLemEditorElement().appendChild(this.wrapper)):(r&&(e.className=r),getLemEditorElement().appendChild(e))}move(e,t){let[n,r]=this.editor.getDisplayRectangle(),i=Math.floor(n+e),a=Math.floor(r+t);this.wrapper?(this.wrapper.style.left=i-borderOffsetX+`px`,this.wrapper.style.top=a-borderOffsetY+`px`,this.mainDOM.style.left=borderOffsetX+`px`,this.mainDOM.style.top=borderOffsetY+`px`):(this.mainDOM.style.left=i+`px`,this.mainDOM.style.top=a+`px`)}_resize(e,t){let n=window.devicePixelRatio||1;this.mainDOM.width=e*n,this.mainDOM.height=t*n,this.mainDOM.style.width=e+`px`,this.mainDOM.style.height=t+`px`,this.wrapper&&(this.wrapper.style.width=e+borderOffsetX*2+`px`,this.wrapper.style.height=t+borderOffsetY*2+`px`)}drawBlock(e,t,n,r,i){}drawText(e,t,n,r,i,a,o,s){}drawImage(e,t,n,r,i,a,o){}clearImages(e,t){}clearAllImages(){}touch(){}evalIn(code){return eval(code)}},CanvasSurface=class extends BaseSurface{constructor({editor:e,view:t,pixelX:n,pixelY:r,pixelWidth:i,pixelHeight:a,styles:o,isFloating:s,border:c,cssClassName:l}){super({editor:e});let u=this.setupCanvas(o);this.setupDOM({dom:u,isFloating:s,border:c,cssClassName:l}),this.move(n,r),this.resize(i,a),this.drawingQueue=[],addMouseEventListeners({dom:u,editor:e})}setupCanvas(e){let t=document.createElement(`canvas`);if(t.style.position=`absolute`,e)for(let n in e)t.style[n]=e[n];return t}resize(e,t){this._resize(e,t);let n=window.devicePixelRatio||1;this.mainDOM.getContext(`2d`).scale(n,n)}move(e,t){if(super.move(e,t),this.imageEls)for(let[,e]of this.imageEls)this.positionImage(e)}delete(){this.clearAllImages(),super.delete()}drawBlock(e,t,n,r,i){this.drawingQueue.push(function(a){drawBlock({ctx:a,x:e,y:t,width:n,height:r,style:i})})}drawText(e,t,n,r,i,a,o,s){let c=this.editor.option,l=o??t,u=s??c.fontHeight;this.drawingQueue.push(function(o){if(a=a?`${c.fontSize}px ${a}`:c.font,!i)drawBlock({ctx:o,x:e,y:l,width:r,height:u,style:c.background}),drawText({ctx:o,x:e,y:t,text:n,style:c.foreground,font:a,option:c});else{let{foreground:s,background:d,bold:f,reverse:p,underline:m,cursor:h}=i;if(s||=c.foreground,d||=c.background,p){let e=d;d=s,s=e}h&&(d=c.background),drawBlock({ctx:o,x:e,y:l,width:r,height:u,style:d}),drawText({ctx:o,x:e,y:t,text:n,style:s,font:f?`bold `+a:a,option:c}),m&&drawHorizontalLine({ctx:o,x:e,y:t+c.fontHeight-2,width:r,style:typeof m==`string`?m:s,lineWidth:2})}})}imageBaseLeft(){return parseFloat(this.mainDOM.style.left)||0}imageBaseTop(){return parseFloat(this.mainDOM.style.top)||0}drawImage(e,t,n,r,i,a,o){this.imageEls||=new Map;let s=e+`,`+t,c=this.imageEls.get(s);if(c&&c.url!==o&&(c.el.remove(),this.imageEls.delete(s),c=null),!c){let e=document.createElement(`img`);e.style.position=`absolute`,e.style.pointerEvents=`none`,e.style.zIndex=`1`,e.src=o,this.mainDOM.parentNode.appendChild(e),c={el:e,url:o},this.imageEls.set(s,c)}c.x=e,c.y=t,c.width=n,c.height=r,c.clipWidth=i,c.clipHeight=a,this.positionImage(c)}positionImage(e){e.el.style.left=this.imageBaseLeft()+e.x+`px`,e.el.style.top=this.imageBaseTop()+e.y+`px`,e.el.style.width=e.width+`px`,e.el.style.height=e.height+`px`;let t=e.clipWidth==null?0:Math.max(0,e.width-e.clipWidth),n=e.clipHeight==null?0:Math.max(0,e.height-e.clipHeight);e.el.style.clipPath=t>0||n>0?`inset(0px ${t}px ${n}px 0px)`:``}clearImages(e,t){if(this.imageEls)for(let[n,r]of this.imageEls){let i=r.y+(r.height||0);r.ye&&(r.el.remove(),this.imageEls.delete(n))}}clearAllImages(){if(this.imageEls){for(let[,e]of this.imageEls)e.el.remove();this.imageEls.clear()}}touch(){let e=this.mainDOM.getContext(`2d`);for(let t of this.drawingQueue)t(e);this.drawingQueue=[]}activate(){this.mainDOM.dataset.store=`active`}deactivate(){this.mainDOM.dataset.store=`inactive`}},HTMLSurface=class extends BaseSurface{constructor({editor:e,pixelX:t,pixelY:n,pixelWidth:r,pixelHeight:i,styles:a,option:o,isFloating:s,border:c,html:l}){super({editor:e});let u=document.createElement(`iframe`);this.setupDOM({dom:u,isFloating:s,border:c}),u.style.position=`absolute`,u.style.backgroundColor=o.background,u.setAttribute(`sandbox`,`allow-scripts allow-same-origin`),u.srcdoc=l,u.addEventListener(`load`,()=>{let e=u.contentWindow;e.invokeLem=(e,t)=>parent.postMessage({type:`invoke-lem`,method:e,args:t})}),this.iframe=u,this.move(t,n),this.resize(r,i)}resize(e,t){this._resize(e,t)}update(e){let t=this.iframe.contentWindow.scrollY;this.iframe.srcdoc=e,this.iframe.onload=()=>{this.iframe.onload=null,this.iframe.contentWindow.scrollTo(0,t)}}evalIn(e){return this.iframe.contentWindow.eval(e)}},VerticalBorder=class{constructor({x:e,y:t,height:n,option:r,editor:i}){this.option=r,this.editor=i,this.line=document.createElement(`div`),this.line.className=`lem-editor__vertical-border`,this.line.style.height=n+`px`,this.line.style.position=`absolute`,this.line.style.zIndex=zindex(`vertical-border`),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:i,isDraggable:!0,draggableStyle:`col-resize`})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.line.style.left=Math.floor(n+e-this.option.fontWidth/2)+`px`,this.line.style.top=r+t+`px`}resize(e){this.line.style.height=e+`px`}},HorizontalBorder=class{constructor({x:e,y:t,width:n,option:r,editor:i}){this.option=r,this.editor=i,this.line=document.createElement(`div`),this.line.className=`lem-editor__horizontal-border`,this.line.style.width=n+`px`,this.line.style.position=`absolute`,this.line.style.zIndex=zindex(`horizontal-border`),getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:i,isDraggable:!0,draggableStyle:`row-resize`})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.line.style.left=n+e+`px`,this.line.style.top=Math.floor(r+t-4)+`px`}resize(e){this.line.style.width=e+`px`}},viewStyles={header:()=>{},tile:()=>{},floating:e=>({boxSizing:`border-box`,borderColor:e.foreground,backgroundColor:e.background})};function getViewStyle(e,t){return viewStyles[e](t)||{}}var View=class{constructor({id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,useModeline:l,kind:u,type:d,content:f,border:p,borderShape:m,option:h,editor:g}){switch(this.option=h,this.id=e,this.x=t,this.y=n,this.width=r,this.height=i,this.pixelX=a,this.pixelY=o,this.pixelWidth=s,this.pixelHeight=c,this.useModeline=l,this.kind=u,this.type=d,this.border=p,this.borderShape=m,this.editor=g,this.bottomBar=null,this.leftsideBar=null,u){case`tile`:this.mainSurface=this.makeSurface(d,f),this.leftSideBar=new VerticalBorder({x:a,y:o,height:c+(l?h.fontHeight:0),option:h,editor:g}),l||(this.bottomBar=new HorizontalBorder({x:a,y:o+c-h.fontHeight,width:s,option:h,editor:g}));break;case`header`:this.mainSurface=this.makeSurface(d,f);break;case`floating`:this.mainSurface=this.makeSurface(d,f),m===`left-border`&&(this.leftSideBar=new VerticalBorder({x:a,y:o,height:c,option:h,editor:g}));break}this.modelineSurface=l?this.makeModelineSurface():null}delete(){this.mainSurface.delete(),this.modelineSurface&&this.modelineSurface.delete(),this.leftSideBar&&this.leftSideBar.delete(),this.bottomBar&&this.bottomBar.delete()}move(e,t,n,r){this.x=e,this.y=t,this.pixelX=n,this.pixelY=r,this.mainSurface.move(n,r),this.modelineSurface&&this.modelineSurface.move(n,r+this.pixelHeight),this.leftSideBar&&this.leftSideBar.move(n,r),this.bottomBar&&this.bottomBar.move(n,r+this.pixelHeight)}resize(e,t,n,r){this.width=e,this.height=t,this.pixelWidth=n,this.pixelHeight=r,this.mainSurface.resize(n,r),this.modelineSurface&&(this.modelineSurface.move(this.pixelX,this.pixelY+r),this.modelineSurface.resize(n,this.option.fontHeight)),this.leftSideBar&&this.leftSideBar.resize(r+(this.modelineSurface?this.option.fontHeight:0)),this.bottomBar&&this.bottomBar.resize(n)}clear(){this.mainSurface.drawBlock(0,0,this.pixelWidth,this.pixelHeight,this.option.background),this.mainSurface.clearImages(0,this.pixelHeight)}clearEol(e,t,n){n??=this.option.fontHeight,this.mainSurface.drawBlock(e,t,this.pixelWidth-e,n,this.option.background),this.mainSurface.clearImages(t,t+n)}clearEob(e,t){this.mainSurface.drawBlock(e,t,this.pixelWidth,this.pixelHeight-t,this.option.background),this.mainSurface.clearImages(t,this.pixelHeight)}print(e,t,n,r,i,a,o,s){this.mainSurface.drawText(e,t,n,r,i,a,o,s)}drawBlock(e,t,n,r,i){this.mainSurface.drawBlock(e,t,n,r,i||this.option.background)}drawBlockOnModeline(e,t,n,r,i){this.modelineSurface&&this.modelineSurface.drawBlock(e,t,n,r,i||this.option.background)}printImage(e,t,n,r,i,a,o){this.mainSurface.drawImage(e,t,n,r,i,a,o)}printToModeline(e,t,n,r,i,a,o){this.modelineSurface&&this.modelineSurface.drawText(e,t,n,r,i,null,a,o)}touch(e){this.mainSurface.touch(),this.modelineSurface&&(this.modelineSurface.touch(),e?this.modelineSurface.activate():this.modelineSurface.deactivate())}makeSurface(e,t){switch(e){case`html`:return this.makeHTMLSurface(t);case`editor`:return this.makeEditorSurface();default:console.error(`unknown type: ${e}`)}}makeHTMLSurface(e){return new HTMLSurface({editor:this.editor,pixelX:this.pixelX,pixelY:this.pixelY,pixelWidth:this.pixelWidth,pixelHeight:this.pixelHeight,styles:getViewStyle(this.kind,this.option),option:this.option,isFloating:this.kind===`floating`,border:this.border,html:e})}makeEditorSurface(){let e=this.borderShape===`left-border`?0:this.border,t=this.kind===`floating`;return new CanvasSurface({option:this.editor.option,pixelX:this.pixelX,pixelY:this.pixelY,pixelWidth:this.pixelWidth,pixelHeight:this.pixelHeight,styles:getViewStyle(this.kind,this.option),editor:this.editor,border:e,isFloating:t,view:this,cssClassName:t&&e?`lem-editor__floating-window--bordered`:null})}makeModelineSurface(){let e=new CanvasSurface({option:this.editor.option,pixelX:this.pixelX,pixelY:this.pixelY+this.pixelHeight,pixelWidth:this.pixelWidth,pixelHeight:this.option.fontHeight,editor:this.editor,view:this,styles:{zIndex:zindex(`modeline`)},cssClassName:`lem-editor__mode-line`});return addMouseEventListeners({dom:e.mainDOM,editor:this.editor,isDraggable:!0,draggableStyle:`row-resize`}),e}changeToHTMLContent(e){this.mainSurface.constructor.name===`HTMLSurface`?this.mainSurface.update(e):(this.mainSurface.delete(),this.mainSurface=this.makeHTMLSurface(e))}changeToEditorContent(){this.mainSurface.delete(),this.mainSurface=this.makeEditorSurface()}evalIn(e){return this.mainSurface.evalIn(e)}};function isPasteKeyEvent(e){return isMacOS()?e.metaKey&&e.key===`v`:e.ctrlKey&&e.shiftKey&&e.key===`V`}var Input=class{constructor(e){let t=e.option;this.editor=e,this.composition=!1,this.ignoreKeydownAfterCompositionend=!1,this.span=document.createElement(`span`),this.span.style.color=t.foreground,this.span.style.backgroundColor=t.background,this.span.style.position=`absolute`,this.span.style.zIndex=1e6,this.span.style.top=`0`,this.span.style.left=`0`,this.span.style.font=t.font,this.input=document.createElement(`input`),this.input.style.backgroundColor=`transparent`,this.input.style.color=`transparent`,this.input.style.width=`0`,this.input.style.padding=`0`,this.input.style.margin=`0`,this.input.style.border=`none`,this.input.style.position=`absolute`,this.input.style.zIndex=`-10`,this.input.style.top=`0`,this.input.style.left=`0`,this.input.style.font=t.font,this.input.addEventListener(`blur`,e=>{this.input.focus()}),this.input.addEventListener(`input`,e=>{this.composition===!1&&(this.input.value=``,this.span.innerHTML=``,this.input.style.width=`0`,isMacOS()||this.editor.emitInputString(e.data))}),this.input.addEventListener(`paste`,async e=>{e.preventDefault();let t=e.clipboardData||window.Clipboard.data,n=t?.getData(`text`)??t?.getData(`text/plain`);if(n&&n.length>0){this.editor.emitInputString(n);return}try{if(navigator.clipboard?.readText){let e=await navigator.clipboard.readText();if(e&&e.length>0){this.editor.emitInputString(e);return}}}catch(e){console.warn(`clipboard.readText() failed:`,e)}alert(`Paste failed (permission/environment restriction`)}),this.input.addEventListener(`keydown`,e=>{if(!isPasteKeyEvent(e)&&!(e.isComposing||this.composition)&&e.key!==`Process`){if(this.ignoreKeydownAfterCompositionend&&(isSafari||isMacOS())){e.preventDefault(),this.ignoreKeydownAfterCompositionend=!1;return}if(!(!isMacOS()&&!e.ctrlKey&&!e.altKey&&e.key.length===1)&&(e.preventDefault(),e.isComposing!==!0&&e.code!==``))return setTimeout(()=>{this.composition||(this.editor.emitInput(e),this.input.value=``)},0),!1}}),this.input.addEventListener(`compositionstart`,e=>{this.composition=!0,this.span.innerHTML=this.input.value,this.input.style.width=this.span.offsetWidth+`px`}),this.input.addEventListener(`compositionupdate`,e=>{this.span.innerHTML=e.data,this.input.style.width=this.span.offsetWidth+`px`}),this.input.addEventListener(`compositionend`,e=>{this.composition=!1,this.editor.emitInputString(this.input.value),this.input.value=``,this.span.innerHTML=this.input.value,this.input.style.width=`0`,this.ignoreKeydownAfterCompositionend=!0}),document.body.appendChild(this.input),document.body.appendChild(this.span),this.input.focus()}finalize(){document.body.removeChild(this.input),document.body.removeChild(this.span)}move(e,t){let[n,r]=this.editor.getDisplayRectangle();this.span.style.top=r+t+`px`,this.span.style.left=n+e+`px`,this.input.style.top=this.span.offsetTop+`px`,this.input.style.left=this.span.offsetLeft+`px`}updateForeground(e){this.span.style.color=e}updateBackground(e){this.span.style.backgroundColor=e}},MessageTable=class{constructor(){this.map=new Map}register(e,t){for(let n in t){let r=t[n];this.map.set(n,r),e.on(n,r)}}get(e){return this.map.get(e)}};function getDisplayRectangleDefault(){return[0,0,window.innerWidth,window.innerHeight]}var Editor=class{constructor({getDisplayRectangle:e=getDisplayRectangleDefault,fontName:t,fontSize:n,url:r,onExit:i,onClosed:a}){this.getDisplayRectangle=e,this.option=new Option({fontName:t,fontSize:n}),this.onExit=i,this.input=new Input(this),this.cursors=new Map,this.cursorOverlay=document.createElement(`div`),this.cursorOverlay.className=`lem-cursor`,this.cursorOverlay.style.width=this.option.fontWidth+`px`,this.cursorOverlay.style.height=this.option.fontHeight+`px`,this.cursorOverlay.style.backgroundColor=`#ffffff`,this.cursorType=`box`,this.viewMap=new Map,this.jsonrpc=new JSONRPC(r,{onClosed:()=>{a()}}),this.messageTable=new MessageTable,this.messageTable.register(this.jsonrpc,{"update-foreground":this.updateForeground.bind(this),"update-background":this.updateBackground.bind(this),"make-view":this.makeView.bind(this),"delete-view":this.deleteView.bind(this),"resize-view":this.resize.bind(this),"move-view":this.move.bind(this),"redraw-view-after":this.redrawViewAfter.bind(this),clear:this.clear.bind(this),"clear-eol":this.clearEol.bind(this),"clear-eob":this.clearEob.bind(this),put:this.put.bind(this),"put-image":this.putImage.bind(this),"modeline-put":this.modelinePut.bind(this),"draw-block":this.drawBlock.bind(this),"modeline-draw-block":this.modelineDrawBlock.bind(this),"update-display":this.updateDisplay.bind(this),"move-cursor":this.moveCursor.bind(this),"change-view":this.changeView.bind(this),"resize-display":this.resizeDisplay.bind(this),bulk:this.bulk.bind(this),exit:this.exitEditor.bind(this),"get-clipboard-text":this.getClipboardText.bind(this),"set-clipboard-text":this.setClipboardText.bind(this),"js-eval":this.jsEval.bind(this),"set-font":this.setFont.bind(this),"get-font":this.getFont.bind(this),"get-display-size":this.getDisplaySize.bind(this),"load-css":this.loadCSS.bind(this),"update-cursor-shape":this.updateCursorShape.bind(this)}),this.login(),this.boundedHandleResize=this.handleResize.bind(this),this.focusHiddenInput=this.focusHiddenInput.bind(this)}init(){window.addEventListener(`resize`,this.boundedHandleResize),document.getElementsByTagName(`html`)[0].style[`background-color`]=`#333`,getLemEditorElement().appendChild(this.cursorOverlay)}finalize(){window.removeEventListener(`resize`,this.boundedHandleResize),this.input.finalize(),this.cursorOverlay.parentNode&&this.cursorOverlay.parentNode.removeChild(this.cursorOverlay)}closeConnection(){this.jsonrpc.close()}emitInput(e){let t=convertKeyEvent(e);if(t){if(t.key===`]`&&t.ctrl&&!t.meta&&!t.super&&!t.shift){this.jsonrpc.notify(`input`,{kind:`abort`});return}t.key!==`Unidentified`&&this.jsonrpc.notify(`input`,{kind:`key`,value:t})}}emitInputString(e){e?this.jsonrpc.notify(`input`,{kind:`input-string`,value:e}):console.error(`unexpected argument`,e)}redrawParams(){return{size:this.getDisplaySize(),fontWidth:this.option.fontWidth,fontHeight:this.option.fontHeight,fontAscent:this.option.fontAscent,fontSize:this.option.fontSize}}handleResize(e){this.jsonrpc.notify(`redraw`,this.redrawParams())}focusHiddenInput(){let e=this.input?.input;if(e){try{window.focus()}catch{}requestAnimationFrame(()=>{setTimeout(()=>{e.focus({preventScroll:!0})},0)})}}sendNotification(e,t){this.jsonrpc.notify(e,t)}request(e,t,n){this.jsonrpc.request(e,t,n)}getDisplaySize(){let[e,t,n,r]=this.getDisplayRectangle();return{width:Math.floor(n/this.option.fontWidth),height:Math.floor(r/this.option.fontHeight)}}callMessage(e,t){this.messageTable.get(e)(t)}findViewById(e){return this.viewMap.get(e)}login(){this.jsonrpc.request(`login`,{size:this.getDisplaySize(),foreground:this.option.foreground,background:this.option.background,fontWidth:this.option.fontWidth,fontHeight:this.option.fontHeight,fontAscent:this.option.fontAscent,fontSize:this.option.fontSize},e=>{if(this.updateForeground(e.foreground),this.updateBackground(e.background),e.views)for(let t of e.views)this.makeView(t);this.jsonrpc.notify(`redraw`,this.redrawParams())})}updateForeground(e){e!=null&&(this.option.foreground=e,this.input.updateForeground(e))}updateBackground(e){if(e==null)return;this.option.background=e,this.input.updateBackground(e);let t=getLemEditorElement();t.style.backgroundColor=e}makeView({id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,use_modeline:l,kind:u,type:d,content:f,border:p,border_shape:m}){let h=new View({option:this.option,id:e,x:t,y:n,width:r,height:i,pixelX:a,pixelY:o,pixelWidth:s,pixelHeight:c,useModeline:l,kind:u,type:d,content:f,border:p,borderShape:m,editor:this});this.viewMap.set(e,h)}deleteView({viewInfo:{id:e}}){this.findViewById(e).delete(),this.viewMap.delete(e)}resize({viewInfo:{id:e},width:t,height:n,pixelWidth:r,pixelHeight:i}){let a=this.findViewById(e);a?a.resize(t,n,r,i):console.warn(`resize: view not found for id ${e}`)}move({viewInfo:{id:e},x:t,y:n,pixelX:r,pixelY:i}){let a=this.findViewById(e);a?a.move(t,n,r,i):console.warn(`move: view not found for id ${e}`)}redrawViewAfter({viewInfo:{id:e},isActive:t}){this.findViewById(e).touch(t)}clear({viewInfo:{id:e}}){this.findViewById(e).clear()}clearEol({viewInfo:{id:e},x:t,y:n,height:r}){this.findViewById(e).clearEol(t,n,r)}clearEob({viewInfo:{id:e},x:t,y:n}){this.findViewById(e).clearEob(t,n)}put({viewInfo:{id:e},x:t,y:n,text:r,textWidth:i,attribute:a,font:o,backgroundY:s,backgroundHeight:c}){this.findViewById(e).print(t,n,r,i,a,o,s,c)}drawBlock({viewInfo:{id:e},x:t,y:n,width:r,height:i,color:a}){this.findViewById(e).drawBlock(t,n,r,i,a)}modelineDrawBlock({viewInfo:{id:e},x:t,y:n,width:r,height:i,color:a}){this.findViewById(e).drawBlockOnModeline(t,n,r,i,a)}putImage({viewInfo:{id:e},x:t,y:n,pixelWidth:r,pixelHeight:i,clipWidth:a,clipHeight:o,url:s}){this.findViewById(e).printImage(t,n,r,i,a,o,s)}modelinePut({viewInfo:{id:e},x:t,y:n,text:r,textWidth:i,attribute:a,backgroundY:o,backgroundHeight:s}){this.findViewById(e).printToModeline(t,n,r,i,a,o,s)}updateDisplay(){}moveCursor({viewInfo:{id:e},x:t,y:n,color:r,cursorText:i,cursorForeground:a}){let o=this.findViewById(e),[s,c]=this.getDisplayRectangle(),l=o.pixelX+t,u=o.pixelY+n;this.input.move(l,u);let d=r||this.option.foreground,f=a||this.option.background,p=this.cursorOverlay;switch(this.cursorType){case`bar`:p.style.left=s+l+`px`,p.style.top=c+u+`px`,p.style.width=`2px`,p.style.height=this.option.fontHeight+`px`,p.style.backgroundColor=d,p.textContent=``,p.style.color=``,p.style.font=``,p.style.paddingTop=``;break;case`underline`:p.style.left=s+l+`px`,p.style.top=c+u+this.option.fontHeight-2+`px`,p.style.width=this.option.fontWidth+`px`,p.style.height=`2px`,p.style.backgroundColor=d,p.textContent=``,p.style.color=``,p.style.font=``,p.style.paddingTop=``;break;default:p.style.left=s+l+`px`,p.style.top=c+u+`px`,p.style.width=this.option.fontWidth+`px`,p.style.height=this.option.fontHeight+`px`,p.style.backgroundColor=d,p.style.font=this.option.font,p.style.paddingTop=textOffsetY+`px`,p.textContent=i||``,p.style.color=f;break}p.style.animation=`none`,p.offsetHeight,p.style.animation=``}updateCursorShape({cursorType:e}){this.cursorType=e||`box`}changeView({viewInfo:{id:e},type:t,content:n}){let r=this.findViewById(e);switch(t){case`html`:r.changeToHTMLContent(n);break;case`editor`:r.changeToEditorContent();break}}resizeDisplay({width:e,height:t}){let n=getLemEditorElement();n.style.width=Math.floor(e*this.option.fontWidth)+`px`,n.style.height=Math.floor(t*this.option.fontHeight)+`px`}bulk(e){for(let{method:t,argument:n}of e)this.callMessage(t,n)}exitEditor(){this.onExit&&this.onExit()}getClipboardText(){navigator.clipboard?.readText().then(e=>{this.jsonrpc.notify(`got-clipboard-text`,{text:e})})}setClipboardText({text:e}){navigator.clipboard&&navigator.clipboard.writeText(e)}jsEval({viewInfo:{id:e},code:t}){let n=this.findViewById(e).evalIn(t);return n&&n.toString()}setFont({fontName:e,fontSize:t}){this.option.setFont(e||this.option.fontName,t||this.option.fontSize),this.cursorOverlay.style.width=this.option.fontWidth+`px`,this.cursorOverlay.style.height=this.option.fontHeight+`px`,this.jsonrpc.notify(`redraw`,this.redrawParams())}getFont(){return{name:this.option.fontName,size:this.option.fontSize}}loadCSS({content:e}){let t=document.createElement(`style`);t.textContent=e,document.head.appendChild(t)}notifyToServer(e,t){this.jsonrpc.notify(`invoke`,{method:e,args:t})}},canvas=document.querySelector(`#editor`);async function main(){await Promise.all([document.fonts.load(`19px file-icons`),document.fonts.load(`19px AllTheIcons`),document.fonts.load(`19px fontawesome`),document.fonts.load(`19px material-design-icons`),document.fonts.load(`19px octicons`)]),await document.fonts.ready;let e=new Editor({canvas,fontName:`Monospace`,fontSize:18,url:`${window.location.protocol===`https:`?`wss`:`ws`}://${window.location.hostname}:${window.location.port}`,onExit:null,onClosed:null});window.addEventListener(`message`,t=>{t.data.type===`invoke-lem`&&e.notifyToServer(t.data.method,t.data.args)}),e.init()}main(); \ No newline at end of file diff --git a/frontends/server/frontend/editor.js b/frontends/server/frontend/editor.js index 1eaf87557..bda811abd 100644 --- a/frontends/server/frontend/editor.js +++ b/frontends/server/frontend/editor.js @@ -1332,6 +1332,7 @@ export class Editor { fontWidth: this.option.fontWidth, fontHeight: this.option.fontHeight, fontAscent: this.option.fontAscent, + fontSize: this.option.fontSize, }; } @@ -1383,6 +1384,7 @@ export class Editor { fontWidth: this.option.fontWidth, fontHeight: this.option.fontHeight, fontAscent: this.option.fontAscent, + fontSize: this.option.fontSize, }, (response) => { this.updateForeground(response.foreground); this.updateBackground(response.background); diff --git a/frontends/server/main.lisp b/frontends/server/main.lisp index 316b531a2..12afdddac 100644 --- a/frontends/server/main.lisp +++ b/frontends/server/main.lisp @@ -110,7 +110,10 @@ :accessor jsonrpc-cell-height) ;; how far below a cell's top the client puts the text baseline. (cell-ascent :initform nil - :accessor jsonrpc-cell-ascent)) + :accessor jsonrpc-cell-ascent) + ;; the font's own size. the cell height is measured from the glyph bounding box, so it is larger. + (font-em :initform nil + :accessor jsonrpc-font-em)) (:default-initargs :name :jsonrpc :redraw-after-modifying-floating-window t @@ -173,7 +176,8 @@ returns true when one of them changed, since nothing already measured survives a (setf changed t))))) (update "fontWidth" 'jsonrpc-cell-width) (update "fontHeight" 'jsonrpc-cell-height) - (update "fontAscent" 'jsonrpc-cell-ascent)) + (update "fontAscent" 'jsonrpc-cell-ascent) + (update "fontSize" 'jsonrpc-font-em)) changed)) (defun handle-login (jsonrpc logged-in-callback params) @@ -509,6 +513,9 @@ returns true when one of them changed, since nothing already measured survives a (jsonrpc-cell-height jsonrpc) (jsonrpc-cell-ascent jsonrpc))) +(defmethod lem-if:font-em-pixels ((jsonrpc jsonrpc)) + (jsonrpc-font-em jsonrpc)) + (defun call (method params) (let ((mailbox (sb-concurrency:make-mailbox :name "lem-server-call-async"))) (loop :for connection diff --git a/src/interface.lisp b/src/interface.lisp index 9be5c1305..2a059935d 100644 --- a/src/interface.lisp +++ b/src/interface.lisp @@ -192,6 +192,12 @@ Always pixels, unlike `cell-width' / `cell-height', which are 1 on a cell-based (:method (implementation) (values nil nil nil))) +(defgeneric lem-if:font-em-pixels (implementation) + (:documentation "Pixels one em of the editor font takes, or NIL when the frontend cannot say. +In the same units as `cell-pixel-size'. +The em is smaller than `cell-height', which is measured from the glyph bounding box.") + (:method (implementation) nil)) + (defgeneric lem-if:render-row (implementation view row) (:documentation "Draw ROW, one screen row of VIEW, replacing whatever it held before. ROW is a `lem-core/display:row'. Its height, background and the position of every object on it were diff --git a/src/internal-packages.lisp b/src/internal-packages.lisp index c463751a4..2ebda9bbd 100644 --- a/src/internal-packages.lisp +++ b/src/internal-packages.lisp @@ -843,6 +843,7 @@ :cell-width :cell-height :cell-pixel-size + :font-em-pixels :clear-to-end-of-window :js-eval :render-row From 064758bc6627b47003dc58a1512cb19e7ae7ef38 Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Tue, 4 Aug 2026 20:20:04 +0300 Subject: [PATCH 24/26] fix theme colors not reaching get-foreground-color/get-background-color --- frontends/server/main.lisp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontends/server/main.lisp b/frontends/server/main.lisp index 12afdddac..bacb30640 100644 --- a/frontends/server/main.lisp +++ b/frontends/server/main.lisp @@ -281,10 +281,14 @@ returns true when one of them changed, since nothing already measured survives a (defmethod lem-if:update-foreground ((jsonrpc jsonrpc) color-name) (with-error-handler () + (alexandria:when-let (color (lem:parse-color color-name)) + (setf (jsonrpc-foreground-color jsonrpc) color)) (notify jsonrpc "update-foreground" color-name))) (defmethod lem-if:update-background ((jsonrpc jsonrpc) color-name) (with-error-handler () + (alexandria:when-let (color (lem:parse-color color-name)) + (setf (jsonrpc-background-color jsonrpc) color)) (notify jsonrpc "update-background" color-name))) (defmethod lem-if:update-cursor-shape ((jsonrpc jsonrpc) cursor-type) From 7a50d2a04c517980a136535e5020c26bed847475 Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Wed, 5 Aug 2026 00:34:34 +0300 Subject: [PATCH 25/26] let virtual text carry an attribute per run --- src/display/logical-line.lisp | 62 +++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/src/display/logical-line.lisp b/src/display/logical-line.lisp index 5c93079c1..a8d9a14a2 100644 --- a/src/display/logical-line.lisp +++ b/src/display/logical-line.lisp @@ -6,8 +6,17 @@ "a display-only string fragment injected at a character position within a logical line." ;; 0-based position in the line's string where this fragment is inserted charpos - string - attribute) + ;; list of (string attribute) runs, drawn in order. see `virtual-text-runs'. + runs) + +(defun virtual-text-runs (spec) + "an overlay's :before-string / :after-string as a list of (string attribute) runs. +SPEC is a bare string, a single (string attribute) pair, or a list of such pairs" + (cond ((stringp spec) (list (list spec nil))) + ((not (consp spec)) nil) + ((stringp (first spec)) (list (list (first spec) (second spec)))) + (t (loop :for (run-string run-attribute) :in spec + :collect (list run-string run-attribute))))) (defstruct logical-line string @@ -378,18 +387,14 @@ several folds that each hide arbitrary character ranges across multiple buffer l :for before-str := (overlay-get overlay :before-string) :for after-str := (overlay-get overlay :after-string) :do (when (and before-str (start-in-line-p overlay)) - (let ((bs (alexandria:ensure-list before-str))) - (push (make-virtual-item :charpos (overlay-start-charpos overlay) - :string (first bs) - :attribute (second bs)) - virtual-items))) + (push (make-virtual-item :charpos (overlay-start-charpos overlay) + :runs (virtual-text-runs before-str)) + virtual-items)) (when (and after-str (end-in-line-p overlay)) - (let ((as (alexandria:ensure-list after-str))) - (push (make-virtual-item :charpos (or (overlay-end-charpos overlay) - (length string)) - :string (first as) - :attribute (second as)) - virtual-items)))) + (push (make-virtual-item :charpos (or (overlay-end-charpos overlay) + (length string)) + :runs (virtual-text-runs after-str)) + virtual-items))) ;; markers were positioned in raw coordinates; remap them into the ;; spliced string so several folds on one visual line stay anchored. (dolist (vi virtual-items) @@ -409,8 +414,7 @@ several folds that each hide arbitrary character ranges across multiple buffer l :when (>= (virtual-item-charpos vi) charpos) :collect (make-virtual-item :charpos (- (virtual-item-charpos vi) charpos) - :string (virtual-item-string vi) - :attribute (virtual-item-attribute vi)))))) + :runs (virtual-item-runs vi)))))) (make-logical-line :string string :attributes attributes @@ -522,19 +526,21 @@ VIRTUAL-ITEMS arrive in draw order (from `create-logical-line')." (items)) (flet ((add-virtuals-at (pos) (loop :while (and pending (= (virtual-item-charpos (first pending)) pos)) - :do (let ((vi (pop pending))) - ;; a newline ends the screen row rather than being drawn, so the segments - ;; around it become items with a break between them. - (loop :for segment :in (uiop:split-string (virtual-item-string vi) - :separator '(#\newline)) - :for firstp := t :then nil - :do (unless firstp - (setf items (cons (make-line-break-item) items))) - (setf items (add-or-merge-item - (make-string-with-attribute-item - :string segment - :attribute (virtual-item-attribute vi)) - items))))))) + ;; runs follow one another on the same row, each keeping its own attribute. + :do (loop :for (run-string run-attribute) :in (virtual-item-runs (pop pending)) + ;; a newline ends the screen row rather than being drawn, so the + ;; segments around it become items with a break between them. + :do (loop :for segment :in (uiop:split-string + run-string + :separator '(#\newline)) + :for firstp := t :then nil + :do (unless firstp + (setf items (cons (make-line-break-item) items))) + (setf items (add-or-merge-item + (make-string-with-attribute-item + :string segment + :attribute run-attribute) + items))))))) ;; walk segments between break positions, injecting virtual items at each boundary (loop :for (pos . rest) :on positions :while rest From b76d8fac4e0526720e73b1fecb9b2104984efc41 Mon Sep 17 00:00:00 2001 From: mahmoodsheikh36 Date: Mon, 10 Aug 2026 12:33:56 +0300 Subject: [PATCH 26/26] add image-support-p field to frontend definitions --- frontends/sdl2/sdl2.lisp | 3 ++- frontends/server/main.lisp | 3 ++- src/interface.lisp | 7 ++++++- src/internal-packages.lisp | 1 + 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/frontends/sdl2/sdl2.lisp b/frontends/sdl2/sdl2.lisp index 0ed1b3333..50bf770c5 100644 --- a/frontends/sdl2/sdl2.lisp +++ b/frontends/sdl2/sdl2.lisp @@ -8,6 +8,7 @@ (:default-initargs :name :sdl2 :redraw-after-modifying-floating-window nil - :underline-color-support t)) + :underline-color-support t + :image-support t)) (pushnew :lem-sdl2 *features*) diff --git a/frontends/server/main.lisp b/frontends/server/main.lisp index bacb30640..0e1d59312 100644 --- a/frontends/server/main.lisp +++ b/frontends/server/main.lisp @@ -122,7 +122,8 @@ :html-support t :underline-color-support t :no-force-needed t - :support-pixel-positioning t)) + :support-pixel-positioning t + :image-support t)) (defun view-id-hash (view) "Return a minimal hash table containing only the view ID. diff --git a/src/interface.lisp b/src/interface.lisp index 2a059935d..8a7f5d7bf 100644 --- a/src/interface.lisp +++ b/src/interface.lisp @@ -44,7 +44,12 @@ When rendering the DOM and a window in a one-to-one manner, no redraw is require :initform nil :initarg :support-pixel-positioning :reader support-pixel-positioning-p - :documentation "When true, the frontend supports pixel-based floating window positioning."))) + :documentation "When true, the frontend supports pixel-based floating window positioning.") + (image-support + :initform nil + :initarg :image-support + :reader image-support-p + :documentation "When true, the frontend can draw an image object."))) (defun get-default-implementation (&key implementation) (let ((classes (c2mop:class-direct-subclasses (find-class 'implementation))) diff --git a/src/internal-packages.lisp b/src/internal-packages.lisp index 2ebda9bbd..c55af8784 100644 --- a/src/internal-packages.lisp +++ b/src/internal-packages.lisp @@ -701,6 +701,7 @@ :support-pixel-positioning-p :html-support-p :underline-color-support-p + :image-support-p :no-force-needed-p :set-foreground :set-background