;;; eaf-browser.el --- Browser plugins ;; Filename: eaf-browser.el ;; Description: Browser plugins ;; Author: Andy Stewart ;; Maintainer: Andy Stewart ;; Copyright (C) 2021, Andy Stewart, all rights reserved. ;; Created: 2021-07-20 22:30:28 ;; Version: 0.1 ;; Last-Updated: Sat Dec 11 14:47:23 2021 (-0500) ;; By: Mingde (Matthew) Zeng ;; URL: http://www.emacswiki.org/emacs/download/eaf-browser.el ;; Keywords: ;; Compatibility: GNU Emacs 28.0.50 ;; ;; Features that might be required by this library: ;; ;; ;; ;;; This file is NOT part of GNU Emacs ;;; License ;; ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 3, or (at your option) ;; any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth ;; Floor, Boston, MA 02110-1301, USA. ;;; Commentary: ;; ;; Browser plugins ;; ;;; Installation: ;; ;; Put eaf-browser.el to your load-path. ;; The load-path is usually ~/elisp/. ;; It's set in your ~/.emacs like this: ;; (add-to-list 'load-path (expand-file-name "~/elisp")) ;; ;; And the following to your ~/.emacs startup file. ;; ;; (require 'eaf-browser) ;; ;; No need more. ;;; Customize: ;; ;; ;; ;; All of the above can customize by: ;; M-x customize-group RET eaf-browser RET ;; ;;; Change log: ;; ;; 2021/07/20 ;; * First released. ;; ;;; Acknowledgements: ;; ;; ;; ;;; TODO ;; ;; ;; ;;; Require ;;; Code: (defgroup eaf-browser nil "The Browser application of Emacs application framework." :group 'eaf) (defcustom eaf-browser-search-engines `(("google" . "http://www.google.com/search?ie=utf-8&oe=utf-8&q=%s") ("duckduckgo" . "https://duckduckgo.com/?q=%s") ("bing" . "https://bing.com/search?q=%s")) "The default search engines offered by EAF. Each element has the form (NAME . URL). NAME is a search engine name, as a string. URL pecifies the url format for the search engine. It should have a %s as placeholder for search string." :type '(alist :key-type (string :tag "Search engine name") :value-type (string :tag "Search engine url"))) (defcustom eaf-browser-default-search-engine "google" "The default search engine used by `eaf-open-browser' and `eaf-search-it'. It must defined at `eaf-browser-search-engines'." :type 'string) (defcustom eaf-browser-extension-list '("html" "htm") "The extension list of browser application." :type 'cons) (defcustom eaf-browser-continue-where-left-off nil "Similar to Chromium's Setting -> On start-up -> Continue where you left off. If non-nil, all active EAF Browser buffers will be saved before Emacs is killed, and will re-open them when calling `eaf-browser-restore-buffers' in the future session." :type 'boolean) (defcustom eaf-browser-fullscreen-move-cursor-corner nil "If non-nil, move the mouse cursor to the corner when fullscreen in the browser." :type 'boolean) (defcustom eaf-browser-enable-plugin t "If non-nil, enable QtWebEngine plugins for EAF Browser." :type 'boolean) (defcustom eaf-browser-enable-adblocker nil "If non-nil, enable adblocker for EAF Browser." :type 'boolean) (defcustom eaf-browser-enable-autofill nil "If non-nil, enable autofill password for EAF Browser." :type 'boolean) (defcustom eaf-browser-enable-javascript t "If non-nil, enable javascript for EAF Browser." :type 'boolean) (defcustom eaf-browser-enable-scrollbar nil "If non-nil, enable scroll bar for EAF Browser." :type 'boolean) (defcustom eaf-browser-remember-history t "If non-nil, remember browsing history for EAF Browser. The history file is stored in .emacs.d/eaf/browser/history/log.txt" :type 'boolean) (defcustom eaf-browser-ignore-history-list '("google.com/search") "A list of case insensitive regexp URL to ignore when saving EAF Browser history." :type 'cons) (defcustom eaf-browser-default-zoom 1.0 "Set the default zoom factor for EAF Browser." :type 'float) (defcustom eaf-browser-font-family "" "Set font family for EAF Browser." :type 'string) (defcustom eaf-browser-progress-bar-height "2" "Set progress bar height for EAF Browser." :type 'int) (defcustom eaf-browser-progress-bar-color (eaf-get-theme-foreground-color) "Color of progress bar in hex code `#hhhhhh'. Default is the foreground color of EAF buffer." :type 'string) (defcustom eaf-browser-text-selection-color "auto" "Possible values are `auto' or a hex code `#hhhhhh' of a color. If it is set to `auto', then the selection color will be automatically configured by darkreader.js." :type 'string) (defcustom eaf-browser-blank-page-url "https://www.google.com" "Set the blank page url for EAF Browser." :type 'string) (defcustom eaf-browser-download-path "~/Downloads" "Set the download path for EAF Browser." :type 'string) (defcustom eaf-browser-aria2-proxy-host "" "Set proxy host for aria2 downloader for EAF Browser." :type 'string) (defcustom eaf-browser-aria2-proxy-port "" "Set proxy port for aria2 downloader for EAF Browser." :type 'string) (defcustom eaf-browser-aria2-auto-file-renaming nil "If non-nil, aria2 downloader will auto rename files for EAF Browser." :type 'boolean) (defcustom eaf-browser-dark-mode "follow" "Configure the dark mode setting for EAF Browser. Options: - \"follow\" to follow Emacs theme - \"force\" to force dark mode - nil to disable dark mode" :type 'string) (defcustom eaf-browser-pc-user-agent "Mozilla/5.0 (X11; Linux x86_64; rv:85.0) Gecko/20100101 Firefox/85.0" "Simulate a PC User-Agent for EAF Browser." :type 'string) (defcustom eaf-browser-phone-user-agent "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A5370a Safari/604.1" "Simulate a Phone User-Agent for EAF Browser." :type 'string) (defcustom eaf-browser-chrome-history-file "~/.config/google-chrome/Default/History" "Set the chrome history file when exporting chrome history." :type 'string) (defcustom eaf-browser-translate-language "" "EAF browser will use current system locale if this option is empty" :type 'string) (defcustom eaf-browser-unknown-url-scheme-policy "AllowUnknownUrlSchemesFromUserInteraction" "Allowed options: DisallowUnknownUrlSchemes, AllowUnknownUrlSchemesFromUserInteraction, or AllowAllUnknownUrlSchemes." :type 'string) (defcustom eaf-browser-reader-mode-style "" "The string of css style used by reader-mode." :type 'string) (defcustom eaf-chrome-bookmark-file "~/.config/google-chrome/Default/Bookmarks" "The default chrome bookmark file to import." :type 'string) (defcustom eaf-browser-caret-mode-keybinding '(("j" . "caret_next_line") ("k" . "caret_previous_line") ("l" . "caret_next_character") ("h" . "caret_previous_character") ("w" . "caret_next_word") ("b" . "caret_previous_word") (")" . "caret_next_sentence") ("(" . "caret_previous_sentence") ("g" . "caret_to_bottom") ("G" . "caret_to_top") ("/" . "caret_search_forward") ("?" . "caret_search_backward") ("." . "caret_clear_search") ("v" . "caret_toggle_mark") ("o" . "caret_rotate_selection") ("y" . "caret_translate_text") ("q" . "caret_exit") ("C-n" . "caret_next_line") ("C-p" . "caret_previous_line") ("C-f" . "caret_next_character") ("C-b" . "caret_previous_character") ("M-f" . "caret_next_word") ("M-b" . "caret_previous_word") ("M-e" . "caret_next_sentence") ("M-a" . "caret_previous_sentence") ("C-<" . "caret_to_bottom") ("C->" . "caret_to_top") ("C-s" . "caret_search_forward") ("C-r" . "caret_search_backward") ("C-." . "caret_clear_search") ("C-SPC" . "caret_toggle_mark") ("C-o" . "caret_rotate_selection") ("C-y" . "caret_translate_text") ("C-q" . "caret_exit") ("c" . "insert_or_caret_at_line") ("M-c" . "caret_toggle_browsing") ("" . "caret_exit")) "The keybinding of EAF Browser Caret Mode." :type 'cons) (defcustom eaf-browser-keybinding '(("C--" . "zoom_out") ("C-=" . "zoom_in") ("C-0" . "zoom_reset") ("C-s" . "search_text_forward") ("C-r" . "search_text_backward") ("C-n" . "scroll_up") ("C-p" . "scroll_down") ("C-f" . "scroll_right") ("C-b" . "scroll_left") ("C-v" . "scroll_up_page") ("C-y" . "yank_text") ("C-w" . "kill_text") ("M-e" . "atomic_edit") ("M-c" . "caret_toggle_browsing") ("M-D" . "select_text") ("M-s" . "open_link") ("M-S" . "open_link_new_buffer") ("M-B" . "open_link_background_buffer") ("C-/" . "undo_action") ("M-_" . "redo_action") ("M-w" . "copy_text") ("M-f" . "history_forward") ("M-b" . "history_backward") ("M-q" . "clear_cookies") ("C-t" . "toggle_password_autofill") ("C-d" . "save_page_password") ("M-a" . "toggle_adblocker") ("C-M-q" . "clear_history") ("C-M-i" . "import_chrome_history") ("M-v" . "scroll_down_page") ("M-<" . "scroll_to_begin") ("M->" . "scroll_to_bottom") ("M-p" . "duplicate_page") ("M-t" . "new_blank_page") ("M-d" . "toggle_dark_mode") ("SPC" . "insert_or_scroll_up_page") ("J" . "insert_or_select_left_tab") ("K" . "insert_or_select_right_tab") ("j" . "insert_or_scroll_up") ("k" . "insert_or_scroll_down") ("h" . "insert_or_scroll_left") ("l" . "insert_or_scroll_right") ("f" . "insert_or_open_link") ("F" . "insert_or_open_link_new_buffer") ("O" . "insert_or_open_link_new_buffer_other_window") ("B" . "insert_or_open_link_background_buffer") ("c" . "insert_or_caret_at_line") ("u" . "insert_or_scroll_down_page") ("d" . "insert_or_scroll_up_page") ("H" . "insert_or_history_backward") ("L" . "insert_or_history_forward") ("t" . "insert_or_new_blank_page") ("T" . "insert_or_recover_prev_close_page") ("i" . "insert_or_focus_input") ("I" . "insert_or_open_downloads_setting") ("r" . "insert_or_refresh_page") ("g" . "insert_or_scroll_to_begin") ("x" . "insert_or_close_buffer") ("G" . "insert_or_scroll_to_bottom") ("-" . "insert_or_zoom_out") ("=" . "insert_or_zoom_in") ("0" . "insert_or_zoom_reset") ("m" . "insert_or_save_as_bookmark") ("o" . "insert_or_open_browser") ("y" . "insert_or_download_youtube_video") ("Y" . "insert_or_download_youtube_audio") ("p" . "insert_or_toggle_device") ("P" . "insert_or_duplicate_page") ("1" . "insert_or_save_as_pdf") ("2" . "insert_or_save_as_single_file") ("3" . "insert_or_save_as_screenshot") ("v" . "insert_or_view_source") ("e" . "insert_or_edit_url") ("n" . "insert_or_export_text") ("," . "insert_or_switch_to_reader_mode") ("." . "insert_or_translate_text") (";" . "insert_or_translate_page") ("C-M-c" . "copy_code") ("C-M-l" . "copy_link") ("C-a" . "select_all_or_input_text") ("M-u" . "clear_focus") ("C-j" . "open_downloads_setting") ("M-o" . "eval_js") ("M-O" . "eval_js_file") ("" . "eaf-browser-send-esc-or-exit-fullscreen") ("M-," . "eaf-send-down-key") ("M-." . "eaf-send-up-key") ("M-m" . "eaf-send-return-key") ("" . "refresh_page") ("" . "open_devtools") ("" . "eaf-send-ctrl-return-sequence") ("C-" . "eaf-send-ctrl-left-sequence") ("C-" . "eaf-send-ctrl-right-sequence") ("C-" . "eaf-send-ctrl-delete-sequence") ("C-" . "eaf-send-ctrl-backspace-sequence") ) "The keybinding of EAF Browser." :type 'cons) (defcustom eaf-browser-key-alias '(("C-a" . "") ("C-e" . "")) "The key alias of EAF Browser." :type 'cons) (defun eaf--browser-update-position (position-percentage) "Format mode line position indicator to show the current position in percentage." (setq-local mode-line-position `(,position-percentage)) (force-mode-line-update)) (defun eaf-browser-restore-buffers () "EAF restore all opened EAF Browser buffers in the previous Emacs session. This should be used after setting `eaf-browser-continue-where-left-off' to t." (interactive) (if eaf-browser-continue-where-left-off (let* ((browser-restore-file-path (concat eaf-config-location (file-name-as-directory "browser") (file-name-as-directory "history") "restore.txt")) (browser-url-list (with-temp-buffer (insert-file-contents browser-restore-file-path) (split-string (buffer-string) "\n" t)))) (if (eaf-epc-live-p eaf-epc-process) (dolist (url browser-url-list) (eaf-open-browser url)) (dolist (url browser-url-list) (push `(,url "browser" "") eaf--active-buffers)) (when eaf--active-buffers (eaf-open-browser (nth 0 (car eaf--active-buffers)))))) (user-error "Please set `eaf-browser-continue-where-left-off' to t first!"))) (defun eaf--browser-bookmark () "Restore EAF buffer according to browser bookmark from the current file path or web URL." `((handler . eaf--bookmark-restore) (eaf-app . "browser") (defaults . ,(list eaf--bookmark-title)) (filename . ,(eaf-get-path-or-url)))) (defun eaf--browser-chrome-bookmark (name url) "Restore EAF buffer according to chrome bookmark of given title and web URL." `((handler . eaf--bookmark-restore) (eaf-app . "browser") (defaults . ,(list name)) (filename . ,url))) (defun eaf--browser-bookmark-restore (bookmark) (eaf-open-browser (cdr (assq 'filename bookmark)))) (defalias 'eaf--browser-firefox-bookmark 'eaf--browser-chrome-bookmark) (defvar eaf--firefox-bookmarks nil "Bookmarks that should be imported from firefox.") (defun eaf--useful-firefox-bookmark? (uri) "Check whether uri is a website url." (or (string-prefix-p "http://" uri) (string-prefix-p "https://" uri))) (defun eaf--firefox-bookmark-to-import? (title uri) "Check whether uri should be imported." (when (eaf--useful-firefox-bookmark? uri) (let ((old (gethash uri eaf--existing-bookmarks))) (when (or (not old) (and (string-equal old "") (not (string-equal title "")))) t)))) (defun eaf--firefox-bookmark-to-import (title uri) (puthash uri title eaf--existing-bookmarks) (add-to-list 'eaf--firefox-bookmarks (cons uri title))) (defun eaf-import-firefox-bookmarks () "Command to import firefox bookmarks." (interactive) (when (eaf-read-input "In order to import, you should first backup firefox's bookmarks to a json file. Continue?" "yes-or-no" "") (let ((fx-bookmark-file (read-file-name "Choose firefox bookmark file:"))) (if (not (file-exists-p fx-bookmark-file)) (message "Firefox bookmark file: '%s' is not exist." fx-bookmark-file) (setq eaf--firefox-bookmarks nil) (setq eaf--existing-bookmarks (eaf--load-existing-bookmarks)) (let ((orig-bookmark-record-fn bookmark-make-record-function) (data (json-read-file fx-bookmark-file))) (cl-labels ((fn (item) (pcase (alist-get 'typeCode item) (1 (let ((title (alist-get 'title item "")) (uri (alist-get 'uri item))) (when (eaf--firefox-bookmark-to-import? title uri) (eaf--firefox-bookmark-to-import title uri)))) (2 (mapc #'fn (alist-get 'children item)))))) (fn data) (dolist (bm eaf--firefox-bookmarks) (let ((uri (car bm)) (title (cdr bm))) (setq-local bookmark-make-record-function #'(lambda () (eaf--browser-firefox-bookmark title uri))) (bookmark-set title))) (setq-local bookmark-make-record-function orig-bookmark-record-fn) (bookmark-save) (message "Import success."))))))) (defun eaf--create-new-browser-buffer (new-window-buffer-id) "Function for creating a new browser buffer with the specified NEW-WINDOW-BUFFER-ID." (let ((eaf-buffer ;; Create a buffer which name look like first buffer (but ;; different). this can prevent mode-line flicker. the ;; buffer's name will be changed to title when title is ;; ready. (generate-new-buffer (concat (buffer-name (car (buffer-list))) " ")))) (with-current-buffer eaf-buffer (eaf--gen-keybinding-map (eaf--get-app-bindings "browser")) (eaf-mode) (set (make-local-variable 'eaf--buffer-id) new-window-buffer-id) (set (make-local-variable 'eaf--buffer-url) "") (set (make-local-variable 'eaf--buffer-app-name) "browser")) (switch-to-buffer eaf-buffer) ;; When user open new window by click link, we should clean ;; minibuffer's message, for it may be show useless info. (message nil))) (defun eaf-browser--duplicate-page-in-new-tab (url) "Duplicate a new tab for the dedicated URL." (eaf-open (eaf-wrap-url url) "browser" nil t)) (defun eaf-is-valid-web-url (url) "Return the same URL if it is valid." (when (and url ;; URL should not include blank char. (< (length (split-string url)) 2) ;; Use regexp matching URL. (or (and (string-prefix-p "file://" url) (string-suffix-p ".html" url)) ;; Normal url address. (string-match "^\\(https?://\\)?[a-z0-9]+\\([-.][a-z0-9]+\\)*.+\\..+[a-z0-9.]\\{1,6\\}\\(:[0-9]{1,5}\\)?\\(/.*\\)?$" url) ;; Localhost url. (string-match "^\\(https?://\\)?\\(localhost\\|127.0.0.1\\):[0-9]+/?" url))) url)) (defun eaf-wrap-url (url) "Wraps URL with prefix http:// if URL does not include it." (if (or (string-prefix-p "http://" url) (string-prefix-p "https://" url) (string-prefix-p "file://" url) (string-prefix-p "chrome://" url)) url (concat "http://" url))) ;;;###autoload (defun eaf-open-browser-in-background (url &optional args) "Open browser with the specified URL and optional ARGS in background." (setq eaf--monitor-configuration-p nil) (let ((save-buffer (current-buffer))) (eaf-open-browser url args) (switch-to-buffer save-buffer)) (setq eaf--monitor-configuration-p t)) ;;;###autoload (defun eaf-open-browser-with-history () "A wrapper around `eaf-open-browser' that provides browser history candidates. If URL is an invalid URL, it will use `eaf-browser-default-search-engine' to search URL as string literal. This function works best if paired with a fuzzy search package." (interactive) (let* ((browser-history-file-path (concat eaf-config-location (file-name-as-directory "browser") (file-name-as-directory "history") "log.txt")) (history-pattern "^\\(.+\\)ᛝ\\(.+\\)ᛡ\\(.+\\)$") (history-file-exists (file-exists-p browser-history-file-path)) (history (completing-read "[EAF/browser] Search || URL || History: " (if history-file-exists (mapcar (lambda (h) (when (string-match history-pattern h) (format "[%s] ⇰ %s" (match-string 1 h) (match-string 2 h)))) (with-temp-buffer (insert-file-contents browser-history-file-path) (split-string (buffer-string) "\n" t))) nil))) (history-url (eaf-is-valid-web-url (when (string-match "⇰\s\\(.+\\)$" history) (match-string 1 history))))) (cond (history-url (eaf-open-browser history-url)) ((eaf-is-valid-web-url history) (eaf-open-browser history)) (t (eaf-search-it history))))) (defun eaf--create-search-url (search-string &optional search-engine use-user-engine) "Create a search-url for SEARCH-STRING using SEARCH-ENGINE. SEARCH-ENGINE is defaulted to `eaf-browser-default-search-engine'. When USE-USER-ENGINE is non-nil, user can choose a search engine defined in `eaf-browser-search-engines'" (let* ((real-search-engine (if use-user-engine (let ((all-search-engine (mapcar #'car eaf-browser-search-engines))) (completing-read (format "[EAF/browser] Select search engine (default %s): " eaf-browser-default-search-engine) all-search-engine nil t nil nil eaf-browser-default-search-engine)) (or search-engine eaf-browser-default-search-engine))) (link (or (cdr (assoc real-search-engine eaf-browser-search-engines)) (error (format "[EAF/browser] Search engine %s is unknown to EAF!" real-search-engine)))) (search-url (format link search-string))) search-url)) ;;;###autoload (defun eaf-search-it (&optional search-string search-engine) "Use SEARCH-ENGINE search SEARCH-STRING. If called interactively, SEARCH-STRING is defaulted to symbol or region string. The user can enter a customized SEARCH-STRING. SEARCH-ENGINE is defaulted to `eaf-browser-default-search-engine' with a prefix arg, the user is able to choose a search engine defined in `eaf-browser-search-engines'" (interactive) (let* ((current-symbol (if mark-active (if (eq major-mode 'pdf-view-mode) (progn (declare-function pdf-view-active-region-text "pdf-view.el") (car (pdf-view-active-region-text))) (buffer-substring (region-beginning) (region-end))) (symbol-at-point))) (search-string (if search-string search-string (let ((search-string (read-string (format "[EAF/browser] Search (%s): " current-symbol)))) (if (string-blank-p search-string) current-symbol search-string)))) (use-user-engine current-prefix-arg) (search-url (eaf--create-search-url search-string search-engine use-user-engine))) (eaf-open search-url "browser"))) (defun eaf--exit_fullscreen_request () "Exit EAF browser fullscreen." (setq-local eaf-fullscreen-p nil) (eaf-monitor-configuration-change)) (defun eaf-browser-send-esc-or-exit-fullscreen () "Escape fullscreen status if browser current is fullscreen. Otherwise send key 'esc' to browser." (interactive) (if eaf-fullscreen-p (eaf-call-async "eval_function" eaf--buffer-id "exit_fullscreen" "") (eaf-call-async "send_key" eaf--buffer-id ""))) (defun eaf-browser-is-loading () "Return non-nil if current page is loading." (interactive) (when (and (string= eaf--buffer-app-name "browser") (string= (eaf-call-sync "execute_function" eaf--buffer-id "page_is_loading") "True")))) (defun eaf--browser-export-text (buffer-name html-text) (let ((eaf-export-text-buffer (get-buffer-create buffer-name))) (with-current-buffer eaf-export-text-buffer (read-only-mode -1) (erase-buffer) (insert html-text) (goto-char (point-min)) (read-only-mode 1)) (switch-to-buffer eaf-export-text-buffer) )) (defun eaf--atomic-edit (buffer-id focus-text) "EAF Browser: edit FOCUS-TEXT with Emacs's BUFFER-ID." (split-window-below -10) (other-window 1) (let ((edit-text-buffer (generate-new-buffer (format "eaf-%s-atomic-edit" eaf--buffer-app-name))) (buffer-app-name eaf--buffer-app-name)) (with-current-buffer edit-text-buffer (eaf-edit-mode) (set (make-local-variable 'eaf--buffer-app-name) buffer-app-name) (set (make-local-variable 'eaf--buffer-id) buffer-id)) (switch-to-buffer edit-text-buffer) (setq-local eaf-edit-confirm-action "") (eaf--edit-set-header-line) (insert focus-text) ;; When text line number above (when (> (line-number-at-pos) 30) (goto-char (point-min))))) (defun eaf-edit-buffer-cancel () "Cancel EAF Browser focus text input and closes the buffer." (interactive) (kill-buffer) (delete-window) (message "[EAF/%s] Edit cancelled!" eaf--buffer-app-name)) (defun eaf-edit-buffer-switch-to-org-mode () "Switch to `org-mode' to edit table handily." (interactive) (let ((buffer-app-name eaf--buffer-app-name) (buffer-id eaf--buffer-id)) (org-mode) (set (make-local-variable 'eaf--buffer-app-name) buffer-app-name) (set (make-local-variable 'eaf--buffer-id) buffer-id) (outline-show-all) (goto-char (point-min)) (local-set-key (kbd "C-c C-c") 'eaf-edit-buffer-confirm) (local-set-key (kbd "C-c C-k") 'eaf-edit-buffer-cancel) (eaf--edit-set-header-line))) (defun eaf--edit-set-header-line () "Set header line." (setq header-line-format (substitute-command-keys (concat "\\" " EAF/" eaf--buffer-app-name " EDIT: " "Confirm with `\\[eaf-edit-buffer-confirm]', " "Cancel with `\\[eaf-edit-buffer-cancel]'. " "Switch to org-mode with `\\[eaf-edit-buffer-switch-to-org-mode]'. " )))) (defun eaf-open-devtool-page () "Use EAF Browser to open the devtools page." (delete-other-windows) (split-window (selected-window) (/ (* (nth 3 (eaf-get-window-allocation (selected-window))) 2) 3) nil t) (other-window 1) (eaf-open "about:blank" "browser" "devtools")) (defun eaf--toggle-caret-browsing (caret-status) "Toggle caret browsing given CARET-STATUS." (if caret-status (eaf--gen-keybinding-map eaf-browser-caret-mode-keybinding t) (eaf--gen-keybinding-map eaf-browser-keybinding)) (setq eaf--buffer-map-alist (list (cons t eaf-mode-map)))) (defun eaf-import-chrome-bookmarks () "Command to import chrome bookmarks." (interactive) (when (eaf-read-input "Are you sure to import chrome bookmarks to EAF" "yes-or-no" "") (if (not (file-exists-p eaf-chrome-bookmark-file)) (message "Chrome bookmark file: '%s' is not exist, check `eaf-chrome-bookmark-file` setting." eaf-chrome-bookmark-file) (let ((orig-bookmark-record-fn bookmark-make-record-function) (data (json-read-file eaf-chrome-bookmark-file))) (cl-labels ((fn (item) (pcase (alist-get 'type item) ("url" (let ((name (alist-get 'name item)) (url (alist-get 'url item))) (if (not (equal "chrome://bookmarks/" url)) (progn (setq-local bookmark-make-record-function #'(lambda () (eaf--browser-chrome-bookmark name url))) (bookmark-set name))))) ("folder" (mapc #'fn (alist-get 'children item)))))) (fn (alist-get 'bookmark_bar (alist-get 'roots data))) (setq-local bookmark-make-record-function orig-bookmark-record-fn) (bookmark-save) (message "Import success.")))))) ;;;###autoload (defun eaf-open-browser (url &optional args) "Open EAF browser application given a URL and ARGS." (interactive "M[EAF/browser] URL: ") (eaf-open (eaf-wrap-url url) "browser" args)) ;;;###autoload (defun eaf-open-browser-other-window (url &optional args) "Open EAF browser application given a URL and ARGS in other window." (interactive "M[EAF/browser] URL: ") (when (< (length (window-list)) 2) (split-window-right)) (other-window 1) (eaf-open (eaf-wrap-url url) "browser" args)) (defun eaf-open-url-at-point () "Open URL at current point by EAF browser." (interactive) (eaf-open-browser (browse-url-url-at-point))) (defun eaf-toggle-proxy() "Toggle proxy to none or default proxy." (interactive) (eaf-call-sync "toggle_proxy")) (add-to-list 'eaf-app-binding-alist '("browser" . eaf-browser-keybinding)) (setq eaf-browser-module-path (concat (file-name-directory load-file-name) "buffer.py")) (add-to-list 'eaf-app-module-path-alist '("browser" . eaf-browser-module-path)) (add-to-list 'eaf-app-bookmark-handlers-alist '("browser" . eaf--browser-bookmark)) (add-to-list 'eaf-app-bookmark-restore-alist '("browser" . eaf--browser-bookmark-restore)) (add-to-list 'eaf-app-extensions-alist '("browser" . eaf-browser-extension-list)) (provide 'eaf-browser) ;;; eaf-browser.el ends here