Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions extensions/vikunja/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

## [Create Task Quick Action & Preselect Project] - 2026-04-22

- Added Cmd+N / Create Task action in the `List Tasks` view and in each task's action panel to quickly create a new task.
- When creating a task from a project context (either the currently selected project in `List Tasks` or via a task's action), the `Create Task` form is opened with that project preselected.
- `create-task` now accepts an optional `projectId` argument and also respects `launchContext.projectId` for compatibility.

## [Default Project Preference] - 2026-04-19

- Add optional "Default Project" Raycast preference (`defaultProject`) to set the initial project shown in List Tasks (use "all" or a project id).
- `List Tasks` now respects the preference when opened without a launch context; explicit launch context `projectId` still takes precedence.
- Updated generated preference types and manifest to include the setting.

## [Task Detail View, Search, and Caching] - 2026-03-25

- Task Detail view with full markdown description and metadata sidebar
Expand Down
17 changes: 17 additions & 0 deletions extensions/vikunja/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
"description": "Create and manage tasks in your self-hosted Vikunja instance",
"icon": "command-icon.png",
"author": "flexorflex",
"contributors": [
"strikerlulu"
],
"categories": [
"Productivity"
],
Expand All @@ -22,6 +25,12 @@
"placeholder": "Task title",
"type": "text",
"required": false
},
{
"name": "projectId",
"placeholder": "Project ID (optional)",
"type": "text",
"required": false
}
]
},
Expand Down Expand Up @@ -62,6 +71,14 @@
"description": "Vikunja API token for authentication",
"type": "password",
"required": true
},
{
"name": "defaultProject",
"title": "Default Project",
"description": "Default project ID to show in List Tasks. Use \"all\" to show all projects.",
"type": "textfield",
"required": false,
"placeholder": "all"
}
],
"dependencies": {
Expand Down
44 changes: 0 additions & 44 deletions extensions/vikunja/raycast-env.d.ts

This file was deleted.

21 changes: 21 additions & 0 deletions extensions/vikunja/src/components/task-list-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
showToast,
Toast,
useNavigation,
launchCommand,
LaunchType,
} from "@raycast/api";
import { updateTask, Project, Task } from "../api";
import { formatDueDate, dueDateColor } from "../helpers/dates";
Expand Down Expand Up @@ -118,6 +120,25 @@ export function TaskActions({
return (
<ActionPanel>
<ActionPanel.Section>
<Action
title="Create Task in Project"
icon={Icon.Plus}
shortcut={{ modifiers: ["cmd"], key: "n" }}
onAction={async () => {
try {
await launchCommand({
name: "create-task",
type: LaunchType.UserInitiated,
arguments: { projectId: String(task.project_id) },
});
} catch {
showToast({
style: Toast.Style.Failure,
title: "Failed to launch Create Task",
});
}
}}
/>
{onShowDetail && (
<Action
title="View Details"
Expand Down
22 changes: 20 additions & 2 deletions extensions/vikunja/src/create-task.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,22 @@ import {
import { PRIORITY_MAP } from "./helpers/priorities";

export default function CreateTask(
props: LaunchProps<{ arguments: Arguments.CreateTask }>,
props: LaunchProps<{
arguments: Arguments.CreateTask;
launchContext?: { projectId?: number };
}>,
) {
const [projects, setProjects] = useState<Project[]>([]);
const [labels, setLabels] = useState<Label[]>([]);
const [isLoading, setIsLoading] = useState(true);
const argTitle = props.arguments.title?.trim() ?? "";
const [prefillTitle, setPrefillTitle] = useState(argTitle);
const [prefillReady, setPrefillReady] = useState(!!argTitle);
// Prefer projectId passed as a command argument, fallback to launch context if present
const argProjectId = props.arguments.projectId?.trim();
const contextProjectId = argProjectId
? parseInt(argProjectId)
: props.launchContext?.projectId;

useEffect(() => {
async function loadData() {
Expand Down Expand Up @@ -175,7 +183,17 @@ export default function CreateTask(
title="Description"
placeholder="Optional description"
/>
<Form.Dropdown id="projectId" title="Project">
<Form.Dropdown
id="projectId"
title="Project"
defaultValue={
props.arguments.projectId
? String(props.arguments.projectId)
: contextProjectId
? String(contextProjectId)
: undefined
}
>
{projects.map((project) => (
<Form.Dropdown.Item
key={project.id}
Expand Down
38 changes: 37 additions & 1 deletion extensions/vikunja/src/list-tasks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import {
confirmAlert,
getPreferenceValues,
LaunchProps,
Action,
ActionPanel,
launchCommand,
LaunchType,
} from "@raycast/api";
import { useCachedPromise } from "@raycast/utils";
import { useMemo, useState } from "react";
Expand All @@ -27,8 +31,10 @@ export default function ListTasks(
props: LaunchProps<{ launchContext: ListTasksContext }>,
) {
const initialProjectId = props.launchContext?.projectId;
const { defaultProject } = getPreferenceValues<Preferences>();
const defaultPref = defaultProject ?? "all";
const [selectedProject, setSelectedProject] = useState<string>(
initialProjectId ? String(initialProjectId) : "all",
initialProjectId ? String(initialProjectId) : defaultPref,
);

const baseUrl = useMemo(() => {
Expand Down Expand Up @@ -116,6 +122,36 @@ export default function ListTasks(
return (
<List
isLoading={isLoading}
actions={
<ActionPanel>
<Action
title="Create Task"
icon={Icon.Plus}
shortcut={{ modifiers: ["cmd"], key: "n" }}
onAction={async () => {
try {
if (selectedProject && selectedProject !== "all") {
await launchCommand({
name: "create-task",
type: LaunchType.UserInitiated,
arguments: { projectId: String(selectedProject) },
});
} else {
await launchCommand({
name: "create-task",
type: LaunchType.UserInitiated,
});
}
} catch {
showToast({
style: Toast.Style.Failure,
title: "Failed to launch Create Task",
});
}
}}
/>
</ActionPanel>
}
searchBarAccessory={
<List.Dropdown
tooltip="Project"
Expand Down