feat: render selected document
This commit is contained in:
		
							parent
							
								
									9ab224f70c
								
							
						
					
					
						commit
						acf93695d3
					
				@ -18,3 +18,16 @@ func GetDocumentCollection() *DocumentCollection {
 | 
				
			|||||||
func (collection *DocumentCollection) AddDocument(document Entity) {
 | 
					func (collection *DocumentCollection) AddDocument(document Entity) {
 | 
				
			||||||
	collection.Documents = append(collection.Documents, document)
 | 
						collection.Documents = append(collection.Documents, document)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (collection *DocumentCollection) GetDocumentById(id string) Entity {
 | 
				
			||||||
 | 
						var foundDocument Entity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, d := range collection.Documents {
 | 
				
			||||||
 | 
							if d.Id == id {
 | 
				
			||||||
 | 
								foundDocument = d
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return foundDocument
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
import 'server-only'
 | 
					import 'server-only'
 | 
				
			||||||
import MainHead from '../components/MainHead'
 | 
					import MainHead from '../components/MainHead'
 | 
				
			||||||
 | 
					import DocumentRenderer from '../components/workspace/DocumentRenderer'
 | 
				
			||||||
import Navigation from '../components/workspace/Navigation'
 | 
					import Navigation from '../components/workspace/Navigation'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Home() {
 | 
					export default function Home() {
 | 
				
			||||||
@ -16,8 +17,8 @@ export default function Home() {
 | 
				
			|||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div className="mx-auto  px-4 sm:px-6 md:px-8">
 | 
					            <div className="mx-auto  px-4 sm:px-6 md:px-8">
 | 
				
			||||||
              <div className="py-4">
 | 
					              <div className="py-4">
 | 
				
			||||||
                <div className="h-96 rounded-lg border-4 border-dashed border-gray-200">
 | 
					                <div className=" min-h-96 rounded-lg border-4 border-dashed border-gray-200">
 | 
				
			||||||
                  {/* Add canvas */}
 | 
					                  <DocumentRenderer />
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										44
									
								
								frontend/components/workspace/DocumentRenderer.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								frontend/components/workspace/DocumentRenderer.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					'use client'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { useEffect, useRef } from "react"
 | 
				
			||||||
 | 
					import { useProject } from "../../context/Project/provider"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const loadImage = (path: string): Promise<HTMLImageElement> => {
 | 
				
			||||||
 | 
					  return new Promise((resolve, reject) => {
 | 
				
			||||||
 | 
					    const image = new Image()
 | 
				
			||||||
 | 
					    image.src = path
 | 
				
			||||||
 | 
					    image.onload = () => resolve(image)
 | 
				
			||||||
 | 
					    image.onerror = (error) => reject(error)
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const DocumentRenderer = () => {
 | 
				
			||||||
 | 
					  const { getSelectedDocument } = useProject()
 | 
				
			||||||
 | 
					  const canvasRef = useRef<HTMLCanvasElement>(null)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const applyDocumentToCanvas = async (path: string) => {
 | 
				
			||||||
 | 
					    const image = await loadImage(path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const canvas = canvasRef.current
 | 
				
			||||||
 | 
					    if (!canvas) return
 | 
				
			||||||
 | 
					    canvas.width = image.naturalWidth
 | 
				
			||||||
 | 
					    canvas.height = image.naturalHeight
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const context = canvas.getContext('2d')
 | 
				
			||||||
 | 
					    if (!context) return
 | 
				
			||||||
 | 
					    context.drawImage(image, 10, 10, image.width, image.height)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    const selectedDocument = getSelectedDocument()
 | 
				
			||||||
 | 
					    const documentPath = selectedDocument?.path
 | 
				
			||||||
 | 
					    if (documentPath) applyDocumentToCanvas(selectedDocument.path)
 | 
				
			||||||
 | 
					  }, [getSelectedDocument])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <canvas ref={canvasRef}></canvas>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default DocumentRenderer
 | 
				
			||||||
@ -43,14 +43,20 @@ function classNames(...classes: any[]) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Sidebar() {
 | 
					function Sidebar() {
 | 
				
			||||||
  const [selectedItemId, setSelectedItemId] = useState('')
 | 
					 | 
				
			||||||
  const [selectedGroupId, setSelectedGroupId] = useState('')
 | 
					  const [selectedGroupId, setSelectedGroupId] = useState('')
 | 
				
			||||||
  const [isAddNewDocumentInputShowing, setIsAddNewDocumentInputShowing] = useState(false)
 | 
					  const [isAddNewDocumentInputShowing, setIsAddNewDocumentInputShowing] = useState(false)
 | 
				
			||||||
  const [isAddNewGroupInputShowing, setIsAddNewGroupInputShowing] = useState(false)
 | 
					  const [isAddNewGroupInputShowing, setIsAddNewGroupInputShowing] = useState(false)
 | 
				
			||||||
  const addDocumentTextInput = useRef<HTMLInputElement>(null)
 | 
					  const addDocumentTextInput = useRef<HTMLInputElement>(null)
 | 
				
			||||||
  const addGroupTextInput = useRef<HTMLInputElement>(null)
 | 
					  const addGroupTextInput = useRef<HTMLInputElement>(null)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const { documents, groups, requestAddDocument, requestAddDocumentGroup } = useProject()
 | 
					  const {
 | 
				
			||||||
 | 
					    documents,
 | 
				
			||||||
 | 
					    groups,
 | 
				
			||||||
 | 
					    requestAddDocument,
 | 
				
			||||||
 | 
					    requestAddDocumentGroup,
 | 
				
			||||||
 | 
					    selectedDocumentId,
 | 
				
			||||||
 | 
					    setSelectedDocumentId
 | 
				
			||||||
 | 
					  } = useProject()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const navigation = getNavigationProps(documents, groups)
 | 
					  const navigation = getNavigationProps(documents, groups)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -75,7 +81,7 @@ function Sidebar() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const onItemClickHandler = (itemId: string) => {
 | 
					  const onItemClickHandler = (itemId: string) => {
 | 
				
			||||||
    setSelectedItemId(itemId)
 | 
					    setSelectedDocumentId(itemId)
 | 
				
			||||||
    setSelectedGroupId(getParentGroupIdFromItemId(itemId))
 | 
					    setSelectedGroupId(getParentGroupIdFromItemId(itemId))
 | 
				
			||||||
    setIsAddNewDocumentInputShowing(false)
 | 
					    setIsAddNewDocumentInputShowing(false)
 | 
				
			||||||
    setIsAddNewGroupInputShowing(false)
 | 
					    setIsAddNewGroupInputShowing(false)
 | 
				
			||||||
@ -96,12 +102,15 @@ function Sidebar() {
 | 
				
			|||||||
    const response = await requestAddDocument(groupId, documentName)
 | 
					    const response = await requestAddDocument(groupId, documentName)
 | 
				
			||||||
    if (!response.id) return
 | 
					    if (!response.id) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setSelectedItemId(response.id)
 | 
					
 | 
				
			||||||
 | 
					    setSelectedDocumentId(response.id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setSelectedGroupId(groupId)
 | 
					    setSelectedGroupId(groupId)
 | 
				
			||||||
    setIsAddNewDocumentInputShowing(false)
 | 
					    setIsAddNewDocumentInputShowing(false)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const onConfirmAddGroupClickHandler = async(e: React.MouseEvent) => {
 | 
					  const onConfirmAddGroupClickHandler = async (e: React.MouseEvent) => {
 | 
				
			||||||
    const groupName = addGroupTextInput.current?.value
 | 
					    const groupName = addGroupTextInput.current?.value
 | 
				
			||||||
    if (!groupName) return
 | 
					    if (!groupName) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -219,7 +228,7 @@ function Sidebar() {
 | 
				
			|||||||
                  role='button'
 | 
					                  role='button'
 | 
				
			||||||
                  onClick={() => onItemClickHandler(child.id)}
 | 
					                  onClick={() => onItemClickHandler(child.id)}
 | 
				
			||||||
                  className={classNames(
 | 
					                  className={classNames(
 | 
				
			||||||
                    child.id === selectedItemId
 | 
					                    child.id === selectedDocumentId
 | 
				
			||||||
                      ? 'bg-gray-900 text-white'
 | 
					                      ? 'bg-gray-900 text-white'
 | 
				
			||||||
                      : 'text-gray-300 hover:bg-gray-700 hover:text-white',
 | 
					                      : 'text-gray-300 hover:bg-gray-700 hover:text-white',
 | 
				
			||||||
                    'group w-full flex items-center pr-2 py-2 text-left font-medium text-sm rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 p-2'
 | 
					                    'group w-full flex items-center pr-2 py-2 text-left font-medium text-sm rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 p-2'
 | 
				
			||||||
 | 
				
			|||||||
@ -14,6 +14,7 @@ type Props = { children: ReactNode, projectProps: ProjectProps }
 | 
				
			|||||||
export function ProjectProvider({ children, projectProps }: Props) {
 | 
					export function ProjectProvider({ children, projectProps }: Props) {
 | 
				
			||||||
  const [ documents, setDocuments ] = useState<ipc.Document[]>(projectProps.documents)
 | 
					  const [ documents, setDocuments ] = useState<ipc.Document[]>(projectProps.documents)
 | 
				
			||||||
  const [ groups, setGroups ] = useState<ipc.Group[]>(projectProps.groups)
 | 
					  const [ groups, setGroups ] = useState<ipc.Group[]>(projectProps.groups)
 | 
				
			||||||
 | 
					  const [selectedDocumentId, setSelectedDocumentId] = useState<string>('')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const updateDocuments = async () => {
 | 
					  const updateDocuments = async () => {
 | 
				
			||||||
    GetDocuments().then(response => {
 | 
					    GetDocuments().then(response => {
 | 
				
			||||||
@ -35,14 +36,19 @@ export function ProjectProvider({ children, projectProps }: Props) {
 | 
				
			|||||||
    return response
 | 
					    return response
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const getSelectedDocument = () => documents.find(d => d.id === selectedDocumentId)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!documents.length || !groups.length) updateDocuments()
 | 
					  if (!documents.length || !groups.length) updateDocuments()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const value = {
 | 
					  const value = {
 | 
				
			||||||
    id: '',
 | 
					    id: '',
 | 
				
			||||||
    documents,
 | 
					    documents,
 | 
				
			||||||
    groups,
 | 
					    groups,
 | 
				
			||||||
 | 
					    getSelectedDocument,
 | 
				
			||||||
    requestAddDocument,
 | 
					    requestAddDocument,
 | 
				
			||||||
    requestAddDocumentGroup,
 | 
					    requestAddDocumentGroup,
 | 
				
			||||||
 | 
					    selectedDocumentId,
 | 
				
			||||||
 | 
					    setSelectedDocumentId,
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return <ProjectContext.Provider value={value}>
 | 
					  return <ProjectContext.Provider value={value}>
 | 
				
			||||||
 | 
				
			|||||||
@ -7,6 +7,9 @@ export type ProjectProps = {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type ProjectContextType = {
 | 
					export type ProjectContextType = {
 | 
				
			||||||
 | 
					  getSelectedDocument: () => ipc.Document | undefined
 | 
				
			||||||
  requestAddDocument: (groupId: string, documentName: string) => Promise<ipc.Document>,
 | 
					  requestAddDocument: (groupId: string, documentName: string) => Promise<ipc.Document>,
 | 
				
			||||||
  requestAddDocumentGroup: (groupName: string) => Promise<ipc.Group>,
 | 
					  requestAddDocumentGroup: (groupName: string) => Promise<ipc.Group>,
 | 
				
			||||||
 | 
					  selectedDocumentId: string,
 | 
				
			||||||
 | 
					  setSelectedDocumentId: (id: string) => void,
 | 
				
			||||||
} & ProjectProps
 | 
					} & ProjectProps
 | 
				
			||||||
							
								
								
									
										27
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								main.go
									
									
									
									
									
								
							@ -2,7 +2,10 @@ package main
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"embed"
 | 
						"embed"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	app "textualize/core/App"
 | 
						app "textualize/core/App"
 | 
				
			||||||
	document "textualize/core/Document"
 | 
						document "textualize/core/Document"
 | 
				
			||||||
@ -11,6 +14,7 @@ import (
 | 
				
			|||||||
	"github.com/wailsapp/wails/v2"
 | 
						"github.com/wailsapp/wails/v2"
 | 
				
			||||||
	"github.com/wailsapp/wails/v2/pkg/logger"
 | 
						"github.com/wailsapp/wails/v2/pkg/logger"
 | 
				
			||||||
	"github.com/wailsapp/wails/v2/pkg/options"
 | 
						"github.com/wailsapp/wails/v2/pkg/options"
 | 
				
			||||||
 | 
						"github.com/wailsapp/wails/v2/pkg/options/assetserver"
 | 
				
			||||||
	"github.com/wailsapp/wails/v2/pkg/options/mac"
 | 
						"github.com/wailsapp/wails/v2/pkg/options/mac"
 | 
				
			||||||
	"github.com/wailsapp/wails/v2/pkg/options/windows"
 | 
						"github.com/wailsapp/wails/v2/pkg/options/windows"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@ -18,6 +22,26 @@ import (
 | 
				
			|||||||
//go:embed frontend/out frontend/out/_next/static
 | 
					//go:embed frontend/out frontend/out/_next/static
 | 
				
			||||||
var assets embed.FS
 | 
					var assets embed.FS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type FileLoader struct {
 | 
				
			||||||
 | 
						http.Handler
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ClientFileLoader() *FileLoader {
 | 
				
			||||||
 | 
						return &FileLoader{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h *FileLoader) ServeHTTP(res http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						requestedFilename := req.URL.Path
 | 
				
			||||||
 | 
						fileData, err := os.ReadFile(requestedFilename)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							res.WriteHeader(http.StatusBadRequest)
 | 
				
			||||||
 | 
							res.Write([]byte(fmt.Sprintf("Could not load file %s", requestedFilename)))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res.Write(fileData)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//go:embed build/appicondark.png
 | 
					//go:embed build/appicondark.png
 | 
				
			||||||
var icon []byte
 | 
					var icon []byte
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -43,7 +67,10 @@ func main() {
 | 
				
			|||||||
		StartHidden:       false,
 | 
							StartHidden:       false,
 | 
				
			||||||
		HideWindowOnClose: false,
 | 
							HideWindowOnClose: false,
 | 
				
			||||||
		BackgroundColour:  &options.RGBA{R: 255, G: 255, B: 255, A: 255},
 | 
							BackgroundColour:  &options.RGBA{R: 255, G: 255, B: 255, A: 255},
 | 
				
			||||||
 | 
							AssetServer: &assetserver.Options{
 | 
				
			||||||
			Assets:  assets,
 | 
								Assets:  assets,
 | 
				
			||||||
 | 
								Handler: ClientFileLoader(),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		Menu:      nil,
 | 
							Menu:      nil,
 | 
				
			||||||
		Logger:    nil,
 | 
							Logger:    nil,
 | 
				
			||||||
		LogLevel:  logger.DEBUG,
 | 
							LogLevel:  logger.DEBUG,
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user