From 6b251705797f5df8fb6c19a58c6bc0d62ac0d913 Mon Sep 17 00:00:00 2001 From: dzucconi Date: Thu, 12 Mar 2026 22:55:35 -0400 Subject: [PATCH] Aligns all flags with available API params --- README.md | 48 +++++++++------ src/commands/add.tsx | 27 +++++++- src/commands/block.tsx | 5 +- src/commands/channel.tsx | 12 +++- src/commands/connect.tsx | 13 +++- src/commands/connection.tsx | 16 +++-- src/commands/group.tsx | 19 +++++- src/commands/user.tsx | 29 +++++++-- src/lib/registry.tsx | 120 +++++++++++++++++++++++++++++++----- 9 files changed, 236 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index b9f76c3..24e9e27 100644 --- a/README.md +++ b/README.md @@ -37,11 +37,13 @@ Running `arena` with no arguments opens an interactive session. Pass a command f ```bash arena channel worldmaking # View a channel arena channel contents worldmaking # Paginated contents +arena channel contents worldmaking --sort updated_at_desc --user-id 123 arena channel create "My Research" --visibility private +arena channel create "Team Notes" --group-id 123 arena channel update my-research --title "New Title" --description "Updated" arena channel delete my-research -arena channel connections worldmaking # Where channel appears -arena channel followers worldmaking +arena channel connections worldmaking --sort connected_at_desc +arena channel followers worldmaking --sort connected_at_desc ``` ### Blocks @@ -49,10 +51,13 @@ arena channel followers worldmaking ```bash arena block 12345 # View a block arena block update 12345 --title "New Title" -arena block comments 12345 # View comments -arena block connections 12345 # Where block appears +arena block comments 12345 --sort connected_at_desc +arena block connections 12345 --sort connected_at_desc --filter OWN arena add my-channel "Hello world" # Add text arena add my-channel https://example.com # Add a URL +arena add my-channel "Hello" --title "Greeting" --description "Pinned note" +arena add my-channel https://example.com --alt-text "Cover image" --insert-at 1 +arena add my-channel https://example.com --original-source-url https://source.com --original-source-title "Original" arena upload photo.jpg --channel my-channel arena batch my-channel "https://a.com" "https://b.com" arena batch status 1234 @@ -66,8 +71,10 @@ echo "piped text" | arena add my-channel ```bash arena connect 12345 my-channel # Connect block to channel +arena connect 12345 my-channel --type Channel --position 1 arena connection 67890 # View a connection arena connection move 67890 --movement move_to_top +arena connection move 67890 --movement insert_at --position 1 arena connection delete 67890 ``` @@ -83,12 +90,12 @@ arena comment delete 67890 ```bash arena whoami # Current user arena user damon-zucconi # View a user -arena user contents damon-zucconi # User's content -arena user followers damon-zucconi -arena user following damon-zucconi +arena user contents damon-zucconi --type Image --sort updated_at_desc +arena user followers damon-zucconi --sort connected_at_desc +arena user following damon-zucconi --type User --sort connected_at_desc arena group are-na-team # View a group -arena group contents are-na-team # Group's content -arena group followers are-na-team +arena group contents are-na-team --type Image --sort updated_at_desc +arena group followers are-na-team --sort connected_at_desc ``` ### Search @@ -134,16 +141,19 @@ arena ping # API health check ### Command-specific flags -| Command | Flags | -| ---------------------------------- | -------------------------------------------------------------------------------------------------- | -| `channel create`, `channel update` | `--title`, `--description`, `--visibility` | -| `block update` | `--title`, `--description`, `--content`, `--alt-text` | -| `add`, `batch` | `--title`, `--description` | -| `upload` | `--channel`, `--title`, `--description` | -| `connect` | `--type`, `--position` | -| `connection move` | `--movement`, `--position` | -| `search` | `--scope`, `--sort`, `--ext`, `--after`, `--seed`, `--user-id`, `--group-id`, `--channel-id` | -| `import` | `--dir`, `--recursive`, `--interactive`, `--batch-size`, `--upload-concurrency`, `--poll-interval` | +| Command | Flags | +| ------------------ | ----------------------------------------------------------------------------------------------------------- | +| `channel create` | `--description`, `--visibility`, `--group-id` | +| `channel update` | `--title`, `--description`, `--visibility` | +| `channel contents` | `--user-id` | +| `block update` | `--title`, `--description`, `--content`, `--alt-text` | +| `add` | `--title`, `--description`, `--alt-text`, `--original-source-url`, `--original-source-title`, `--insert-at` | +| `batch` | `--title`, `--description` | +| `upload` | `--channel`, `--title`, `--description` | +| `connect` | `--type`, `--position` | +| `connection move` | `--movement`, `--position` | +| `search` | `--scope`, `--sort`, `--ext`, `--after`, `--seed`, `--user-id`, `--group-id`, `--channel-id` | +| `import` | `--dir`, `--recursive`, `--interactive`, `--batch-size`, `--upload-concurrency`, `--poll-interval` | ## Aliases diff --git a/src/commands/add.tsx b/src/commands/add.tsx index a5d0960..471dc3f 100644 --- a/src/commands/add.tsx +++ b/src/commands/add.tsx @@ -6,10 +6,24 @@ import { useCommand } from "../hooks/use-command"; interface Props { channel: string; value: string; + title?: string; description?: string; + altText?: string; + originalSourceUrl?: string; + originalSourceTitle?: string; + insertAt?: number; } -export function AddCommand({ channel, value, description }: Props) { +export function AddCommand({ + channel, + value, + title, + description, + altText, + originalSourceUrl, + originalSourceTitle, + insertAt, +}: Props) { const { data, error, loading } = useCommand(async () => { const ch = await getData( client.GET("/v3/channels/{id}", { @@ -18,7 +32,16 @@ export function AddCommand({ channel, value, description }: Props) { ); const block = await getData( client.POST("/v3/blocks", { - body: { value, channel_ids: [ch.id], description }, + body: { + value, + channel_ids: [ch.id], + title, + description, + alt_text: altText, + original_source_url: originalSourceUrl, + original_source_title: originalSourceTitle, + insert_at: insertAt, + }, }), ); return { block, channel: ch }; diff --git a/src/commands/block.tsx b/src/commands/block.tsx index 323c989..c6de489 100644 --- a/src/commands/block.tsx +++ b/src/commands/block.tsx @@ -1,5 +1,6 @@ import { Box, Text } from "ink"; import { client, getData } from "../api/client"; +import type { ConnectionSort } from "../api/types"; import { BlockContent } from "../components/BlockContent"; import { Spinner } from "../components/Spinner"; import { useCommand } from "../hooks/use-command"; @@ -60,15 +61,17 @@ export function BlockCommentsCommand({ id, page = 1, per, + sort, }: { id: number; page?: number; per?: number; + sort?: ConnectionSort; }) { const { data, error, loading } = useCommand(() => getData( client.GET("/v3/blocks/{id}/comments", { - params: { path: { id }, query: { page, per } }, + params: { path: { id }, query: { page, per, sort } }, }), ), ); diff --git a/src/commands/channel.tsx b/src/commands/channel.tsx index a963173..ee97eba 100644 --- a/src/commands/channel.tsx +++ b/src/commands/channel.tsx @@ -1,6 +1,6 @@ import { Box, Text, useApp } from "ink"; import { client, getData } from "../api/client"; -import type { Visibility } from "../api/types"; +import type { ChannelContentSort, Visibility } from "../api/types"; import { BlockItem } from "../components/BlockItem"; import { ChannelBlockViewer } from "../components/ChannelBlockViewer"; import { @@ -175,17 +175,21 @@ export function ChannelContentsCommand({ slug, page = 1, per = 24, + sort, + userId, }: { slug: string; page?: number; per?: number; + sort?: ChannelContentSort; + userId?: number; }) { const { data, error, loading } = useCommand(() => getData( client.GET("/v3/channels/{id}/contents", { params: { path: { id: slug }, - query: { page, per, sort: "position_desc" }, + query: { page, per, sort: sort ?? "position_desc", user_id: userId }, }, }), ), @@ -223,15 +227,17 @@ export function ChannelCreateCommand({ title, visibility, description, + groupId, }: { title: string; visibility?: Visibility; description?: string; + groupId?: number; }) { const { data, error, loading } = useCommand(() => getData( client.POST("/v3/channels", { - body: { title, visibility, description }, + body: { title, visibility, description, group_id: groupId }, }), ), ); diff --git a/src/commands/connect.tsx b/src/commands/connect.tsx index 2c6aa7a..ed5b7e5 100644 --- a/src/commands/connect.tsx +++ b/src/commands/connect.tsx @@ -1,14 +1,22 @@ import { Box, Text } from "ink"; import { client, getData } from "../api/client"; +import type { ConnectableType } from "../api/types"; import { Spinner } from "../components/Spinner"; import { useCommand } from "../hooks/use-command"; interface Props { blockId: number; channel: string; + connectableType?: ConnectableType; + position?: number; } -export function ConnectCommand({ blockId, channel }: Props) { +export function ConnectCommand({ + blockId, + channel, + connectableType = "Block", + position, +}: Props) { const { data, error, loading } = useCommand(async () => { const ch = await getData( client.GET("/v3/channels/{id}", { @@ -19,8 +27,9 @@ export function ConnectCommand({ blockId, channel }: Props) { await client.POST("/v3/connections", { body: { connectable_id: blockId, - connectable_type: "Block", + connectable_type: connectableType, channel_ids: [ch.id], + position, }, }); return { channel: ch }; diff --git a/src/commands/connection.tsx b/src/commands/connection.tsx index 4e26c14..942c70d 100644 --- a/src/commands/connection.tsx +++ b/src/commands/connection.tsx @@ -1,6 +1,6 @@ import { Box, Text } from "ink"; import { client, getData } from "../api/client"; -import type { Movement } from "../api/types"; +import type { ConnectionFilter, ConnectionSort, Movement } from "../api/types"; import { Spinner } from "../components/Spinner"; import { useCommand } from "../hooks/use-command"; import { plural } from "../lib/format"; @@ -87,15 +87,19 @@ export function BlockConnectionsCommand({ id, page = 1, per, + sort, + filter, }: { id: number; page?: number; per?: number; + sort?: ConnectionSort; + filter?: ConnectionFilter; }) { const { data, error, loading } = useCommand(() => getData( client.GET("/v3/blocks/{id}/connections", { - params: { path: { id }, query: { page, per } }, + params: { path: { id }, query: { page, per, sort, filter } }, }), ), ); @@ -132,15 +136,17 @@ export function ChannelConnectionsCommand({ slug, page = 1, per, + sort, }: { slug: string; page?: number; per?: number; + sort?: ConnectionSort; }) { const { data, error, loading } = useCommand(() => getData( client.GET("/v3/channels/{id}/connections", { - params: { path: { id: slug }, query: { page, per } }, + params: { path: { id: slug }, query: { page, per, sort } }, }), ), ); @@ -177,15 +183,17 @@ export function ChannelFollowersCommand({ slug, page = 1, per, + sort, }: { slug: string; page?: number; per?: number; + sort?: ConnectionSort; }) { const { data, error, loading } = useCommand(() => getData( client.GET("/v3/channels/{id}/followers", { - params: { path: { id: slug }, query: { page, per } }, + params: { path: { id: slug }, query: { page, per, sort } }, }), ), ); diff --git a/src/commands/group.tsx b/src/commands/group.tsx index b46f49c..04e73e9 100644 --- a/src/commands/group.tsx +++ b/src/commands/group.tsx @@ -1,6 +1,10 @@ import { Box, Text } from "ink"; import { client, getData } from "../api/client"; -import type { ContentTypeFilter } from "../api/types"; +import type { + ConnectionSort, + ContentSort, + ContentTypeFilter, +} from "../api/types"; import { BlockItem } from "../components/BlockItem"; import { Spinner } from "../components/Spinner"; import { useCommand } from "../hooks/use-command"; @@ -42,6 +46,7 @@ interface GroupContentsProps { page?: number; per?: number; type?: string; + sort?: ContentSort; } export function GroupContentsCommand({ @@ -49,13 +54,19 @@ export function GroupContentsCommand({ page = 1, per, type, + sort, }: GroupContentsProps) { const { data, error, loading } = useCommand(() => getData( client.GET("/v3/groups/{id}/contents", { params: { path: { id: slug }, - query: { page, per, type: type as ContentTypeFilter | undefined }, + query: { + page, + per, + type: type as ContentTypeFilter | undefined, + sort, + }, }, }), ), @@ -84,17 +95,19 @@ interface GroupFollowersProps { slug: string; page?: number; per?: number; + sort?: ConnectionSort; } export function GroupFollowersCommand({ slug, page = 1, per, + sort, }: GroupFollowersProps) { const { data, error, loading } = useCommand(() => getData( client.GET("/v3/groups/{id}/followers", { - params: { path: { id: slug }, query: { page, per } }, + params: { path: { id: slug }, query: { page, per, sort } }, }), ), ); diff --git a/src/commands/user.tsx b/src/commands/user.tsx index 231bbab..200bdb9 100644 --- a/src/commands/user.tsx +++ b/src/commands/user.tsx @@ -1,6 +1,11 @@ import { Box, Text } from "ink"; import { client, getData } from "../api/client"; -import type { ContentTypeFilter, FollowableType } from "../api/types"; +import type { + ConnectionSort, + ContentSort, + ContentTypeFilter, + FollowableType, +} from "../api/types"; import { BlockItem } from "../components/BlockItem"; import { Spinner } from "../components/Spinner"; import { useCommand } from "../hooks/use-command"; @@ -37,18 +42,25 @@ export function UserContentsCommand({ page = 1, per, type, + sort, }: { slug: string; page?: number; per?: number; type?: string; + sort?: ContentSort; }) { const { data, error, loading } = useCommand(() => getData( client.GET("/v3/users/{id}/contents", { params: { path: { id: slug }, - query: { page, per, type: type as ContentTypeFilter | undefined }, + query: { + page, + per, + type: type as ContentTypeFilter | undefined, + sort, + }, }, }), ), @@ -77,15 +89,17 @@ export function UserFollowersCommand({ slug, page = 1, per, + sort, }: { slug: string; page?: number; per?: number; + sort?: ConnectionSort; }) { const { data, error, loading } = useCommand(() => getData( client.GET("/v3/users/{id}/followers", { - params: { path: { id: slug }, query: { page, per } }, + params: { path: { id: slug }, query: { page, per, sort } }, }), ), ); @@ -116,18 +130,25 @@ export function UserFollowingCommand({ page = 1, per, type, + sort, }: { slug: string; page?: number; per?: number; type?: string; + sort?: ConnectionSort; }) { const { data, error, loading } = useCommand(() => getData( client.GET("/v3/users/{id}/following", { params: { path: { id: slug }, - query: { page, per, type: type as FollowableType | undefined }, + query: { + page, + per, + type: type as FollowableType | undefined, + sort, + }, }, }), ), diff --git a/src/lib/registry.tsx b/src/lib/registry.tsx index 7027690..a4b1282 100644 --- a/src/lib/registry.tsx +++ b/src/lib/registry.tsx @@ -119,14 +119,28 @@ export const commands: CommandDefinition[] = [ usage: "channel contents ", description: "Channel contents (paginated)", }, - { usage: "channel create ", description: "Create a channel" }, - { usage: "channel update <slug>", description: "Update a channel" }, + { + usage: "channel contents <slug> --sort updated_at_desc --user-id 123", + description: "Sort/filter channel contents", + }, + { + usage: "channel create <title> --visibility private --group-id 123", + description: "Create a channel (optionally in a group)", + }, + { + usage: + 'channel update <slug> --title "New title" --description "Updated"', + description: "Update channel metadata", + }, { usage: "channel delete <slug>", description: "Delete a channel" }, { - usage: "channel connections <slug>", - description: "Where channel appears", + usage: "channel connections <slug> --sort connected_at_desc", + description: "Where channel appears (sortable)", + }, + { + usage: "channel followers <slug> --sort connected_at_desc", + description: "Channel followers (sortable)", }, - { usage: "channel followers <slug>", description: "Channel followers" }, ], destructive: { subcommands: { @@ -144,6 +158,7 @@ export const commands: CommandDefinition[] = [ title={requireArg(args, 1, "title")} visibility={visibility} description={flag(flags, "description")} + groupId={intFlag(flags, "group-id")} /> ); case "update": @@ -163,6 +178,10 @@ export const commands: CommandDefinition[] = [ slug={requireArg(args, 1, "slug")} page={optPage(flags)} per={optPer(flags)} + sort={ + flagAs<ChannelContentSort>(flags, "sort") ?? "position_desc" + } + userId={intFlag(flags, "user-id")} /> ); case "connections": @@ -171,6 +190,7 @@ export const commands: CommandDefinition[] = [ slug={requireArg(args, 1, "slug")} page={optPage(flags)} per={optPer(flags)} + sort={flagAs<ConnectionSort>(flags, "sort")} /> ); case "followers": @@ -179,6 +199,7 @@ export const commands: CommandDefinition[] = [ slug={requireArg(args, 1, "slug")} page={optPage(flags)} per={optPer(flags)} + sort={flagAs<ConnectionSort>(flags, "sort")} /> ); default: @@ -280,9 +301,18 @@ export const commands: CommandDefinition[] = [ group: "Blocks", help: [ { usage: "block <id>", description: "View a block" }, - { usage: "block update <id>", description: "Update a block" }, - { usage: "block comments <id>", description: "View block comments" }, - { usage: "block connections <id>", description: "Where block appears" }, + { + usage: 'block update <id> --title "New" --description "Updated"', + description: "Update block metadata/content", + }, + { + usage: "block comments <id> --sort connected_at_desc", + description: "View block comments (sortable)", + }, + { + usage: "block connections <id> --sort connected_at_desc --filter OWN", + description: "Where block appears (sortable/filterable)", + }, ], session: { args: "<id>", desc: "View a block" }, render(args, flags) { @@ -304,6 +334,7 @@ export const commands: CommandDefinition[] = [ id={idArg(args, 1, "block id")} page={optPage(flags)} per={optPer(flags)} + sort={flagAs<ConnectionSort>(flags, "sort")} /> ); case "connections": @@ -312,6 +343,8 @@ export const commands: CommandDefinition[] = [ id={idArg(args, 1, "block id")} page={optPage(flags)} per={optPer(flags)} + sort={flagAs<ConnectionSort>(flags, "sort")} + filter={flagAs<ConnectionFilter>(flags, "filter")} /> ); default: @@ -463,6 +496,23 @@ export const commands: CommandDefinition[] = [ usage: "add <channel> <value>", description: "Add content to a channel", }, + { + usage: 'add <channel> <value> --title "Title" --description "Notes"', + description: "Add with title/description", + }, + { + usage: 'add <channel> <value> --alt-text "Accessible text"', + description: "Add image alt text", + }, + { + usage: + 'add <channel> <value> --original-source-url <url> --original-source-title "Source"', + description: "Attach original source metadata", + }, + { + usage: "add <channel> <value> --insert-at 1", + description: "Insert at a specific position", + }, ], render(args, flags) { const value = requireArg([args.slice(1).join(" ")], 0, "value"); @@ -470,7 +520,12 @@ export const commands: CommandDefinition[] = [ <AddCommand channel={requireArg(args, 0, "channel")} value={value} + title={flag(flags, "title")} description={flag(flags, "description")} + altText={flag(flags, "alt-text")} + originalSourceUrl={flag(flags, "original-source-url")} + originalSourceTitle={flag(flags, "original-source-title")} + insertAt={intFlag(flags, "insert-at")} /> ); }, @@ -512,6 +567,11 @@ export const commands: CommandDefinition[] = [ usage: "upload <file> --channel <ch>", description: "Upload a file", }, + { + usage: + 'upload <file> --channel <ch> --title "Title" --description "Notes"', + description: "Upload with metadata", + }, ], render(args, flags) { return ( @@ -629,12 +689,18 @@ export const commands: CommandDefinition[] = [ usage: "connect <id> <channel>", description: "Connect block to channel", }, + { + usage: "connect <id> <channel> --type Channel --position 1", + description: "Set connectable type and insertion position", + }, ], - render(args) { + render(args, flags) { return ( <ConnectCommand blockId={idArg(args, 0, "block id")} channel={requireArg(args, 1, "channel")} + connectableType={flagAs<"Block" | "Channel">(flags, "type")} + position={intFlag(flags, "position")} /> ); }, @@ -660,9 +726,13 @@ export const commands: CommandDefinition[] = [ { usage: "connection <id>", description: "View a connection" }, { usage: "connection delete <id>", description: "Remove a connection" }, { - usage: "connection move <id>", + usage: "connection move <id> --movement move_to_top", description: "Reposition a connection", }, + { + usage: "connection move <id> --movement insert_at --position 1", + description: "Move connection to explicit position", + }, ], destructive: { subcommands: { @@ -769,9 +839,18 @@ export const commands: CommandDefinition[] = [ group: "Users & Groups", help: [ { usage: "user <slug>", description: "View a user" }, - { usage: "user contents <slug>", description: "User's content" }, - { usage: "user followers <slug>", description: "User's followers" }, - { usage: "user following <slug>", description: "Who user follows" }, + { + usage: "user contents <slug> --type Image --sort updated_at_desc", + description: "User's content (filter/sort)", + }, + { + usage: "user followers <slug> --sort connected_at_desc", + description: "User's followers (sortable)", + }, + { + usage: "user following <slug> --type User --sort connected_at_desc", + description: "Who user follows (filter/sort)", + }, ], session: { args: "<slug>", desc: "View a user profile" }, render(args, flags) { @@ -784,6 +863,7 @@ export const commands: CommandDefinition[] = [ page={optPage(flags)} per={optPer(flags)} type={flag(flags, "type")} + sort={flagAs<ContentSort>(flags, "sort")} /> ); case "followers": @@ -792,6 +872,7 @@ export const commands: CommandDefinition[] = [ slug={requireArg(args, 1, "slug")} page={optPage(flags)} per={optPer(flags)} + sort={flagAs<ConnectionSort>(flags, "sort")} /> ); case "following": @@ -801,6 +882,7 @@ export const commands: CommandDefinition[] = [ page={optPage(flags)} per={optPer(flags)} type={flag(flags, "type")} + sort={flagAs<ConnectionSort>(flags, "sort")} /> ); default: @@ -867,8 +949,14 @@ export const commands: CommandDefinition[] = [ group: "Users & Groups", help: [ { usage: "group <slug>", description: "View a group" }, - { usage: "group contents <slug>", description: "Group's content" }, - { usage: "group followers <slug>", description: "Group's followers" }, + { + usage: "group contents <slug> --type Image --sort updated_at_desc", + description: "Group's content (filter/sort)", + }, + { + usage: "group followers <slug> --sort connected_at_desc", + description: "Group's followers (sortable)", + }, ], session: { args: "<slug>", desc: "View a group profile" }, render(args, flags) { @@ -881,6 +969,7 @@ export const commands: CommandDefinition[] = [ page={optPage(flags)} per={optPer(flags)} type={flag(flags, "type")} + sort={flagAs<ContentSort>(flags, "sort")} /> ); case "followers": @@ -889,6 +978,7 @@ export const commands: CommandDefinition[] = [ slug={requireArg(args, 1, "slug")} page={optPage(flags)} per={optPer(flags)} + sort={flagAs<ConnectionSort>(flags, "sort")} /> ); default: