floq 0.2.3 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +4 -0
- package/README.md +4 -0
- package/dist/i18n/en.d.ts +16 -0
- package/dist/i18n/en.js +9 -0
- package/dist/i18n/ja.js +9 -0
- package/dist/ui/App.js +116 -58
- package/dist/ui/SplashScreen.js +1 -1
- package/dist/ui/components/HelpModal.js +3 -2
- package/dist/ui/components/KanbanBoard.js +100 -40
- package/dist/ui/history/HistoryContext.d.ts +29 -0
- package/dist/ui/history/HistoryContext.js +44 -0
- package/dist/ui/history/HistoryManager.d.ts +56 -0
- package/dist/ui/history/HistoryManager.js +137 -0
- package/dist/ui/history/commands/ConvertToProjectCommand.d.ts +19 -0
- package/dist/ui/history/commands/ConvertToProjectCommand.js +37 -0
- package/dist/ui/history/commands/CreateCommentCommand.d.ts +18 -0
- package/dist/ui/history/commands/CreateCommentCommand.js +23 -0
- package/dist/ui/history/commands/CreateTaskCommand.d.ts +18 -0
- package/dist/ui/history/commands/CreateTaskCommand.js +24 -0
- package/dist/ui/history/commands/DeleteCommentCommand.d.ts +17 -0
- package/dist/ui/history/commands/DeleteCommentCommand.js +26 -0
- package/dist/ui/history/commands/DeleteTaskCommand.d.ts +18 -0
- package/dist/ui/history/commands/DeleteTaskCommand.js +51 -0
- package/dist/ui/history/commands/LinkTaskCommand.d.ts +20 -0
- package/dist/ui/history/commands/LinkTaskCommand.js +37 -0
- package/dist/ui/history/commands/MoveTaskCommand.d.ts +25 -0
- package/dist/ui/history/commands/MoveTaskCommand.js +43 -0
- package/dist/ui/history/commands/index.d.ts +7 -0
- package/dist/ui/history/commands/index.js +7 -0
- package/dist/ui/history/index.d.ts +6 -0
- package/dist/ui/history/index.js +8 -0
- package/dist/ui/history/types.d.ts +26 -0
- package/dist/ui/history/types.js +4 -0
- package/dist/ui/history/useHistory.d.ts +24 -0
- package/dist/ui/history/useHistory.js +31 -0
- package/package.json +3 -3
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { UndoableCommand } from '../types.js';
|
|
2
|
+
import type { Task } from '../../../db/schema.js';
|
|
3
|
+
interface DeleteTaskParams {
|
|
4
|
+
task: Task;
|
|
5
|
+
description: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Command to delete a task (and its comments)
|
|
9
|
+
*/
|
|
10
|
+
export declare class DeleteTaskCommand implements UndoableCommand {
|
|
11
|
+
readonly description: string;
|
|
12
|
+
private readonly task;
|
|
13
|
+
private savedComments;
|
|
14
|
+
constructor(params: DeleteTaskParams);
|
|
15
|
+
execute(): Promise<void>;
|
|
16
|
+
undo(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { eq } from 'drizzle-orm';
|
|
2
|
+
import { getDb, schema } from '../../../db/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Command to delete a task (and its comments)
|
|
5
|
+
*/
|
|
6
|
+
export class DeleteTaskCommand {
|
|
7
|
+
description;
|
|
8
|
+
task;
|
|
9
|
+
savedComments = [];
|
|
10
|
+
constructor(params) {
|
|
11
|
+
this.task = params.task;
|
|
12
|
+
this.description = params.description;
|
|
13
|
+
}
|
|
14
|
+
async execute() {
|
|
15
|
+
const db = getDb();
|
|
16
|
+
// Save comments before deleting
|
|
17
|
+
this.savedComments = await db
|
|
18
|
+
.select()
|
|
19
|
+
.from(schema.comments)
|
|
20
|
+
.where(eq(schema.comments.taskId, this.task.id));
|
|
21
|
+
// Delete comments first
|
|
22
|
+
await db.delete(schema.comments).where(eq(schema.comments.taskId, this.task.id));
|
|
23
|
+
// Delete the task
|
|
24
|
+
await db.delete(schema.tasks).where(eq(schema.tasks.id, this.task.id));
|
|
25
|
+
}
|
|
26
|
+
async undo() {
|
|
27
|
+
const db = getDb();
|
|
28
|
+
// Restore the task
|
|
29
|
+
await db.insert(schema.tasks).values({
|
|
30
|
+
id: this.task.id,
|
|
31
|
+
title: this.task.title,
|
|
32
|
+
description: this.task.description,
|
|
33
|
+
status: this.task.status,
|
|
34
|
+
isProject: this.task.isProject,
|
|
35
|
+
parentId: this.task.parentId,
|
|
36
|
+
waitingFor: this.task.waitingFor,
|
|
37
|
+
dueDate: this.task.dueDate,
|
|
38
|
+
createdAt: this.task.createdAt,
|
|
39
|
+
updatedAt: this.task.updatedAt,
|
|
40
|
+
});
|
|
41
|
+
// Restore comments
|
|
42
|
+
for (const comment of this.savedComments) {
|
|
43
|
+
await db.insert(schema.comments).values({
|
|
44
|
+
id: comment.id,
|
|
45
|
+
taskId: comment.taskId,
|
|
46
|
+
content: comment.content,
|
|
47
|
+
createdAt: comment.createdAt,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { UndoableCommand } from '../types.js';
|
|
2
|
+
interface LinkTaskParams {
|
|
3
|
+
taskId: string;
|
|
4
|
+
fromParentId: string | null;
|
|
5
|
+
toParentId: string | null;
|
|
6
|
+
description: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Command to link/unlink a task to a project
|
|
10
|
+
*/
|
|
11
|
+
export declare class LinkTaskCommand implements UndoableCommand {
|
|
12
|
+
readonly description: string;
|
|
13
|
+
private readonly taskId;
|
|
14
|
+
private readonly fromParentId;
|
|
15
|
+
private readonly toParentId;
|
|
16
|
+
constructor(params: LinkTaskParams);
|
|
17
|
+
execute(): Promise<void>;
|
|
18
|
+
undo(): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { eq } from 'drizzle-orm';
|
|
2
|
+
import { getDb, schema } from '../../../db/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Command to link/unlink a task to a project
|
|
5
|
+
*/
|
|
6
|
+
export class LinkTaskCommand {
|
|
7
|
+
description;
|
|
8
|
+
taskId;
|
|
9
|
+
fromParentId;
|
|
10
|
+
toParentId;
|
|
11
|
+
constructor(params) {
|
|
12
|
+
this.taskId = params.taskId;
|
|
13
|
+
this.fromParentId = params.fromParentId;
|
|
14
|
+
this.toParentId = params.toParentId;
|
|
15
|
+
this.description = params.description;
|
|
16
|
+
}
|
|
17
|
+
async execute() {
|
|
18
|
+
const db = getDb();
|
|
19
|
+
await db
|
|
20
|
+
.update(schema.tasks)
|
|
21
|
+
.set({
|
|
22
|
+
parentId: this.toParentId,
|
|
23
|
+
updatedAt: new Date(),
|
|
24
|
+
})
|
|
25
|
+
.where(eq(schema.tasks.id, this.taskId));
|
|
26
|
+
}
|
|
27
|
+
async undo() {
|
|
28
|
+
const db = getDb();
|
|
29
|
+
await db
|
|
30
|
+
.update(schema.tasks)
|
|
31
|
+
.set({
|
|
32
|
+
parentId: this.fromParentId,
|
|
33
|
+
updatedAt: new Date(),
|
|
34
|
+
})
|
|
35
|
+
.where(eq(schema.tasks.id, this.taskId));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { UndoableCommand } from '../types.js';
|
|
2
|
+
import type { TaskStatus } from '../../../db/schema.js';
|
|
3
|
+
interface MoveTaskParams {
|
|
4
|
+
taskId: string;
|
|
5
|
+
fromStatus: TaskStatus;
|
|
6
|
+
toStatus: TaskStatus;
|
|
7
|
+
fromWaitingFor: string | null;
|
|
8
|
+
toWaitingFor: string | null;
|
|
9
|
+
description: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Command to move a task to a different status
|
|
13
|
+
*/
|
|
14
|
+
export declare class MoveTaskCommand implements UndoableCommand {
|
|
15
|
+
readonly description: string;
|
|
16
|
+
private readonly taskId;
|
|
17
|
+
private readonly fromStatus;
|
|
18
|
+
private readonly toStatus;
|
|
19
|
+
private readonly fromWaitingFor;
|
|
20
|
+
private readonly toWaitingFor;
|
|
21
|
+
constructor(params: MoveTaskParams);
|
|
22
|
+
execute(): Promise<void>;
|
|
23
|
+
undo(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { eq } from 'drizzle-orm';
|
|
2
|
+
import { getDb, schema } from '../../../db/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Command to move a task to a different status
|
|
5
|
+
*/
|
|
6
|
+
export class MoveTaskCommand {
|
|
7
|
+
description;
|
|
8
|
+
taskId;
|
|
9
|
+
fromStatus;
|
|
10
|
+
toStatus;
|
|
11
|
+
fromWaitingFor;
|
|
12
|
+
toWaitingFor;
|
|
13
|
+
constructor(params) {
|
|
14
|
+
this.taskId = params.taskId;
|
|
15
|
+
this.fromStatus = params.fromStatus;
|
|
16
|
+
this.toStatus = params.toStatus;
|
|
17
|
+
this.fromWaitingFor = params.fromWaitingFor;
|
|
18
|
+
this.toWaitingFor = params.toWaitingFor;
|
|
19
|
+
this.description = params.description;
|
|
20
|
+
}
|
|
21
|
+
async execute() {
|
|
22
|
+
const db = getDb();
|
|
23
|
+
await db
|
|
24
|
+
.update(schema.tasks)
|
|
25
|
+
.set({
|
|
26
|
+
status: this.toStatus,
|
|
27
|
+
waitingFor: this.toWaitingFor,
|
|
28
|
+
updatedAt: new Date(),
|
|
29
|
+
})
|
|
30
|
+
.where(eq(schema.tasks.id, this.taskId));
|
|
31
|
+
}
|
|
32
|
+
async undo() {
|
|
33
|
+
const db = getDb();
|
|
34
|
+
await db
|
|
35
|
+
.update(schema.tasks)
|
|
36
|
+
.set({
|
|
37
|
+
status: this.fromStatus,
|
|
38
|
+
waitingFor: this.fromWaitingFor,
|
|
39
|
+
updatedAt: new Date(),
|
|
40
|
+
})
|
|
41
|
+
.where(eq(schema.tasks.id, this.taskId));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { CreateTaskCommand } from './CreateTaskCommand.js';
|
|
2
|
+
export { DeleteTaskCommand } from './DeleteTaskCommand.js';
|
|
3
|
+
export { MoveTaskCommand } from './MoveTaskCommand.js';
|
|
4
|
+
export { LinkTaskCommand } from './LinkTaskCommand.js';
|
|
5
|
+
export { ConvertToProjectCommand } from './ConvertToProjectCommand.js';
|
|
6
|
+
export { CreateCommentCommand } from './CreateCommentCommand.js';
|
|
7
|
+
export { DeleteCommentCommand } from './DeleteCommentCommand.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { CreateTaskCommand } from './CreateTaskCommand.js';
|
|
2
|
+
export { DeleteTaskCommand } from './DeleteTaskCommand.js';
|
|
3
|
+
export { MoveTaskCommand } from './MoveTaskCommand.js';
|
|
4
|
+
export { LinkTaskCommand } from './LinkTaskCommand.js';
|
|
5
|
+
export { ConvertToProjectCommand } from './ConvertToProjectCommand.js';
|
|
6
|
+
export { CreateCommentCommand } from './CreateCommentCommand.js';
|
|
7
|
+
export { DeleteCommentCommand } from './DeleteCommentCommand.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { UndoableCommand, HistoryState } from './types.js';
|
|
2
|
+
export { MAX_HISTORY_SIZE } from './types.js';
|
|
3
|
+
export { HistoryManager, getHistoryManager } from './HistoryManager.js';
|
|
4
|
+
export { HistoryProvider, useHistoryContext } from './HistoryContext.js';
|
|
5
|
+
export { useHistory } from './useHistory.js';
|
|
6
|
+
export { CreateTaskCommand, DeleteTaskCommand, MoveTaskCommand, LinkTaskCommand, ConvertToProjectCommand, CreateCommentCommand, DeleteCommentCommand, } from './commands/index.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { MAX_HISTORY_SIZE } from './types.js';
|
|
2
|
+
// Manager
|
|
3
|
+
export { HistoryManager, getHistoryManager } from './HistoryManager.js';
|
|
4
|
+
// React integration
|
|
5
|
+
export { HistoryProvider, useHistoryContext } from './HistoryContext.js';
|
|
6
|
+
export { useHistory } from './useHistory.js';
|
|
7
|
+
// Commands
|
|
8
|
+
export { CreateTaskCommand, DeleteTaskCommand, MoveTaskCommand, LinkTaskCommand, ConvertToProjectCommand, CreateCommentCommand, DeleteCommentCommand, } from './commands/index.js';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface for undoable commands (Command Pattern)
|
|
3
|
+
*/
|
|
4
|
+
export interface UndoableCommand {
|
|
5
|
+
/** Human-readable description of the command for display */
|
|
6
|
+
readonly description: string;
|
|
7
|
+
/** Execute the command */
|
|
8
|
+
execute(): Promise<void>;
|
|
9
|
+
/** Undo the command (reverse the operation) */
|
|
10
|
+
undo(): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* State of the history manager
|
|
14
|
+
*/
|
|
15
|
+
export interface HistoryState {
|
|
16
|
+
/** Number of commands that can be undone */
|
|
17
|
+
undoCount: number;
|
|
18
|
+
/** Number of commands that can be redone */
|
|
19
|
+
redoCount: number;
|
|
20
|
+
/** Description of the last executed command (if any) */
|
|
21
|
+
lastCommandDescription: string | null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Maximum number of commands to keep in history
|
|
25
|
+
*/
|
|
26
|
+
export declare const MAX_HISTORY_SIZE = 50;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { UndoableCommand } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Hook for undo/redo functionality
|
|
4
|
+
*/
|
|
5
|
+
export declare function useHistory(): {
|
|
6
|
+
/** Execute a command and add to history */
|
|
7
|
+
execute: (command: UndoableCommand) => Promise<void>;
|
|
8
|
+
/** Undo the last command */
|
|
9
|
+
undo: () => Promise<boolean>;
|
|
10
|
+
/** Redo the last undone command */
|
|
11
|
+
redo: () => Promise<boolean>;
|
|
12
|
+
/** Whether undo is available */
|
|
13
|
+
canUndo: boolean;
|
|
14
|
+
/** Whether redo is available */
|
|
15
|
+
canRedo: boolean;
|
|
16
|
+
/** Number of commands that can be undone */
|
|
17
|
+
undoCount: number;
|
|
18
|
+
/** Number of commands that can be redone */
|
|
19
|
+
redoCount: number;
|
|
20
|
+
/** Description of what would be undone */
|
|
21
|
+
undoDescription: string | null;
|
|
22
|
+
/** Description of what would be redone */
|
|
23
|
+
redoDescription: string | null;
|
|
24
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { useHistoryContext } from './HistoryContext.js';
|
|
3
|
+
/**
|
|
4
|
+
* Hook for undo/redo functionality
|
|
5
|
+
*/
|
|
6
|
+
export function useHistory() {
|
|
7
|
+
const { execute, undo, redo, canUndo, canRedo, state, undoDescription, redoDescription } = useHistoryContext();
|
|
8
|
+
const executeCommand = useCallback(async (command) => {
|
|
9
|
+
await execute(command);
|
|
10
|
+
}, [execute]);
|
|
11
|
+
return {
|
|
12
|
+
/** Execute a command and add to history */
|
|
13
|
+
execute: executeCommand,
|
|
14
|
+
/** Undo the last command */
|
|
15
|
+
undo,
|
|
16
|
+
/** Redo the last undone command */
|
|
17
|
+
redo,
|
|
18
|
+
/** Whether undo is available */
|
|
19
|
+
canUndo,
|
|
20
|
+
/** Whether redo is available */
|
|
21
|
+
canRedo,
|
|
22
|
+
/** Number of commands that can be undone */
|
|
23
|
+
undoCount: state.undoCount,
|
|
24
|
+
/** Number of commands that can be redone */
|
|
25
|
+
redoCount: state.redoCount,
|
|
26
|
+
/** Description of what would be undone */
|
|
27
|
+
undoDescription,
|
|
28
|
+
/** Description of what would be redone */
|
|
29
|
+
redoDescription,
|
|
30
|
+
};
|
|
31
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "floq",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Floq - Getting Things Done Task Manager with MS-DOS style themes",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
"license": "MIT",
|
|
34
34
|
"repository": {
|
|
35
35
|
"type": "git",
|
|
36
|
-
"url": "git+https://github.com/polidog/
|
|
36
|
+
"url": "git+https://github.com/polidog/floq.git"
|
|
37
37
|
},
|
|
38
|
-
"homepage": "https://github.com/polidog/
|
|
38
|
+
"homepage": "https://github.com/polidog/floq#readme",
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@libsql/client": "^0.17.0",
|
|
41
41
|
"better-sqlite3": "^11.7.0",
|