Make logging in work on frontend

This commit is contained in:
2024-11-04 21:51:38 +01:00
parent 9cdbf9fec8
commit 69afef15ec
16 changed files with 165 additions and 98 deletions

View File

@@ -45,8 +45,8 @@ func SetupRoutes(r chi.Router, db *db.DB, fs *filesystem.FileSystem, authMiddlew
r.Route("/workspaces", func(r chi.Router) { r.Route("/workspaces", func(r chi.Router) {
r.Get("/", handler.ListWorkspaces()) r.Get("/", handler.ListWorkspaces())
r.Post("/", handler.CreateWorkspace()) r.Post("/", handler.CreateWorkspace())
r.Get("/last", handler.GetLastWorkspace()) r.Get("/last", handler.GetLastWorkspaceName())
r.Put("/last", handler.UpdateLastWorkspace()) r.Put("/last", handler.UpdateLastWorkspaceName())
// Single workspace routes // Single workspace routes
r.Route("/{workspaceName}", func(r chi.Router) { r.Route("/{workspaceName}", func(r chi.Router) {

View File

@@ -120,9 +120,26 @@ func (db *DB) UpdateUser(user *models.User) error {
return err return err
} }
func (db *DB) UpdateLastWorkspace(userID, workspaceID int) error { func (db *DB) UpdateLastWorkspace(userID int, workspaceName string) error {
_, err := db.Exec("UPDATE users SET last_workspace_id = ? WHERE id = ?", workspaceID, userID) tx, err := db.Begin()
return err if err != nil {
return err
}
defer tx.Rollback()
var workspaceID int
err = tx.QueryRow("SELECT id FROM workspaces WHERE user_id = ? AND name = ?", userID, workspaceName).Scan(&workspaceID)
if err != nil {
return err
}
_, err = tx.Exec("UPDATE users SET last_workspace_id = ? WHERE id = ?", workspaceID, userID)
if err != nil {
return err
}
return tx.Commit()
} }
func (db *DB) DeleteUser(id int) error { func (db *DB) DeleteUser(id int) error {
@@ -147,8 +164,14 @@ func (db *DB) DeleteUser(id int) error {
return tx.Commit() return tx.Commit()
} }
func (db *DB) GetLastWorkspaceID(userID int) (int, error) { func (db *DB) GetLastWorkspaceName(userID int) (string, error) {
var workspaceID int var workspaceName string
err := db.QueryRow("SELECT last_workspace_id FROM users WHERE id = ?", userID).Scan(&workspaceID) err := db.QueryRow(`
return workspaceID, err SELECT
w.name
FROM workspaces w
JOIN users u ON u.last_workspace_id = w.id
WHERE u.id = ?`, userID).
Scan(&workspaceName)
return workspaceName, err
} }

View File

@@ -2,6 +2,7 @@ package handlers
import ( import (
"encoding/json" "encoding/json"
"fmt"
"net/http" "net/http"
"novamd/internal/httpcontext" "novamd/internal/httpcontext"
@@ -151,10 +152,12 @@ func (h *Handler) DeleteWorkspace() http.HandlerFunc {
} }
// Find another workspace to set as last // Find another workspace to set as last
var nextWorkspaceName string
var nextWorkspaceID int var nextWorkspaceID int
for _, ws := range workspaces { for _, ws := range workspaces {
if ws.ID != ctx.Workspace.ID { if ws.ID != ctx.Workspace.ID {
nextWorkspaceID = ws.ID nextWorkspaceID = ws.ID
nextWorkspaceName = ws.Name
break break
} }
} }
@@ -188,28 +191,28 @@ func (h *Handler) DeleteWorkspace() http.HandlerFunc {
} }
// Return the next workspace ID in the response so frontend knows where to redirect // Return the next workspace ID in the response so frontend knows where to redirect
respondJSON(w, map[string]int{"nextWorkspaceId": nextWorkspaceID}) respondJSON(w, map[string]string{"nextWorkspaceName": nextWorkspaceName})
} }
} }
func (h *Handler) GetLastWorkspace() http.HandlerFunc { func (h *Handler) GetLastWorkspaceName() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
ctx, ok := httpcontext.GetRequestContext(w, r) ctx, ok := httpcontext.GetRequestContext(w, r)
if !ok { if !ok {
return return
} }
workspaceID, err := h.DB.GetLastWorkspaceID(ctx.UserID) workspaceName, err := h.DB.GetLastWorkspaceName(ctx.UserID)
if err != nil { if err != nil {
http.Error(w, "Failed to get last workspace", http.StatusInternalServerError) http.Error(w, "Failed to get last workspace", http.StatusInternalServerError)
return return
} }
respondJSON(w, map[string]int{"lastWorkspaceId": workspaceID}) respondJSON(w, map[string]string{"lastWorkspaceName": workspaceName})
} }
} }
func (h *Handler) UpdateLastWorkspace() http.HandlerFunc { func (h *Handler) UpdateLastWorkspaceName() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
ctx, ok := httpcontext.GetRequestContext(w, r) ctx, ok := httpcontext.GetRequestContext(w, r)
if !ok { if !ok {
@@ -217,15 +220,17 @@ func (h *Handler) UpdateLastWorkspace() http.HandlerFunc {
} }
var requestBody struct { var requestBody struct {
WorkspaceID int `json:"workspaceId"` WorkspaceName string `json:"workspaceName"`
} }
if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil { if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil {
fmt.Println(err)
http.Error(w, "Invalid request body", http.StatusBadRequest) http.Error(w, "Invalid request body", http.StatusBadRequest)
return return
} }
if err := h.DB.UpdateLastWorkspace(ctx.UserID, requestBody.WorkspaceID); err != nil { if err := h.DB.UpdateLastWorkspace(ctx.UserID, requestBody.WorkspaceName); err != nil {
fmt.Println(err)
http.Error(w, "Failed to update last workspace", http.StatusInternalServerError) http.Error(w, "Failed to update last workspace", http.StatusInternalServerError)
return return
} }

View File

@@ -28,6 +28,7 @@
"rehype-mathjax": "^6.0.0", "rehype-mathjax": "^6.0.0",
"rehype-prism": "^2.3.3", "rehype-prism": "^2.3.3",
"rehype-react": "^8.0.0", "rehype-react": "^8.0.0",
"remark": "^15.0.1",
"remark-math": "^6.0.0", "remark-math": "^6.0.0",
"remark-parse": "^11.0.0", "remark-parse": "^11.0.0",
"remark-rehype": "^11.1.1", "remark-rehype": "^11.1.1",
@@ -4975,6 +4976,22 @@
"url": "https://opencollective.com/unified" "url": "https://opencollective.com/unified"
} }
}, },
"node_modules/remark": {
"version": "15.0.1",
"resolved": "https://registry.npmjs.org/remark/-/remark-15.0.1.tgz",
"integrity": "sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
"remark-parse": "^11.0.0",
"remark-stringify": "^11.0.0",
"unified": "^11.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/remark-math": { "node_modules/remark-math": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/remark-math/-/remark-math-6.0.0.tgz", "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-6.0.0.tgz",
@@ -5024,6 +5041,21 @@
"url": "https://opencollective.com/unified" "url": "https://opencollective.com/unified"
} }
}, },
"node_modules/remark-stringify": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz",
"integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
"mdast-util-to-markdown": "^2.0.0",
"unified": "^11.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/require-from-string": { "node_modules/require-from-string": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",

View File

@@ -42,6 +42,7 @@
"rehype-mathjax": "^6.0.0", "rehype-mathjax": "^6.0.0",
"rehype-prism": "^2.3.3", "rehype-prism": "^2.3.3",
"rehype-react": "^8.0.0", "rehype-react": "^8.0.0",
"remark": "^15.0.1",
"remark-math": "^6.0.0", "remark-math": "^6.0.0",
"remark-parse": "^11.0.0", "remark-parse": "^11.0.0",
"remark-rehype": "^11.1.1", "remark-rehype": "^11.1.1",

View File

@@ -21,15 +21,10 @@ const MarkdownPreview = ({ content, handleFileSelect }) => {
if (href.startsWith(`${baseUrl}/internal/`)) { if (href.startsWith(`${baseUrl}/internal/`)) {
// For existing files, extract the path and directly select it // For existing files, extract the path and directly select it
const [filePath, heading] = decodeURIComponent( const [filePath] = decodeURIComponent(
href.replace(`${baseUrl}/internal/`, '') href.replace(`${baseUrl}/internal/`, '')
).split('#'); ).split('#');
handleFileSelect(filePath); handleFileSelect(filePath);
// TODO: Handle heading navigation if needed
if (heading) {
console.debug('Heading navigation not implemented:', heading);
}
} else if (href.startsWith(`${baseUrl}/notfound/`)) { } else if (href.startsWith(`${baseUrl}/notfound/`)) {
// For non-existent files, show a notification // For non-existent files, show a notification
const fileName = decodeURIComponent( const fileName = decodeURIComponent(
@@ -47,7 +42,7 @@ const MarkdownPreview = ({ content, handleFileSelect }) => {
() => () =>
unified() unified()
.use(remarkParse) .use(remarkParse)
.use(remarkWikiLinks, currentWorkspace?.id) .use(remarkWikiLinks, currentWorkspace?.name)
.use(remarkMath) .use(remarkMath)
.use(remarkRehype) .use(remarkRehype)
.use(rehypeMathjax) .use(rehypeMathjax)
@@ -90,7 +85,7 @@ const MarkdownPreview = ({ content, handleFileSelect }) => {
}, },
}, },
}), }),
[baseUrl, handleFileSelect, currentWorkspace?.id] [baseUrl, handleFileSelect, currentWorkspace?.name]
); );
useEffect(() => { useEffect(() => {

View File

@@ -47,7 +47,7 @@ const WorkspaceSwitcher = () => {
const handleWorkspaceCreated = async (newWorkspace) => { const handleWorkspaceCreated = async (newWorkspace) => {
await loadWorkspaces(); await loadWorkspaces();
switchWorkspace(newWorkspace.id); switchWorkspace(newWorkspace.name);
}; };
return ( return (
@@ -102,10 +102,10 @@ const WorkspaceSwitcher = () => {
</Center> </Center>
) : ( ) : (
workspaces.map((workspace) => { workspaces.map((workspace) => {
const isSelected = workspace.id === currentWorkspace?.id; const isSelected = workspace.name === currentWorkspace?.name;
return ( return (
<Paper <Paper
key={workspace.id} key={workspace.name}
p="xs" p="xs"
withBorder withBorder
style={{ style={{
@@ -125,7 +125,7 @@ const WorkspaceSwitcher = () => {
<UnstyledButton <UnstyledButton
style={{ flex: 1 }} style={{ flex: 1 }}
onClick={() => { onClick={() => {
switchWorkspace(workspace.id); switchWorkspace(workspace.name);
setPopoverOpened(false); setPopoverOpened(false);
}} }}
> >

View File

@@ -8,10 +8,10 @@ import React, {
import { useMantineColorScheme } from '@mantine/core'; import { useMantineColorScheme } from '@mantine/core';
import { notifications } from '@mantine/notifications'; import { notifications } from '@mantine/notifications';
import { import {
fetchLastWorkspaceId, fetchLastWorkspaceName,
getWorkspace, getWorkspace,
updateWorkspace, updateWorkspace,
updateLastWorkspace, updateLastWorkspaceName,
deleteWorkspace, deleteWorkspace,
listWorkspaces, listWorkspaces,
} from '../services/api'; } from '../services/api';
@@ -41,9 +41,9 @@ export const WorkspaceProvider = ({ children }) => {
} }
}, []); }, []);
const loadWorkspaceData = useCallback(async (workspaceId) => { const loadWorkspaceData = useCallback(async (workspaceName) => {
try { try {
const workspace = await getWorkspace(workspaceId); const workspace = await getWorkspace(workspaceName);
setCurrentWorkspace(workspace); setCurrentWorkspace(workspace);
setColorScheme(workspace.theme); setColorScheme(workspace.theme);
} catch (error) { } catch (error) {
@@ -61,8 +61,8 @@ export const WorkspaceProvider = ({ children }) => {
const allWorkspaces = await listWorkspaces(); const allWorkspaces = await listWorkspaces();
if (allWorkspaces.length > 0) { if (allWorkspaces.length > 0) {
const firstWorkspace = allWorkspaces[0]; const firstWorkspace = allWorkspaces[0];
await updateLastWorkspace(firstWorkspace.id); await updateLastWorkspaceName(firstWorkspace.name);
await loadWorkspaceData(firstWorkspace.id); await loadWorkspaceData(firstWorkspace.name);
} }
} catch (error) { } catch (error) {
console.error('Failed to load first available workspace:', error); console.error('Failed to load first available workspace:', error);
@@ -77,9 +77,9 @@ export const WorkspaceProvider = ({ children }) => {
useEffect(() => { useEffect(() => {
const initializeWorkspace = async () => { const initializeWorkspace = async () => {
try { try {
const { lastWorkspaceId } = await fetchLastWorkspaceId(); const { lastWorkspaceName } = await fetchLastWorkspaceName();
if (lastWorkspaceId) { if (lastWorkspaceName) {
await loadWorkspaceData(lastWorkspaceId); await loadWorkspaceData(lastWorkspaceName);
} else { } else {
await loadFirstAvailableWorkspace(); await loadFirstAvailableWorkspace();
} }
@@ -95,11 +95,11 @@ export const WorkspaceProvider = ({ children }) => {
initializeWorkspace(); initializeWorkspace();
}, []); }, []);
const switchWorkspace = useCallback(async (workspaceId) => { const switchWorkspace = useCallback(async (workspaceName) => {
try { try {
setLoading(true); setLoading(true);
await updateLastWorkspace(workspaceId); await updateLastWorkspaceName(workspaceName);
await loadWorkspaceData(workspaceId); await loadWorkspaceData(workspaceName);
await loadWorkspaces(); await loadWorkspaces();
} catch (error) { } catch (error) {
console.error('Failed to switch workspace:', error); console.error('Failed to switch workspace:', error);
@@ -129,10 +129,10 @@ export const WorkspaceProvider = ({ children }) => {
} }
// Delete workspace and get the next workspace ID // Delete workspace and get the next workspace ID
const response = await deleteWorkspace(currentWorkspace.id); const response = await deleteWorkspace(currentWorkspace.name);
// Load the new workspace data // Load the new workspace data
await loadWorkspaceData(response.nextWorkspaceId); await loadWorkspaceData(response.nextWorkspaceName);
notifications.show({ notifications.show({
title: 'Success', title: 'Success',
@@ -162,7 +162,7 @@ export const WorkspaceProvider = ({ children }) => {
}; };
const response = await updateWorkspace( const response = await updateWorkspace(
currentWorkspace.id, currentWorkspace.name,
updatedWorkspace updatedWorkspace
); );
setCurrentWorkspace(response); setCurrentWorkspace(response);

View File

@@ -19,7 +19,7 @@ export const useFileContent = (selectedFile) => {
if (filePath === DEFAULT_FILE.path) { if (filePath === DEFAULT_FILE.path) {
newContent = DEFAULT_FILE.content; newContent = DEFAULT_FILE.content;
} else if (!isImageFile(filePath)) { } else if (!isImageFile(filePath)) {
newContent = await fetchFileContent(currentWorkspace.id, filePath); newContent = await fetchFileContent(currentWorkspace.name, filePath);
} else { } else {
newContent = ''; // Set empty content for image files newContent = ''; // Set empty content for image files
} }

View File

@@ -10,7 +10,7 @@ export const useFileList = () => {
if (!currentWorkspace || workspaceLoading) return; if (!currentWorkspace || workspaceLoading) return;
try { try {
const fileList = await fetchFileList(currentWorkspace.id); const fileList = await fetchFileList(currentWorkspace.name);
if (Array.isArray(fileList)) { if (Array.isArray(fileList)) {
setFiles(fileList); setFiles(fileList);
} else { } else {

View File

@@ -29,7 +29,7 @@ export const useFileOperations = () => {
if (!currentWorkspace) return false; if (!currentWorkspace) return false;
try { try {
await saveFileContent(currentWorkspace.id, filePath, content); await saveFileContent(currentWorkspace.name, filePath, content);
notifications.show({ notifications.show({
title: 'Success', title: 'Success',
message: 'File saved successfully', message: 'File saved successfully',
@@ -55,7 +55,7 @@ export const useFileOperations = () => {
if (!currentWorkspace) return false; if (!currentWorkspace) return false;
try { try {
await deleteFile(currentWorkspace.id, filePath); await deleteFile(currentWorkspace.name, filePath);
notifications.show({ notifications.show({
title: 'Success', title: 'Success',
message: 'File deleted successfully', message: 'File deleted successfully',
@@ -81,7 +81,7 @@ export const useFileOperations = () => {
if (!currentWorkspace) return false; if (!currentWorkspace) return false;
try { try {
await saveFileContent(currentWorkspace.id, fileName, initialContent); await saveFileContent(currentWorkspace.name, fileName, initialContent);
notifications.show({ notifications.show({
title: 'Success', title: 'Success',
message: 'File created successfully', message: 'File created successfully',

View File

@@ -10,7 +10,7 @@ export const useGitOperations = () => {
if (!currentWorkspace || !settings.gitEnabled) return false; if (!currentWorkspace || !settings.gitEnabled) return false;
try { try {
await pullChanges(currentWorkspace.id); await pullChanges(currentWorkspace.name);
notifications.show({ notifications.show({
title: 'Success', title: 'Success',
message: 'Successfully pulled latest changes', message: 'Successfully pulled latest changes',
@@ -33,7 +33,7 @@ export const useGitOperations = () => {
if (!currentWorkspace || !settings.gitEnabled) return false; if (!currentWorkspace || !settings.gitEnabled) return false;
try { try {
await commitAndPush(currentWorkspace.id, message); await commitAndPush(currentWorkspace.name, message);
notifications.show({ notifications.show({
title: 'Success', title: 'Success',
message: 'Successfully committed and pushed changes', message: 'Successfully committed and pushed changes',

View File

@@ -9,7 +9,7 @@ export const useLastOpenedFile = () => {
if (!currentWorkspace) return null; if (!currentWorkspace) return null;
try { try {
const response = await getLastOpenedFile(currentWorkspace.id); const response = await getLastOpenedFile(currentWorkspace.name);
return response.lastOpenedFilePath || null; return response.lastOpenedFilePath || null;
} catch (error) { } catch (error) {
console.error('Failed to load last opened file:', error); console.error('Failed to load last opened file:', error);
@@ -22,7 +22,7 @@ export const useLastOpenedFile = () => {
if (!currentWorkspace) return; if (!currentWorkspace) return;
try { try {
await updateLastOpenedFile(currentWorkspace.id, filePath); await updateLastOpenedFile(currentWorkspace.name, filePath);
} catch (error) { } catch (error) {
console.error('Failed to save last opened file:', error); console.error('Failed to save last opened file:', error);
} }

View File

@@ -1,28 +1,28 @@
import { API_BASE_URL } from '../utils/constants'; import { API_BASE_URL } from '../utils/constants';
import { apiCall } from './authApi'; import { apiCall } from './authApi';
export const fetchLastWorkspaceId = async () => { export const fetchLastWorkspaceName = async () => {
const response = await apiCall(`${API_BASE_URL}/workspaces/last`); const response = await apiCall(`${API_BASE_URL}/workspaces/last`);
return response.json(); return response.json();
}; };
export const fetchFileList = async (workspaceId) => { export const fetchFileList = async (workspaceName) => {
const response = await apiCall( const response = await apiCall(
`${API_BASE_URL}/workspaces/${workspaceId}/files` `${API_BASE_URL}/workspaces/${workspaceName}/files`
); );
return response.json(); return response.json();
}; };
export const fetchFileContent = async (workspaceId, filePath) => { export const fetchFileContent = async (workspaceName, filePath) => {
const response = await apiCall( const response = await apiCall(
`${API_BASE_URL}/workspaces/${workspaceId}/files/${filePath}` `${API_BASE_URL}/workspaces/${workspaceName}/files/${filePath}`
); );
return response.text(); return response.text();
}; };
export const saveFileContent = async (workspaceId, filePath, content) => { export const saveFileContent = async (workspaceName, filePath, content) => {
const response = await apiCall( const response = await apiCall(
`${API_BASE_URL}/workspaces/${workspaceId}/files/${filePath}`, `${API_BASE_URL}/workspaces/${workspaceName}/files/${filePath}`,
{ {
method: 'POST', method: 'POST',
headers: { headers: {
@@ -34,9 +34,9 @@ export const saveFileContent = async (workspaceId, filePath, content) => {
return response.text(); return response.text();
}; };
export const deleteFile = async (workspaceId, filePath) => { export const deleteFile = async (workspaceName, filePath) => {
const response = await apiCall( const response = await apiCall(
`${API_BASE_URL}/workspaces/${workspaceId}/files/${filePath}`, `${API_BASE_URL}/workspaces/${workspaceName}/files/${filePath}`,
{ {
method: 'DELETE', method: 'DELETE',
} }
@@ -44,26 +44,29 @@ export const deleteFile = async (workspaceId, filePath) => {
return response.text(); return response.text();
}; };
export const getWorkspace = async (workspaceId) => { export const getWorkspace = async (workspaceName) => {
const response = await apiCall(`${API_BASE_URL}/workspaces/${workspaceId}`); const response = await apiCall(`${API_BASE_URL}/workspaces/${workspaceName}`);
return response.json(); return response.json();
}; };
// Combined function to update workspace data including settings // Combined function to update workspace data including settings
export const updateWorkspace = async (workspaceId, workspaceData) => { export const updateWorkspace = async (workspaceName, workspaceData) => {
const response = await apiCall(`${API_BASE_URL}/workspaces/${workspaceId}`, { const response = await apiCall(
method: 'PUT', `${API_BASE_URL}/workspaces/${workspaceName}`,
headers: { {
'Content-Type': 'application/json', method: 'PUT',
}, headers: {
body: JSON.stringify(workspaceData), 'Content-Type': 'application/json',
}); },
body: JSON.stringify(workspaceData),
}
);
return response.json(); return response.json();
}; };
export const pullChanges = async (workspaceId) => { export const pullChanges = async (workspaceName) => {
const response = await apiCall( const response = await apiCall(
`${API_BASE_URL}/workspaces/${workspaceId}/git/pull`, `${API_BASE_URL}/workspaces/${workspaceName}/git/pull`,
{ {
method: 'POST', method: 'POST',
} }
@@ -71,9 +74,9 @@ export const pullChanges = async (workspaceId) => {
return response.json(); return response.json();
}; };
export const commitAndPush = async (workspaceId, message) => { export const commitAndPush = async (workspaceName, message) => {
const response = await apiCall( const response = await apiCall(
`${API_BASE_URL}/workspaces/${workspaceId}/git/commit`, `${API_BASE_URL}/workspaces/${workspaceName}/git/commit`,
{ {
method: 'POST', method: 'POST',
headers: { headers: {
@@ -85,13 +88,13 @@ export const commitAndPush = async (workspaceId, message) => {
return response.json(); return response.json();
}; };
export const getFileUrl = (workspaceId, filePath) => { export const getFileUrl = (workspaceName, filePath) => {
return `${API_BASE_URL}/workspaces/${workspaceId}/files/${filePath}`; return `${API_BASE_URL}/workspaces/${workspaceName}/files/${filePath}`;
}; };
export const lookupFileByName = async (workspaceId, filename) => { export const lookupFileByName = async (workspaceName, filename) => {
const response = await apiCall( const response = await apiCall(
`${API_BASE_URL}/workspaces/${workspaceId}/files/lookup?filename=${encodeURIComponent( `${API_BASE_URL}/workspaces/${workspaceName}/files/lookup?filename=${encodeURIComponent(
filename filename
)}` )}`
); );
@@ -99,9 +102,9 @@ export const lookupFileByName = async (workspaceId, filename) => {
return data.paths; return data.paths;
}; };
export const updateLastOpenedFile = async (workspaceId, filePath) => { export const updateLastOpenedFile = async (workspaceName, filePath) => {
const response = await apiCall( const response = await apiCall(
`${API_BASE_URL}/workspaces/${workspaceId}/files/last`, `${API_BASE_URL}/workspaces/${workspaceName}/files/last`,
{ {
method: 'PUT', method: 'PUT',
headers: { headers: {
@@ -113,9 +116,9 @@ export const updateLastOpenedFile = async (workspaceId, filePath) => {
return response.json(); return response.json();
}; };
export const getLastOpenedFile = async (workspaceId) => { export const getLastOpenedFile = async (workspaceName) => {
const response = await apiCall( const response = await apiCall(
`${API_BASE_URL}/workspaces/${workspaceId}/files/last` `${API_BASE_URL}/workspaces/${workspaceName}/files/last`
); );
return response.json(); return response.json();
}; };
@@ -136,20 +139,23 @@ export const createWorkspace = async (name) => {
return response.json(); return response.json();
}; };
export const deleteWorkspace = async (workspaceId) => { export const deleteWorkspace = async (workspaceName) => {
const response = await apiCall(`${API_BASE_URL}/workspaces/${workspaceId}`, { const response = await apiCall(
method: 'DELETE', `${API_BASE_URL}/workspaces/${workspaceName}`,
}); {
method: 'DELETE',
}
);
return response.json(); return response.json();
}; };
export const updateLastWorkspace = async (workspaceId) => { export const updateLastWorkspaceName = async (workspaceName) => {
const response = await apiCall(`${API_BASE_URL}/workspaces/last`, { const response = await apiCall(`${API_BASE_URL}/workspaces/last`, {
method: 'PUT', method: 'PUT',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ workspaceId }), body: JSON.stringify({ workspaceName }),
}); });
return response.json(); return response.json();
}; };

View File

@@ -27,10 +27,10 @@ function createFileLink(filePath, displayText, heading, baseUrl) {
}; };
} }
function createImageNode(workspaceId, filePath, displayText) { function createImageNode(workspaceName, filePath, displayText) {
return { return {
type: 'image', type: 'image',
url: getFileUrl(workspaceId, filePath), url: getFileUrl(workspaceName, filePath),
alt: displayText, alt: displayText,
title: displayText, title: displayText,
}; };
@@ -43,9 +43,9 @@ function addMarkdownExtension(fileName) {
return `${fileName}.md`; return `${fileName}.md`;
} }
export function remarkWikiLinks(workspaceId) { export function remarkWikiLinks(workspaceName) {
return async function transformer(tree) { return async function transformer(tree) {
if (!workspaceId) { if (!workspaceName) {
console.warn('No workspace ID provided to remarkWikiLinks plugin'); console.warn('No workspace ID provided to remarkWikiLinks plugin');
return; return;
} }
@@ -113,13 +113,13 @@ export function remarkWikiLinks(workspaceId) {
? match.fileName ? match.fileName
: addMarkdownExtension(match.fileName); : addMarkdownExtension(match.fileName);
const paths = await lookupFileByName(workspaceId, lookupFileName); const paths = await lookupFileByName(workspaceName, lookupFileName);
if (paths && paths.length > 0) { if (paths && paths.length > 0) {
const filePath = paths[0]; const filePath = paths[0];
if (match.isImage) { if (match.isImage) {
newNodes.push( newNodes.push(
createImageNode(workspaceId, filePath, match.displayText) createImageNode(workspaceName, filePath, match.displayText)
); );
} else { } else {
newNodes.push( newNodes.push(

View File

@@ -52,11 +52,16 @@ export default defineConfig(({ mode }) => ({
// Markdown processing // Markdown processing
markdown: [ markdown: [
'react-markdown',
'react-syntax-highlighter', 'react-syntax-highlighter',
'rehype-katex', 'rehype-mathjax',
'rehype-prism',
'rehype-react',
'remark',
'remark-math', 'remark-math',
'katex', 'remark-parse',
'remark-rehype',
'unified',
'unist-util-visit',
], ],
// Icons and utilities // Icons and utilities