fix(inbox): Fix tag/project autocomplete selection (#1043)

* fix(mcp): Include subtasks in get_task API response

Add Subtasks association to the findTaskByIdentifier function
so that the get_task MCP API endpoint returns subtasks along
with the main task. This enables clients to access the full
task hierarchy in a single API call.

The serializeTask function already supported subtasks
serialization, so this change only required updating the
query includes to load the Subtasks relation with proper
ordering and Tag associations.

Fixes #1029

* fix(inbox): Fix tag/project autocomplete selection

Fixes #996

Previously, when creating a task from inbox with autocomplete suggestions,
the tag/project replacement would fail if there was regular text before
the hashtag or plus sign. This caused two issues:

1. When typing "#technical_writing" and creating a task, the tag wouldn't
   be created or applied because the autocomplete wasn't replacing the input
2. When typing "#tech_" and selecting "technical_writing" from autocomplete,
   a new tag "tech_" would be created instead of applying the existing tag

This was caused by an overly restrictive condition in handleTagSelect and
handleProjectSelect that prevented replacement when there was regular text
before the tag/project marker.

Changes:
- Removed the allowReplacement condition that blocked autocomplete when
  regular text preceded the tag/project marker
- Simplified handleTagSelect and handleProjectSelect to always replace
  partial input when a suggestion is selected
- Added a space after the selected tag/project for better UX
This commit is contained in:
Chris 2026-04-18 10:04:57 +03:00 committed by GitHub
parent 0440f46645
commit dcb711c515
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -910,53 +910,29 @@ const QuickCaptureInput = React.forwardRef<
const hashtagMatch = beforeCursor.match(/#([a-zA-Z0-9_]*)$/);
if (hashtagMatch) {
const hashtagStart = beforeCursor.lastIndexOf('#');
const textBeforeHashtag = inputText
.substring(0, hashtagStart)
.trim();
const textAfterCursor = afterCursor.trim();
const newText =
beforeCursor.replace(
/#([a-zA-Z0-9_]*)$/,
`#${tagName} `
) + afterCursor;
setInputText(newText);
setShowTagSuggestions(false);
setFilteredTags([]);
setSelectedSuggestionIndex(-1);
let allowReplacement = false;
if (textAfterCursor === '' || textBeforeHashtag === '') {
allowReplacement = true;
} else {
const wordsBeforeHashtag = textBeforeHashtag
.split(/\s+/)
.filter((word) => word.length > 0);
const allWordsAreTagsOrProjects = wordsBeforeHashtag.every(
(word) => word.startsWith('#') || word.startsWith('+')
);
if (allWordsAreTagsOrProjects) {
allowReplacement = true;
}
}
if (allowReplacement) {
const newText =
beforeCursor.replace(
setTimeout(() => {
if (inputRef.current) {
inputRef.current.focus();
const newCursorPos = beforeCursor.replace(
/#([a-zA-Z0-9_]*)$/,
`#${tagName}`
) + afterCursor;
setInputText(newText);
setShowTagSuggestions(false);
setFilteredTags([]);
setSelectedSuggestionIndex(-1);
setTimeout(() => {
if (inputRef.current) {
inputRef.current.focus();
const newCursorPos = beforeCursor.replace(
/#([a-zA-Z0-9_]*)$/,
`#${tagName}`
).length;
inputRef.current.setSelectionRange(
newCursorPos,
newCursorPos
);
}
}, 0);
}
`#${tagName} `
).length;
inputRef.current.setSelectionRange(
newCursorPos,
newCursorPos
);
}
}, 0);
}
};
@ -968,57 +944,33 @@ const QuickCaptureInput = React.forwardRef<
);
if (projectMatch) {
const projectStart = beforeCursor.lastIndexOf('+');
const textBeforeProject = inputText
.substring(0, projectStart)
.trim();
const textAfterCursor = afterCursor.trim();
const formattedProjectName = projectName.includes(' ')
? `"${projectName}"`
: projectName;
let allowReplacement = false;
const newText =
beforeCursor.replace(
/\+(?:"([^"]*)"|([a-zA-Z0-9_\s]*))$/,
`+${formattedProjectName} `
) + afterCursor;
setInputText(newText);
setShowProjectSuggestions(false);
setFilteredProjects([]);
setSelectedSuggestionIndex(-1);
if (textAfterCursor === '' || textBeforeProject === '') {
allowReplacement = true;
} else {
const wordsBeforeProject = textBeforeProject
.split(/\s+/)
.filter((word) => word.length > 0);
const allWordsAreTagsOrProjects = wordsBeforeProject.every(
(word) => word.startsWith('#') || word.startsWith('+')
);
if (allWordsAreTagsOrProjects) {
allowReplacement = true;
}
}
if (allowReplacement) {
const formattedProjectName = projectName.includes(' ')
? `"${projectName}"`
: projectName;
const newText =
beforeCursor.replace(
setTimeout(() => {
if (inputRef.current) {
inputRef.current.focus();
const newCursorPos = beforeCursor.replace(
/\+(?:"([^"]*)"|([a-zA-Z0-9_\s]*))$/,
`+${formattedProjectName}`
) + afterCursor;
setInputText(newText);
setShowProjectSuggestions(false);
setFilteredProjects([]);
setSelectedSuggestionIndex(-1);
setTimeout(() => {
if (inputRef.current) {
inputRef.current.focus();
const newCursorPos = beforeCursor.replace(
/\+(?:"([^"]*)"|([a-zA-Z0-9_\s]*))$/,
`+${formattedProjectName}`
).length;
inputRef.current.setSelectionRange(
newCursorPos,
newCursorPos
);
}
}, 0);
}
`+${formattedProjectName} `
).length;
inputRef.current.setSelectionRange(
newCursorPos,
newCursorPos
);
}
}, 0);
}
};