From 66a9640d9c4d1101ae5cc986e4e0c86c20262f25 Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Wed, 13 Aug 2025 19:59:04 +0530 Subject: Add docs for events and event opts + document web.json --- Makefile | 1 + TODO.org | 30 +-- docs/api/index.html | 441 +++++++++++++++++++++++++++++++++++++++++++- docs/api/symbols.json | 2 +- init.lua | 12 +- lua/null-browser/api.lua | 4 +- lua/null-browser/events.lua | 79 ++++++++ lua/null-browser/utils.lua | 32 +++- src/App.cpp | 2 +- src/Configuration.hpp | 8 +- src/InstanceManager.hpp | 1 + src/widgets/BrowserApp.cpp | 6 +- 12 files changed, 579 insertions(+), 39 deletions(-) create mode 100644 lua/null-browser/events.lua diff --git a/Makefile b/Makefile index 87f0571..5c86d6d 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,7 @@ check: fmt clang-tidy --config-file=.clang-tidy ./src/**/*.{hpp,cpp} docs: + rm -rf ./docs/api; # API reference pages ldoc -p null-browser -t "Null browser api" \ --merge --ignore --lls \ diff --git a/TODO.org b/TODO.org index 8ca498b..ad08829 100644 --- a/TODO.org +++ b/TODO.org @@ -17,17 +17,17 @@ - [X] Embed docs in app (`null:/docs`) - [X] web.decorations.*.set_size() - [X] Vertical tabline ui -- [ ] Document events and event opts +- [X] Install docs/ and assets/ in build +- [X] Make the asset/config/lua paths readable via lua +- [X] json encode/decode +- [X] Fix path to symbols.json in web.help.get_items (use docs path) +- [X] Document events and event opts - [ ] Update all api to use opts table pattern -- [ ] Install docs/ and assets/ in build - [ ] web.keymap.configure_mode(modename, { passthrough = false }) - [ ] Fullscreen - [ ] Zoom in/out/reset - [ ] Remove unwanted "async"-ness in lua calls - [ ] Api for enabling rpc api in view -- [ ] Make the asset/config/lua paths readable via lua -- [ ] json encode/decode -- [ ] Fix path to symbols.json in web.help.get_items (use docs path) ** Bugs - [ ] Closing webview should close devtools window @@ -127,22 +127,6 @@ web.decorations.top.disable() web.decorations.top.set_size(20) local view_id = web.decorations.top.view() web.view.set_html('
Hello world
', { view = view }) -web.decorations.top.destroy() -- Maybe to destroy unwanted webviews - --- Show tabs in top decoration -web.view.expose_js('openTab', function(tabId) - web.view.select({ view = tabId }) -end, { view = web.decorations.top.view() }) -web.event.add_listener({ 'TabOpen', 'TabClose' }, { - callback = function() - local tabs_html = '' - local views = web.view.list() - for index, view in ipairs(web.view.list()) do - local text = index..': '..view.title..' ('..view.url..')' - local tab = '' .. text .. '') - tabs_html = tabs_html .. tab - end - web.view.set_html(tabs_html, { view = web.decorations.top.view() }) - end, -}) +-- Hide/unhide +web.decorations.top.set_visible(false) #+end_src diff --git a/docs/api/index.html b/docs/api/index.html index fa2ab5a..cfa0023 100644 --- a/docs/api/index.html +++ b/docs/api/index.html @@ -177,6 +177,18 @@ Open help docs for a given item + print (value) + Print a lua object to inspect + + + web.json.encode (obj, opts) + Encodes lua object as json into a string + + + web.json.decode (json_str, opts) + Decodes a string into a lua object + + web.utils.string_trim (str) Trim whitespace from start and end of a string @@ -203,6 +215,46 @@ WebOpts Maps to values defined in ./src/Configuration.hpp + + WebEvents + Events that can be listened to with web.events.add_listener + + + WinCreatedEventOpts + + + + ViewCreatedEventOpts + + + + ViewSelectedEventOpts + + + + ViewClosedEventOpts + + + + UrlChangedEventOpts + + + + PermissionRequestedEventOpts + + + + ModeChangedEventOpts + + + + KeyPressedEventOpts + + + + NotificationReceivedEventOpts + +

Fields

@@ -676,12 +728,16 @@ web.view.reload({ view = See also: +

Usage:

@@ -1038,6 +1094,93 @@ web.view.reload({ view = +
+ + print (value) +
+
+ Print a lua object to inspect + + +

Parameters:

+
    +
  • value + any + +
  • +
+ + + + + +
+
+ + web.json.encode (obj, opts) +
+
+ Encodes lua object as json into a string + + +

Parameters:

+
    +
  • obj + any + Value to encode into json string +
  • +
  • opts Options +
      +
    • indent + boolean? Whether to enable indentation (default: false) +
    • +
    +
+ +

Returns:

+
    + + string + +
+ + + + +
+
+ + web.json.decode (json_str, opts) +
+
+ Decodes a string into a lua object + + +

Parameters:

+
    +
  • json_str + string + Json string to decode +
  • +
  • opts Options +
      +
    • null + any What to use as null value (default: nil) +
    • +
    +
+ +

Returns:

+
    + + any + +
+ + + +
@@ -1211,6 +1354,12 @@ web.view.reload({ view = new_view_url string URL used when view url is not specified (default: 'https://duckduckgo.com') +
  • null_assets_dir + string (readonly) Directory where null assets are installed +
  • +
  • null_docs_dir + string (readonly) Directory where null docs are installed +
  • permissions_persistance 'always'|'session'|'never' How to persist choices for permissions (default: 'always')
  • @@ -1223,6 +1372,296 @@ web.view.reload({ view = +
    + + WebEvents +
    +
    + Events that can be listened to with web.events.add_listener + + + + + +

    See also:

    + + +

    Usage:

    +
      +
      web.events.add_listener('UrlChanged', {
      +  callback = function(opts)
      +    print(opts.url)
      +  },
      +end)
      +
    + +
    +
    + + WinCreatedEventOpts +
    +
    + + + +

    Fields:

    +
      +
    • type + 'WinCreated' +
    • +
    • view + integer +
    • +
    • win + integer +
    • +
    + + + + + +
    +
    + + ViewCreatedEventOpts +
    +
    + + + +

    Fields:

    +
      +
    • type + 'ViewCreated' +
    • +
    • view + integer +
    • +
    • win + integer +
    • +
    + + + + + +
    +
    + + ViewSelectedEventOpts +
    +
    + + + +

    Fields:

    +
      +
    • type + 'ViewSelected' +
    • +
    • view + integer +
    • +
    • win + integer +
    • +
    + + + + + +
    +
    + + ViewClosedEventOpts +
    +
    + + + +

    Fields:

    +
      +
    • type + 'ViewClosed' +
    • +
    • view + integer +
    • +
    • win + integer +
    • +
    + + + + + +
    +
    + + UrlChangedEventOpts +
    +
    + + + +

    Fields:

    +
      +
    • type + 'UrlChanged' +
    • +
    • view_id + integer +
    • +
    • win_id + integer +
    • +
    • url + string +
    • +
    + + + + + +
    +
    + + PermissionRequestedEventOpts +
    +
    + + + +

    Fields:

    +
      +
    • type + 'PermissionRequested' +
    • +
    • premission_type + 'MediaAudioCapture' | 'MediaVideoCapture' | 'MediaAudioVideoCapture' | 'DesktopVideoCapture' | 'DesktopAudioVideoCapture' | 'MouseLock' | 'Notifications' | 'Geolocation' | 'ClipboardReadWrite' | 'LocalFontsAccess' | 'Unsupported' +
    • +
    • webview_id + integer +
    • +
    • win_id + integer +
    • +
    • accept + fun():nil +
    • +
    • reject + fun():nil +
    • +
    + + + + + +
    +
    + + ModeChangedEventOpts +
    +
    + + + +

    Fields:

    +
      +
    • type + 'ModeChanged' +
    • +
    • mode + string +
    • +
    + + + + + +
    +
    + + KeyPressedEventOpts +
    +
    + + + +

    Fields:

    +
      +
    • type + 'KeyPressed' +
    • +
    • key + string +
    • +
    + + + + + +
    +
    + + NotificationReceivedEventOpts +
    +
    + + + +

    Fields:

    +
      +
    • type + 'NotificationReceived' +
    • +
    • title + string +
    • +
    • message + string +
    • +
    • tag + string +
    • +
    • origin + string +
    • +
    • click + fun():nil +
    • +
    • show + fun():nil +
    • +
    • close + fun():nil +
    • +
    + + + + +

    Fields

    diff --git a/docs/api/symbols.json b/docs/api/symbols.json index aa356c5..8b5319e 100644 --- a/docs/api/symbols.json +++ b/docs/api/symbols.json @@ -1 +1 @@ -["_G.__internals","schedule","web.keymap.set","web.keymap.set_mode","web.keymap.get_mode","web.view.close","web.view.create","web.view.current","web.view.list","web.view.select","web.view.set_url","web.view.set_html","web.view.run_js","web.view.reload","web.view.expose","web.view.open_devtools","web.view.current_index","web.event.add_listener","set","get","WebOpts","opts","web.history.back","web.history.forward","web.search.set_text","web.search.reset","web.search.get_text","web.search.next","web.search.previous","web.view.scroll","web.view.scroll_to_top","web.view.scroll_to_bottom","web.help.get_items","web.help.show","uv","inspect","web.utils.string_trim","web.utils.table_merge","web.utils.table_keys","web.utils.equals","web.utils.table_contains"] \ No newline at end of file +["_G.__internals","schedule","web.keymap.set","web.keymap.set_mode","web.keymap.get_mode","web.view.close","web.view.create","web.view.current","web.view.list","web.view.select","web.view.set_url","web.view.set_html","web.view.run_js","web.view.reload","web.view.expose","web.view.open_devtools","web.view.current_index","web.event.add_listener","set","get","WebOpts","opts","web.history.back","web.history.forward","web.search.set_text","web.search.reset","web.search.get_text","web.search.next","web.search.previous","web.view.scroll","web.view.scroll_to_top","web.view.scroll_to_bottom","web.help.get_items","web.help.show","WebEvents","WinCreatedEventOpts","ViewCreatedEventOpts","ViewSelectedEventOpts","ViewClosedEventOpts","UrlChangedEventOpts","PermissionRequestedEventOpts","ModeChangedEventOpts","KeyPressedEventOpts","NotificationReceivedEventOpts","uv","inspect","print","web.json.encode","web.json.decode","web.utils.string_trim","web.utils.table_merge","web.utils.table_keys","web.utils.equals","web.utils.table_contains"] \ No newline at end of file diff --git a/init.lua b/init.lua index d2a8e23..622bd79 100644 --- a/init.lua +++ b/init.lua @@ -165,9 +165,15 @@ end) local hints = require 'null-browser.extras.hints' hints.init() -web.keymap.set('n', 'f', function() hints.start('a[href], button', hints.action.open_in_view) end) -web.keymap.set('n', '', function() hints.start('a[href], button', hints.action.open_in_new_view) end) -web.keymap.set('n', 'yl', function() hints.start('a[href]', hints.action.copy_link) end) +web.keymap.set('n', 'f', function() + hints.start('a[href], button, [role="button"]', hints.action.open_in_view) +end) +web.keymap.set('n', '', function() + hints.start('a[href], button, [role="button"]', hints.action.open_in_new_view) +end) +web.keymap.set('n', 'yl', function() + hints.start('a[href]', hints.action.copy_link) +end) web.keymap.set('n', 'gi', function() local input_selectors = { 'input:not([type="hidden"])', diff --git a/lua/null-browser/api.lua b/lua/null-browser/api.lua index ee861f0..0625869 100644 --- a/lua/null-browser/api.lua +++ b/lua/null-browser/api.lua @@ -187,10 +187,12 @@ end --- @param opts table Options --- @param opts.callback fun(opts:any):nil Callback called when that event occurs --- +--- @see WebEvents +--- --- @usage --- web.event.add_listener('UrlChanged', { --- callback = function(opts) ---- print(opts.url, opts.tab, opts.win) +--- print(opts.url, opts.view, opts.win) --- end, --- }) function web.event.add_listener(events, opts) diff --git a/lua/null-browser/events.lua b/lua/null-browser/events.lua new file mode 100644 index 0000000..1e55999 --- /dev/null +++ b/lua/null-browser/events.lua @@ -0,0 +1,79 @@ +---@diagnostic disable: doc-field-no-class, undefined-doc-param, undefined-doc-name +--- Lua api for configuring null browser. +--- @module web + +-- TODO: Make all opts naming consistent + +--- Events that can be listened to with web.events.add_listener +--- +--- @table WebEvents +--- @see KeyPressedEventOpts +--- @see ModeChangedEventOpts +--- @see NotificationReceivedEventOpts +--- @see PermissionRequestedEventOpts +--- @see UrlChangedEventOpts +--- @see ViewClosedEventOpts +--- @see ViewCreatedEventOpts +--- @see ViewSelectedEventOpts +--- @see WinCreatedEventOpts +--- @see web.event.add_listener +--- +--- @usage +--- web.events.add_listener('UrlChanged', { +--- callback = function(opts) +--- print(opts.url) +--- }, +--- end) + +--- @table WinCreatedEventOpts +--- @field type 'WinCreated' +--- @field view integer +--- @field win integer + +--- @table ViewCreatedEventOpts +--- @field type 'ViewCreated' +--- @field view integer +--- @field win integer + +--- @table ViewSelectedEventOpts +--- @field type 'ViewSelected' +--- @field view integer +--- @field win integer + +--- @table ViewClosedEventOpts +--- @field type 'ViewClosed' +--- @field view integer +--- @field win integer + +--- @table UrlChangedEventOpts +--- @field type 'UrlChanged' +--- @field view_id integer +--- @field win_id integer +--- @field url string + +--- @table PermissionRequestedEventOpts +--- @field type 'PermissionRequested' +--- @field premission_type 'MediaAudioCapture' | 'MediaVideoCapture' | 'MediaAudioVideoCapture' | 'DesktopVideoCapture' | 'DesktopAudioVideoCapture' | 'MouseLock' | 'Notifications' | 'Geolocation' | 'ClipboardReadWrite' | 'LocalFontsAccess' | 'Unsupported' +--- @field webview_id integer +--- @field win_id integer +--- @field accept fun():nil +--- @field reject fun():nil + +--- @table ModeChangedEventOpts +--- @field type 'ModeChanged' +--- @field mode string + +--- @table KeyPressedEventOpts +--- @field type 'KeyPressed' +--- @field key string + +--- @table NotificationReceivedEventOpts +--- @field type 'NotificationReceived' +--- @field title string +--- @field message string +--- @field tag string +--- @field origin string +--- @field click fun():nil +--- @field show fun():nil +--- @field close fun():nil + diff --git a/lua/null-browser/utils.lua b/lua/null-browser/utils.lua index 2c9aba4..907f20c 100644 --- a/lua/null-browser/utils.lua +++ b/lua/null-browser/utils.lua @@ -17,10 +17,38 @@ local inspector = require 'null-browser.inspect' --- @return string web.inspect = inspector.inspect +--- Print a lua object to inspect +--- @param value any +function web.print(value) + print(web.inspect(value)) +end + web.json = web.json or {} local json = require 'null-browser.json' -web.json.encode = json.encode -web.json.decode = json.decode + +--- Encodes lua object as json into a string +--- @param obj any Value to encode into json string +--- @param opts table? Options +--- @param opts.indent boolean? Whether to enable indentation (default: false) +--- @return string +web.json.encode = function(obj, opts) + opts = opts or {} + local state = { indent = opts.indent or false } + return tostring(json.encode(obj, state)) +end + +--- Decodes a string into a lua object +--- @diagnostic disable-next-line: undefined-doc-param +--- @param json_str string Json string to decode +--- @param opts table Options +--- @param opts.null any What to use as null value (default: nil) +--- @return any +web.json.decode = function(json_str, opts) + opts = opts or {} + local value, _, err = json.decode(json_str, nil, opts.null) + if err then error(err) end + return value +end --- Trim whitespace from start and end of a string --- @param str string diff --git a/src/App.cpp b/src/App.cpp index 7ce8932..63ecfa6 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -16,7 +16,7 @@ void App::run() { auto config_dir = get_cli_config_dir(); if (!config_dir.isEmpty()) - configuration.set_config_dir(config_dir); + configuration.set_user_config_dir(config_dir); if (instance_manager.is_server()) { init_server(); diff --git a/src/Configuration.hpp b/src/Configuration.hpp index 1a0f0dc..587205b 100644 --- a/src/Configuration.hpp +++ b/src/Configuration.hpp @@ -65,11 +65,11 @@ public: return QWebEngineProfile::PersistentPermissionsPolicy::StoreOnDisk; } - DEFINE_GETTER(get_config_dir, config_dir) - DEFINE_SETTER(set_config_dir, config_dir) + DEFINE_GETTER(get_user_config_dir, config_dir) + DEFINE_SETTER(set_user_config_dir, config_dir) - QString get_config_lua_init_file() const { return config_dir.filePath("init.lua"); } - QString get_config_lua_dir() const { return config_dir.filePath("lua"); } + QString get_user_config_lua_init_file() const { return config_dir.filePath("init.lua"); } + QString get_user_config_lua_dir() const { return config_dir.filePath("lua"); } private: void on_update(const QString &name, const QVariant &value) { diff --git a/src/InstanceManager.hpp b/src/InstanceManager.hpp index 47d4932..5d70347 100644 --- a/src/InstanceManager.hpp +++ b/src/InstanceManager.hpp @@ -41,6 +41,7 @@ private: bool is_server_mode = false; // TODO: On windows, \\.\pipe\null-browser + // TODO: Use path unique to app data const char *socket_path = "/tmp/null-browser-socket.sock"; }; diff --git a/src/widgets/BrowserApp.cpp b/src/widgets/BrowserApp.cpp index e57b923..9bf86d6 100644 --- a/src/widgets/BrowserApp.cpp +++ b/src/widgets/BrowserApp.cpp @@ -24,13 +24,13 @@ BrowserApp::BrowserApp(Configuration &configuration) : configuration(configurati // Global event filter qApp->installEventFilter(this); - qDebug() << "Config dir:" << configuration.get_config_dir().path(); + qDebug() << "Config dir:" << configuration.get_user_config_dir().path(); // Load lua directory into package path - lua.append_package_path(configuration.get_config_lua_dir()); + lua.append_package_path(configuration.get_user_config_lua_dir()); // Load init.lua - auto lua_init_file = configuration.get_config_lua_init_file(); + auto lua_init_file = configuration.get_user_config_lua_init_file(); if (QFile::exists(lua_init_file)) { lua.load_file_sync(lua_init_file); } else { -- cgit v1.3.1