diff --git a/README.md b/README.md index 94cea12..f89aa96 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ *jnv* is designed for navigating JSON, offering an interactive JSON viewer and `jq` filter editor. -![jnv.gif](https://github.com/ynqa/ynqa/blob/master/demo/jnv.gif) +![demo](tapes/demo.gif) Inspired by [jid](https://github.com/simeji/jid) and [jiq](https://github.com/fiatjaf/jiq). diff --git a/tapes/demo.gif b/tapes/demo.gif new file mode 100644 index 0000000..ef1a0e4 Binary files /dev/null and b/tapes/demo.gif differ diff --git a/tapes/demo.json b/tapes/demo.json new file mode 100644 index 0000000..d1814cc --- /dev/null +++ b/tapes/demo.json @@ -0,0 +1,233 @@ +{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "annotations": { + "kubeadm.kubernetes.io/etcd.advertise-client-urls": "https://172.18.0.2:2379", + "kubernetes.io/config.hash": "9c4c3ba79af7ad68d939c568f053bfff", + "kubernetes.io/config.mirror": "9c4c3ba79af7ad68d939c568f053bfff", + "kubernetes.io/config.seen": "2024-10-12T12:53:27.751706220Z", + "kubernetes.io/config.source": "file" + }, + "creationTimestamp": "2024-10-12T12:53:31Z", + "labels": { + "component": "etcd", + "tier": "control-plane" + }, + "name": "etcd-kind-control-plane", + "namespace": "kube-system", + "ownerReferences": [ + { + "apiVersion": "v1", + "controller": true, + "kind": "Node", + "name": "kind-control-plane", + "uid": "6cb2c3e5-1a73-4932-9cc5-6d69b80a9932" + } + ], + "resourceVersion": "373199", + "uid": "77465839-5a58-43b1-b754-55deed66d5ca" + }, + "spec": { + "containers": [ + { + "command": [ + "etcd", + "--advertise-client-urls=https://172.18.0.2:2379", + "--cert-file=/etc/kubernetes/pki/etcd/server.crt", + "--client-cert-auth=true", + "--data-dir=/var/lib/etcd", + "--experimental-initial-corrupt-check=true", + "--experimental-watch-progress-notify-interval=5s", + "--initial-advertise-peer-urls=https://172.18.0.2:2380", + "--initial-cluster=kind-control-plane=https://172.18.0.2:2380", + "--key-file=/etc/kubernetes/pki/etcd/server.key", + "--listen-client-urls=https://127.0.0.1:2379,https://172.18.0.2:2379", + "--listen-metrics-urls=http://127.0.0.1:2381", + "--listen-peer-urls=https://172.18.0.2:2380", + "--name=kind-control-plane", + "--peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt", + "--peer-client-cert-auth=true", + "--peer-key-file=/etc/kubernetes/pki/etcd/peer.key", + "--peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt", + "--snapshot-count=10000", + "--trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt" + ], + "image": "registry.k8s.io/etcd:3.5.15-0", + "imagePullPolicy": "IfNotPresent", + "livenessProbe": { + "failureThreshold": 8, + "httpGet": { + "host": "127.0.0.1", + "path": "/livez", + "port": 2381, + "scheme": "HTTP" + }, + "initialDelaySeconds": 10, + "periodSeconds": 10, + "successThreshold": 1, + "timeoutSeconds": 15 + }, + "name": "etcd", + "readinessProbe": { + "failureThreshold": 3, + "httpGet": { + "host": "127.0.0.1", + "path": "/readyz", + "port": 2381, + "scheme": "HTTP" + }, + "periodSeconds": 1, + "successThreshold": 1, + "timeoutSeconds": 15 + }, + "resources": { + "requests": { + "cpu": "100m", + "memory": "100Mi" + } + }, + "startupProbe": { + "failureThreshold": 24, + "httpGet": { + "host": "127.0.0.1", + "path": "/readyz", + "port": 2381, + "scheme": "HTTP" + }, + "initialDelaySeconds": 10, + "periodSeconds": 10, + "successThreshold": 1, + "timeoutSeconds": 15 + }, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "volumeMounts": [ + { + "mountPath": "/var/lib/etcd", + "name": "etcd-data" + }, + { + "mountPath": "/etc/kubernetes/pki/etcd", + "name": "etcd-certs" + } + ] + } + ], + "dnsPolicy": "ClusterFirst", + "enableServiceLinks": true, + "hostNetwork": true, + "nodeName": "kind-control-plane", + "preemptionPolicy": "PreemptLowerPriority", + "priority": 2000001000, + "priorityClassName": "system-node-critical", + "restartPolicy": "Always", + "schedulerName": "default-scheduler", + "securityContext": { + "seccompProfile": { + "type": "RuntimeDefault" + } + }, + "terminationGracePeriodSeconds": 30, + "tolerations": [ + { + "effect": "NoExecute", + "operator": "Exists" + } + ], + "volumes": [ + { + "hostPath": { + "path": "/etc/kubernetes/pki/etcd", + "type": "DirectoryOrCreate" + }, + "name": "etcd-certs" + }, + { + "hostPath": { + "path": "/var/lib/etcd", + "type": "DirectoryOrCreate" + }, + "name": "etcd-data" + } + ] + }, + "status": { + "conditions": [ + { + "lastProbeTime": null, + "lastTransitionTime": "2024-12-18T11:41:52Z", + "status": "True", + "type": "PodReadyToStartContainers" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-12-18T11:41:51Z", + "status": "True", + "type": "Initialized" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-12-18T11:41:51Z", + "message": "containers with unready status: [etcd]", + "reason": "ContainersNotReady", + "status": "False", + "type": "Ready" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-12-18T11:41:51Z", + "message": "containers with unready status: [etcd]", + "reason": "ContainersNotReady", + "status": "False", + "type": "ContainersReady" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2024-12-18T11:41:51Z", + "status": "True", + "type": "PodScheduled" + } + ], + "containerStatuses": [ + { + "containerID": "containerd://bb9db5c5c0f8dcee5a9090832f39574ff274689edc818408ad4d78db821a70d5", + "image": "registry.k8s.io/etcd:3.5.15-0", + "imageID": "sha256:27e3830e1402783674d8b594038967deea9d51f0d91b34c93c8f39d2f68af7da", + "lastState": { + "terminated": { + "containerID": "containerd://de0d57479a3ac10e213df6ea4fc1d648ad4d70d4ddf1b95a7999d0050171a41e", + "exitCode": 255, + "finishedAt": "2024-12-18T11:41:49Z", + "reason": "Unknown", + "startedAt": "2024-12-06T13:28:35Z" + } + }, + "name": "etcd", + "ready": false, + "restartCount": 3, + "started": false, + "state": { + "running": { + "startedAt": "2024-12-18T11:41:52Z" + } + } + } + ], + "hostIP": "172.18.0.2", + "hostIPs": [ + { + "ip": "172.18.0.2" + } + ], + "phase": "Running", + "podIP": "172.18.0.2", + "podIPs": [ + { + "ip": "172.18.0.2" + } + ], + "qosClass": "Burstable", + "startTime": "2024-12-18T11:41:51Z" + } +} diff --git a/tapes/demo.tape b/tapes/demo.tape new file mode 100644 index 0000000..adfdbf6 --- /dev/null +++ b/tapes/demo.tape @@ -0,0 +1,81 @@ +# Run from the repository root after `cargo build --locked`: +# vhs tapes/demo.tape +# Clean dark background noise, reduce the palette, then optimize similar frames: +# ffmpeg -i tapes/demo.gif -filter_complex '[0:v]fps=12,format=rgba,colorkey=0x000000:0.2:0[fg];color=c=0x1e1f29:s=1000x680:r=12[bg];[bg][fg]overlay=shortest=1,split[a][b];[a]palettegen=max_colors=64:stats_mode=diff[p];[b][p]paletteuse=dither=none:diff_mode=rectangle' -loop 0 tapes/demo.palette.gif +# magick tapes/demo.palette.gif -fuzz 3% -layers Optimize tapes/demo.gif +# rm tapes/demo.palette.gif +Output tapes/demo.gif + +Require cat +Require ./target/debug/jnv + +Set Shell "bash" +Set Theme "Dracula" +Set FontSize 20 +Set Width 1000 +Set Height 680 +Set Padding 20 +Set Framerate 12 +Set CursorBlink false +Set TypingSpeed 45ms + +# Use this checkout and its default config, independent of the user's settings. +Hide +Type `export PATH="$PWD/target/debug:$PATH"` +Enter +Type "unset NO_COLOR" +Enter +Type "clear" +Enter +Sleep 500ms +Show + +Type "cat tapes/demo.json | jnv -c default.toml" +Enter +Sleep 1500ms + +# Complete a key, then choose between matching nested paths. +Type ".m" +Tab +Sleep 1200ms +Enter +Type ".na" +Tab +Sleep 1s +Down +Sleep 1s +Up +Sleep 1s +Enter +Sleep 1s + +# Apply a jq projection to extract a compact, useful result. +Ctrl+U +Type@35ms ".spec.containers[] | {name, image}" +Sleep 3s + +# Relaunch with a custom prompt, JSON colors, indentation, and completion style. +Ctrl+C +Hide +Type "clear" +Enter +Sleep 300ms +Show +Type "# Custom style" +Enter +Type "jnv -c tapes/demo.toml tapes/demo.json" +Enter +Sleep 1500ms +Type ".metadata.na" +Tab +Sleep 1200ms +Down +Sleep 1200ms +Enter +Sleep 1s +Ctrl+U +Type@35ms ".spec.containers[] | {name, image}" +Sleep 3s + +Hide +Ctrl+C diff --git a/tapes/demo.toml b/tapes/demo.toml new file mode 100644 index 0000000..3efd567 --- /dev/null +++ b/tapes/demo.toml @@ -0,0 +1,88 @@ +# Demo style: custom prompt, JSON colors, indentation, and completion colors. +# All other settings follow default.toml. + +no_hint = false + +[editor.on_focus] + +edit_mode = "Insert" + +word_break_chars = [".", "|", "(", ")", "[", "]"] + +prefix = "jq ❯ " +prefix_style = "fg=#50FA7B,attr=bold" +active_char_style = "bg=#BD93F9,fg=#282A36" +inactive_char_style = "" + +[editor.on_defocus] +prefix = "jq ▾ " +prefix_style = "fg=#50FA7B,attr=dim" +active_char_style = "attr=dim" +inactive_char_style = "attr=dim" + +[json] + +[json.stream] +indent = 4 +curly_brackets_style = "fg=#F8F8F2,attr=bold" +square_brackets_style = "fg=#F8F8F2,attr=bold" +key_style = "fg=#FF79C6" +string_value_style = "fg=#F1FA8C" +number_value_style = "fg=#BD93F9" +boolean_value_style = "fg=#BD93F9" +null_value_style = "fg=grey" +active_item_attribute = "bold" +inactive_item_attribute = "normalintensity" +overflow_mode = "Wrap" + +[completion] +search_result_chunk_size = 100 + +search_load_chunk_size = 50000 + +[completion.listbox] +lines = 3 +cursor = "→ " +active_item_style = "fg=#282A36,bg=#50FA7B,attr=bold" +inactive_item_style = "fg=#BD93F9" + +[keybinds] +exit = ["Ctrl+C"] +copy_query = ["Ctrl+Q"] +copy_result = ["Ctrl+O"] +switch_mode = ["Shift+Down", "Shift+Up"] + +[keybinds.on_editor] +backward = ["Left"] + +forward = ["Right"] + +move_to_head = ["Ctrl+A"] +move_to_tail = ["Ctrl+E"] +move_to_previous_nearest = ["Alt+B"] +move_to_next_nearest = ["Alt+F"] +erase = ["Backspace"] + +erase_all = ["Ctrl+U"] + +erase_to_previous_nearest = ["Ctrl+W"] +erase_to_next_nearest = ["Alt+D"] +completion = ["Tab"] +on_completion.up = ["Up"] +on_completion.down = ["Down", "Tab"] + +[keybinds.on_json_viewer] +up = ["Up", "Ctrl+K", "ScrollUp"] +down = ["Down", "Ctrl+J", "ScrollDown"] +move_to_head = ["Ctrl+L"] +move_to_tail = ["Ctrl+H"] +toggle = ["Enter"] +expand = ["Ctrl+P"] +collapse = ["Ctrl+N"] + +[reactivity_control] +query_debounce_duration = "600ms" + +resize_debounce_duration = "200ms" + +spin_duration = "300ms"