Updated the documentation
Some checks failed
ci / Build (push) Failing after 3m41s
Release / Build (push) Failing after 3m41s
Release / Site (push) Has been skipped
Release / Release (push) Has been skipped

This commit is contained in:
Leon Mika 2025-11-15 11:34:07 +11:00
parent 4cb66f52fe
commit b4cee0e2e5
31 changed files with 267 additions and 296 deletions

0
_site/.hugo_build.lock Normal file
View file

View file

@ -1,7 +1,6 @@
+++
title = "Dynamo-Browse"
bookToc = false
+++
---
weight: 10
---
<div class="site-header">
<img src="/images/dynamo-browse/dynamo-browse-logo.png">
@ -17,15 +16,31 @@ With it, you can quickly connect to and browse the contents of a DynamoDB table
in your AWS account or local machine. There are some basic facilities for
editing as well.
## Getting Started
Instructions for installing Dynamo-Browse can be found on the [Downloads page](/download).
This video gives a brief introduction of how to use Dynamo-Browse to view the items of a DynamoDB table:
{{< youtube cQnTIg1_tfg >}}
More information about the tool can be found within the [Dynamo-Browse user manual](/docs).
## Download
Binary packages for MacOS and Linux can be [found at Forgejo](https://lmika.dev/cmd/dynamo-browse/releases).
### MacOS Using Homebrew
If you have Homebrew, you can install Dynamo-Browse using the following command:
```
brew tap lmika/dynamo-browse https://lmika.dev/casks/dynamo-browse
brew install dynamo-browse
```
### Go
If you have Go installed, you can install Dynamo-Browse using the following command:
```
go install lmika.dev/cmd/dynamo-browse/cmd/dynamo-browse@latest
```

View file

@ -1,35 +0,0 @@
# User Guide
## Table Of Contents
- [Launching and Quitting](/docs/launching)
- [Selecting a Table](/docs/launching#selecting-a-table)
- [Selecting a Workspace](/docs/launching#selecting-a-workspace)
- [Quitting](/docs/launching#quitting)
- [Getting Around](/docs/getting-around)
- [The Back-stack](/docs/getting-around#the-back-stack)
- [Adjusting The Layout](/docs/getting-around#adjusting-the-layout)
- [Adjusting The Displayed Columns](/docs/getting-around#adjusting-the-displayed-columns)
- [Entering Commands](/docs/getting-around#entering-commands)
- [Filtering and Querying](/docs/filtering-querying)
- [Filtering](/docs/filtering-querying#filtering)
- [Querying](/docs/filtering-querying#querying)
- [Editing Items](/docs/editing-items)
- [Marking Items](/docs/editing-items#marking-items)
- [Modifying Attributes](/docs/editing-items#modifying-attributes)
- [Deleting Attributes](/docs/editing-items#deleting-attributes)
- [Adding Items](/docs/editing-items#adding-items)
- [Deleting Items](/docs/editing-items#deleting-items)
- [Committing Changes](/docs/editing-items#committing-changes)
- [Backing Out of Changes](/docs/editing-items#backing-out-of-changes)
- [Customising Dynamo-Browse](/docs/customising)
- [The RC File](/docs/customising#rc-file)
- [Rebinding Keys](/docs/customising#rebinding-keys)
References
- [Key Bindings](/docs/reference/key-bindings)
- [Commands](/docs/reference/commands)
- [Query Expressions](/docs/reference/query-expressions)
- [Launch Flags](/docs/reference/launch-flags)
- [Settings](/docs/reference/settings)

View file

@ -0,0 +1,4 @@
---
bookFlatSection: true
weight: 40
---

View file

@ -1,5 +1,12 @@
---
title: Commands
weight: 20
---
# Commands
These commands are meant for general, interactive use. For additional commands meant for defining
extensions, see [Extensions](/docs/reference/extensions/).
## clone
```
@ -40,7 +47,7 @@ Displays _message_ in the status bar. Mainly used for debugging.
## export
```
:export [-all] <filename>
:export <filename> [-all]
```
Writes the currently loaded items as a CSV file to _filename_.
@ -105,7 +112,7 @@ Quits Dynamo-Browse.
Rebinds the action with _bindingName_ to _key_. This will replace any existing binding for that action.
See [Key Bindings](#key-bindings) with "Show binding names" checked to see available binding names.
## set
## set-opt
```
:set <name> [value]
@ -116,7 +123,7 @@ Set the value of a setting. Flag setting types can be enabled without any value
## set-attr
```
:set-attr [type] <attributeName>
:set-attr <attributeName> [type]
```
Alias: `sa`

View file

@ -0,0 +1,7 @@
---
title: "Extensions"
type: script-api
weight: 60
draft: true
---
# Extensions

View file

@ -1,3 +1,7 @@
---
title: Key Bindings
weight: 10
---
# Key Bindings
<div data-controller="keybindings">

View file

@ -1,3 +1,7 @@
---
title: Launch Flags
weight: 40
---
# Launch Flags
## -debug

View file

@ -1,3 +1,7 @@
---
title: Query Expressions
weight: 30
---
# Query Expression
Query expressions are used to select rows of a table. When executed as a query (i.e. by pressing <kbd>?</kbd>),

View file

@ -1,5 +0,0 @@
---
title: "Script API"
type: script-api
---
# Script API

View file

@ -1,3 +1,7 @@
---
title: Settings
weight: 50
---
# Settings
## default-limit

View file

@ -1,103 +0,0 @@
---
title: "Scripting"
---
# Scripting
Scripts can be used to automate certain tasks with Dynamo-Browse. They can also be used to define
new commands or key bindings.
## Scripting Basics
Dynamo-Browse scripts are written using the [Tamarin](https://cloudcmds.github.io/tamarin/) scripting language,
which looks a lot like [Go](https://go.dev). All features of the language are available in Dynamo-Browse.
The typical "hello world" script for Dynamo-Browse is below:
```
ui.print("Hello, world")
```
This uses the [ui](/docs/reference/script-api/#module-ui) package, which is the package used to interact with
the Dynamo-Browse user interface.
A full list of supported packages can be found in the [Script API](/docs/reference/script-api/) reference, along
with the builtins and packages supported by Tamarin itself.
{{<hint info>}}
**Note:** the [ext](/docs/reference/script-api/#module-ext) package is only available to Extension Scripts.
{{</hint>}}
To execute this script, use the `run-script` command:
```
run-script /path/to/script/hello.tm
```
You'll see that the message "Hello, world" will appear in the status bar of Dynamo-Browse.
<!-- TODO: Screenshot -->
Any `print` or `printf` messages will be written to the debug log with the prefix `script <filename>`. The
debug log is turned off by default, but it can be enabled using the [-debug](/docs/reference/launch-flags/#-debug) flag on launch.
Scripts loaded using the `run-script` command are for ad-hoc automation tasks that are not necessarily designed for
repeated use. These ad-hoc scripts are executed, then immediately unloaded, and are not generally allowed to extend
Dynamo-Browse. In order to do so, you will need to write an Extension Script.
## Extension Scripts
Extension scripts are scripts designed to extend Dynamo-Browse in some way, such as with new commands or key bindings.
They are traditionally loaded on startup and exist in the predefined "script" directory. They are usually designed for
repeated operations, including those that can be bound to command name or keys.
The following is an example script which will define a "goto" command. When invoked, the script will prompt the
user for the value of the partition key. It will then perform a query over the currently viewed table for any rows with
that partition key. If no error occurred, the results of the query will be shown to the user.
```
// Define a new "goto" command, which can be invoked when the user presses ':' and types in 'goto'
ext.command("goto", func() {
// Use the information of the current table to get the name of the partition key.
pkName := session.current_table().keys["partition"]
// Prompt the user for the value to go to. The user can press Esc, which will cancel
// the input and return 'nil'.
keyVal := ui.prompt(pkName + "? ")
if keyVal == nil {
return nil
}
// Run a query over the DynamoDB table for any rows with the partition key. Notice
// the use of the 'args' option, and the presence of both the name prefix (':key')
// and value prefix ('$val').
res := session.query(":key = $val", {
args: {
key: pkName,
val: keyVal,
},
})
// The query method will return either an error or a result. If it's an error, print
// a notice and exist.
if res.is_err() {
ui.print("Can't goto: " + res.err_msg())
return nil
}
// If no error, unwrap the result object to get the result-set returned from the query.
// Then change the current result-set to this one. This will change the result-set the
// user is currently seeing.
session.set_result_set(res.unwrap())
})
```
To load an extension script, use the `load-script` command:
```
load-script script.tm
```
The script must exist in the "script" directory, which by default is:
```
$HOME/.config/audax/dynamo-browse/scripts
```

View file

@ -0,0 +1,4 @@
---
bookFlatSection: true
weight: 30
---

View file

@ -1,22 +1,24 @@
---
title: Customising Dynamo-Browse
weight: 50
---
# Customising Dynamo-Browse
Some commands can be used to customise Dynamo-Browse, such as modify key bindings.
The effect of these commands will only be applied for the duration of the session: they are currently not
tracked within the workspace file. So in order to keep customisations across relaunches, these commands
tracked within the workspace file. To keep customisations across relaunches, these commands
can be added to an RC file.
## The RC File
## RC Files
The RC file is a text file containing commands that will be executed by Dynamo-Browse upon launch.
By default, the RC file is located at the following path:
RC files are text files containing commands that will be executed by Dynamo-Browse upon launch.
By default, RC files are located in `$HOME/.config/dynamo-browse/` and have the suffix `.ucl`. For example:
```
$HOME/.config/audax/dynamo-browse/init.rc
$HOME/.config/dynamo-browse/init.ucl
```
This file is primarily intended for commands that customise Dynamo-Browse in a particular way, but any
command can be entered here. If this file is found, Dynamo-Browse will invoke each command before loading
or prompting the table.
Any number of RC files can be present in this directory, and they are executed in lexicographical order.
## Rebinding Keys
@ -38,4 +40,11 @@ At the moment each binding name can only be mapped to a single key. It's also c
to setup bindings for commands. These may be supported in the future.
A list of available binding names can be found the the [reference](/docs/reference/key-bindings)
(check the "Show binding names" checkbox). Note that some bindings may not have default key bindings.
(check the "Show binding names" checkbox). Note that some bindings may not have default key bindings.
## Extensions
The RC files are primarily intended for commands that customise Dynamo-Browse in a particular way, but any
command can be entered here, including those for defining new commands or running predefined queries. These
are generally known as extensions, although act as any other RC file located within that directory. More
information on extensions can be found in the [reference](/docs/reference/script-api).

View file

@ -1,3 +1,7 @@
---
title: Editing Items
weight: 40
---
# Editing Items
Dynamo-Browse offers some basic facilities for editing items — such as creating items, deleting items,

View file

@ -1,4 +1,8 @@
# Querying And Viewing Results
---
title: Querying and Filtering Results
weight: 30
---
# Querying and Filtering Results
## Querying
@ -8,7 +12,24 @@
A query or scan over the table can be performed by entering a _Query Expression_.
Query expressions are a built-in expression language which translates to either a DynamoDB query
or scan, depending on the expression. Details about the Query Expression language can be found in the
or scan, depending on the expression.
Query expressions are entered as expressions, with most being of the form `attribute operator value`.
For example, the expression for selecting records where `color` equals `red` is:
```
color = "red"
```
The `color` attribute can either be a partition key, sort key, or a regular attribute. Dynamo Browse
will do it's best to run the query as a DynamoDB query if possible. Usually query expressions of the
form `pk = <something>` or `pk = <something> and sk <comparison> <something>` where `pk` is a partition key,
`sk` is the sort key, and `<comparison>` is one of the [equality](/docs/reference/query-expressions/#equality),
[numerical comparison](/docs/reference/query-expressions/#numerical-comparison), or
[prefix operator](/docs/reference/query-expressions/#prefix-operator), will be executed as queries.
Other expressions will be executed as scans.
Details about the Query Expression language can be found in the
[Query Expressions references](/docs/reference/query-expressions/).
To run a query, press <kbd>?</kbd>, and enter the query expression.

View file

@ -1,3 +1,7 @@
---
title: Getting Around
weight: 20
---
# Getting Around
After selecting a table, Dynamo-Browse will perform a scan and present the results in the default view mode.

View file

@ -1,4 +1,8 @@
# Launching And Quitting
---
title: Launching and Quitting
weight: 10
---
# Launching and Quitting
To launch Dynamo-Browse, run the following command at the terminal:

View file

@ -1,19 +1,8 @@
+++
layout = "single"
+++
---
weight: 20
---
# Download
Binary packages can be [download from GitHub](https://github.com/lmika/audax/releases/latest).
## MacOS Using Homebrew
If you have Homebrew, you can install using the following command:
```
brew tap lmika/audax
brew install audax
```
## Linux

View file

@ -1,25 +0,0 @@
+++
headless = true
+++
- [Download]({{< relref "/download" >}})
- [Releases](https://github.com/lmika/audax/releases)
- [Github](https://github.com/lmika/audax)
<br>
[**User Guide**]({{< relref "/docs" >}})
- [Launching]({{< relref "/docs/launching" >}})
- [Getting Around]({{< relref "/docs/getting-around" >}})
- [Filtering And Querying]({{< relref "/docs/filtering-querying" >}})
- [Editing Items]({{< relref "/docs/editing-items" >}})
- [Customising]({{< relref "/docs/customising" >}})
- [Scripting]({{< relref "/docs/scripting" >}})
<br>
**References**
- [Key Bindings]({{< relref "/docs/reference/key-bindings" >}})
- [Commands]({{< relref "/docs/reference/commands" >}})
- [Query Expressions]({{< relref "/docs/reference/query-expressions" >}})
- [Launch Flags]({{< relref "/docs/reference/launch-flags" >}})
- [Settings]({{< relref "/docs/reference/settings" >}})
- [Script API]({{< relref "/docs/reference/script-api" >}})
<br>

View file

@ -0,0 +1,40 @@
module: async
docs: |
Provides commands for executing blocks asynchronously.
Asynchronous blocks are executed in the same thread as regular commands, so it's recommended
to avoid long running operations in these blocks. Exception to these are commands which require fetching
data from the database.
symbols:
- name: do
syntax: async:do BLOCK
docs: |
Schedules a block to be executed at the conclusion of all other running commands.
Blocks are place in a queue in the order the call to `async:do` is made, with a maximum
queue size of 100 blocks. If the queue is full, the command will return an error.
example: |
async:do {
echo "World"
}
echo "Hello"
- name: in
syntax: async:in SECONDS BLOCK
docs: |
Schedules a block to be executed in SECONDS seconds. Once the timout has ellapsed, the block will
be placed on the queue and will be executed once all other pending blocks have been consumed.
example: |
async:in 5 {
echo "5 seconds have ellapsed"
}
- name: query
syntax: async:query EXPRESSION [QUERY_ARGS] BLOCK [OPTIONS]
docs: |
Executes the query in the background and schedules BLOCK once the resultset is available. The available
options match that of rs:query.
Note: the RC files are invoked before the table picker is shown, so any async:query calls invoked during
startup should have the `-table` option set.
example: |
async:query 'pk = $arg' [arg:"abc123"] { |rs|
echo $rs.First.pk
}

View file

@ -0,0 +1,22 @@
module: av
docs: |
Provides commands for translating between UCL and DynamoDB types.
symbols:
- name: 'true'
syntax: av:true
docs: |
Returns a true BOOL attribute value.
- name: 'false'
syntax: av:false
docs: |
Returns a false BOOL attribute value.
- name: 'null'
syntax: av:null
docs: |
Returns a NULL attribute value.
- name: string-set
syntax: av:string-set LIST
docs: |
Converts a list into a string set (SS) attribute value.
example: |
av:string-set [apple banana cherry]

View file

@ -1,75 +0,0 @@
module: ext
docs: |
Provides access to the extension points scripts can used to extend the functionality of Dynamo-Browse.
This module is only available for scripts loaded using the [load-script]() command.
symbols:
- name: command
syntax: ext.command(name, fn)
docs: |
Defines a new command, which can be invoked by entering _name_ within the main view mode.
The parameter _fn_ must be a function, which will be executed when the _name_ command is entered
while in view mode.
The command can accept arguments, which will be passed in to the parameters of _fn_. The number
of command arguments must match the number of parameters, except for any function arguments with
a default value.
example: |
ext.command("add", func(x, y) {
sum := x + y
ui.print("x + y = ", sum)
})
- name: related_items
syntax: ext.related_items(table, fn)
docs: |
Defines a "related item" for a table. These act as quick jumps between tables.
When the user presses Shift+O, all the related item functions that match the given
table will be evaluated. Each one is to return zero or more related queries, which are presented
to the user as a list. When the user selects one, the query will be evaluated and the result set will
be shown.
The _table_ parameter is the name of the table of the related items managed by this function.
If the last character of the table is `*`, then _table_ will be treated as a name prefix.
The _fn_ will produce a list of queries that are related to a given item. The function takes the currently
selected item as the argument, and is expected to produce a list of maps, with each map having the following
fields:
- `label`: The label to use for the picker option
- `query`: The query expression that will run when the option is chosen
- `table`: The table to run the query over. If not set, the current table will be used
- `args`: A map of query placeholder values
- `on_select`: An optional function that will run in place of a predefined query. If set, the `query` field will
be ignored.
example: |
ext.related_items("user-account", func(item) {
return [
{
"label": "Customer",
"table": "billing",
"query": "email=$email",
"args": {"email": item.attr("email")},
},
]
})
- name: key_binding
syntax: ext.key_binding(name, options, fn)
docs: |
Defines a new key binding, which can be invoked while viewing the table.
The _name_ parameter defines the binding name. The binding names will be prefixed with
`ext.<script_basename>`. This name can be used with the [rebind]() command.
The _option_ parameter defines a map of options. The only valid option is
`default`, which is the default key to use for this binding. If unset, the binding will
have no key binding and can only be bound using the [rebind]() command.
The _fn_ parameter is the function that will be invoked when the key is pressed.
It must accept no parameters.
example: |
// Script name: sayhello.tm
//
// This binding can be rebound with the command "rebind ext.sayhello.hello <key>"
ext.key_binding("hello", {"default": "H"}, func() {
ui.print("Hello")
})

View file

@ -0,0 +1,9 @@
module: opt
docs: |
Provides commands for modifying options.
symbols:
- name: 'set'
syntax: opt:set NAME [VALUE]
docs: |
Returns the current value of the setting option NAME. If VALUE is present, modifies the setting option first before
returning the value.

View file

@ -0,0 +1,14 @@
module: pb
docs: |
Provides access to the pasteboard.
symbols:
- name: 'paste'
syntax: pb:paste
docs: |
Returns the current text value of the paste board. If the paste board contains no text value,
or is unavailable, returns the empty string.
- name: 'copy'
syntax: pb:copy VALUE
docs: |
Sets the current text value of the paste board to VALUE. If the paste board is unavailable, this
command nops.

View file

@ -3,18 +3,25 @@ type: type
docs: |
Holds a collection of items returned from a query, or presented to a user.
A specific item of a result-set can be retrived using the subscript option. For example, `result[21]` will
return the 21st item of the result-set from the first item. A negative index can be used to retrieve an
item from the last item.
A specific item of a result-set can be retrived using the subscript option. For example, `$result.(21)` will
return the 21st item of the result-set from the first item.
There is no guarantee to the ordering of items within the result-set, although items are usually
ordered based on the partition and sort key.
symbols:
- name: length
syntax: resultset.length
- name: Items
syntax: '$resultset.Items'
docs: |
Returns the number of items within the result set.
- name: table
syntax: resultset.table
Returns the items within the result set.
- name: HasNextPage
syntax: '$resultset.HasNextPage'
docs: |
Returns true if this result-set has another page. The next page can be retrieved by calling `rs:next-page $resultset`.
- name: First
syntax: '$resultset.First'
docs: |
Returns the first item of the result-set, or nil if the result set is empty. Shorthand for `$result.Items.(0)`.
- name: Table
syntax: '$resultset.Table'
docs: |
Returns information about the table this result set belongs to.

View file

@ -0,0 +1,37 @@
module: rs
docs: |
Provides operations over result-sets, or commands which return result-sets.
symbols:
- name: new
syntax: rs:new [-table TABLE]
docs: |
Creates a new, empty result-set. If -table is specific, will configure the result-set table to
that of TABLE. Otherwise, the result-set will inherit the current table.
- name: query
syntax: rs:query EXPRESSION [ARGUMENTS] [-table TABLE]
docs: |
Invokes a query expression and returns the result as a result-set. The query can be invoked with
ARGUMENTS, which must be a dictionary with key/value pairs that are made available to the query
as either attribute placeholders (`:attr`), or value placeholders (`$value`).
If -table is specific, will run the query over TABLE. Otherwise, the query will be invoked
over the current table.
Note that this command blocks the command thread until either the results are available, or if
an error occurs. To avoid this, use 'async:query', which will run the query in the background.
example: |
results = rs:query 'pk = $myID' [myID:"abc132"] -table users
- name: scan
syntax: rs:scan [-table TABLE]
docs: |
Runs a table scan, returning the first page of results as a result-set. If -table is specific, will
run the scan over TABLE. Otherwise, the scan will be over the current table.
- name: next-page
syntax: rs:next-page RESULTSET
docs: |
Runs the next page of results from the passed in result-set. This will depend on how RESULTSET was
created. For example, if RESULTSET was from a query, this will return the next page of results from that
query. Likewise, for scans.
If the next page is available, the results will be returned as a new result-set, leaving the original result-set
unmodified. If no next page is available, then nil will be returned.

View file

@ -6,7 +6,7 @@ builds:
- darwin_amd64
- darwin_arm64
env:
- CGO_ENABLED=1
- CGO_ENABLED=0
main: ./cmd/dynamo-browse/.
binary: dynamo-browse
- id: dynamo-browse_linux
@ -14,7 +14,7 @@ builds:
- linux_amd64
- linux_arm64
env:
- CGO_ENABLED=1
- CGO_ENABLED=0
main: ./cmd/dynamo-browse/.
binary: dynamo-browse

View file

@ -2,6 +2,7 @@ package cmdpacks
import (
"context"
"lmika.dev/cmd/dynamo-browse/internal/common/ui/commandctrl"
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/controllers"
"ucl.lmika.dev/ucl"
@ -11,7 +12,7 @@ type optModule struct {
settingsController *controllers.SettingsController
}
func (m optModule) pbSet(ctx context.Context, args ucl.CallArgs) (any, error) {
func (m optModule) optSet(ctx context.Context, args ucl.CallArgs) (any, error) {
var (
name string
newVale string
@ -41,7 +42,7 @@ func moduleOpt(
return ucl.Module{
Name: "opt",
Builtins: map[string]ucl.BuiltinHandler{
"set": m.pbSet,
"set": m.optSet,
},
}
}

View file

@ -187,7 +187,7 @@ func (rs *rsModule) rsScan(ctx context.Context, args ucl.CallArgs) (_ any, err e
return newResultSetProxy(newResultSet), nil
}
func (rs *rsModule) rsFilter(ctx context.Context, args ucl.CallArgs) (any, error) {
func (rs *rsModule) rsHide(ctx context.Context, args ucl.CallArgs) (any, error) {
var (
rsProxy SimpleProxy[*models.ResultSet]
filter string
@ -307,7 +307,7 @@ func moduleRS(tableService *tables.Service, state *controllers.State) ucl.Module
"new": m.rsNew,
"query": m.rsQuery,
"scan": m.rsScan,
"filter": m.rsFilter,
"hide": m.rsHide,
"next-page": m.rsNextPage,
"union": m.rsUnion,
"set": m.rsSet,

View file

@ -21,8 +21,8 @@ import (
const (
commandsCategory = "commands"
pendingTaskBuffer = 50
pendingAuxTaskBuffer = 50
pendingTaskBuffer = 100
pendingAuxTaskBuffer = 100
auxWorkers = 4
)