diff --git a/server/cmd/server.go b/server/cmd/server.go index 2c7f79d5..bca0d20c 100644 --- a/server/cmd/server.go +++ b/server/cmd/server.go @@ -1,6 +1,8 @@ package cmd import ( + _ "embed" + "fmt" "io/fs" "log" "net/http" @@ -165,9 +167,39 @@ func buildConfig(bundledFiles fs.FS, args []string) *server.ServerConfig { rootSpaceConfig.ShellBackend = server.NewNotSupportedShell() } + // Ensure at least the index page and config page exist + ensureIndexAndConfig(rootSpaceConfig) + return serverConfig } +//go:embed space_template/index.md +var indexPageContent []byte + +//go:embed space_template/CONFIG.md +var configPageContent []byte + +func ensureIndexAndConfig(rootSpaceConfig *server.SpaceConfig) { + // Index page first + indexPagePath := fmt.Sprintf("%s.md", rootSpaceConfig.IndexPage) + _, err := rootSpaceConfig.SpacePrimitives.GetFileMeta(indexPagePath) + if err == server.ErrNotFound { + log.Printf("Index page %s does not yet exist, creating...", indexPagePath) + if _, err := rootSpaceConfig.SpacePrimitives.WriteFile(indexPagePath, indexPageContent, nil); err != nil { + log.Fatalf("Could not write index page %s: %v", indexPagePath, err) + } + } + // Now let's check for a CONFIG.md + configPagePath := "CONFIG.md" + _, err = rootSpaceConfig.SpacePrimitives.GetFileMeta(configPagePath) + if err == server.ErrNotFound { + log.Printf("Config page %s does not yet exist, creating...", configPagePath) + if _, err := rootSpaceConfig.SpacePrimitives.WriteFile(configPagePath, configPageContent, nil); err != nil { + log.Fatalf("Could not write config page %s: %v", configPagePath, err) + } + } +} + func ServerCommand(bundledFiles fs.FS) *cobra.Command { var hostname string var port int diff --git a/server/cmd/space_template/CONFIG.md b/server/cmd/space_template/CONFIG.md new file mode 100644 index 00000000..5965b045 --- /dev/null +++ b/server/cmd/space_template/CONFIG.md @@ -0,0 +1,10 @@ +This is where you configure SilverBullet to your liking. See [[^Library/Std/Config]] for a full list of configuration options. + +```space-lua +config.set { + plugs = { + -- Add your plugs here (https://silverbullet.md/Plugs) + -- Then run the `Plugs: Update` command to update them + } +} +``` diff --git a/server/cmd/space_template/index.md b/server/cmd/space_template/index.md new file mode 100644 index 00000000..2a31d581 --- /dev/null +++ b/server/cmd/space_template/index.md @@ -0,0 +1,34 @@ +# Hello 👋 +Welcome to the wondrous world of SilverBullet. A world that once you discover and appreciate, you’ll never want to leave. + +_One of us!_ + +You can go ahead and delete this placeholder content in a second, but before you do, let me quickly show you around. + +We’ll do this through a list of **silver bullets**. + +_Ah, so that explains the name!_ + +* Click on the page picker (book icon) icon at the top right, or hit `Cmd-k` (Mac) or `Ctrl-k` (Linux and Windows) to open the **page picker**. + * Type the name of a non-existent page to create it. + * Folders are implicitly created by putting slashes (`/`) in the name (even on Windows), e.g. `My Folder/My Page`. Don’t worry about folders existing, SilverBullet will automatically create them if they don’t. +* Click on the terminal icon (top right), or hit `Cmd-/` (Mac) or `Ctrl-/` (Linux and Windows) to open the **command palette**. From here you can run various useful and perhaps less useful commands. +* Select some text and hit `Cmd-b` (Mac) or `Ctrl-b` (Windows/Linux) to make it **bold**, or `Cmd-i` (Mac) or `Ctrl-i` (Windows/Linux) to make it _italic_. +* You can create links to pages using the `[[other page]]` syntax, for instance: `[[aspiring page]]`. When you link to a non-existent page it will initially show up in orange (to indicate it does not yet exist), but once you click it — you will create the page automatically (only for real when you actually enter some text). +* Start typing `:party` to trigger the emoji picker 🎉 +* Type `/` somewhere in the text to invoke a **slash command**. +* If this matches your personality type, there is also an `Editor: Toggle Vim Mode` command to switch to Vim mode. If you cannot figure out how to exit it (classic vim issue), just run that same command again. _Phew!_ +* As you may have noticed, while SilverBullet uses [markdown](https://www.markdownguide.org/) underneath, it also “live previews” it to give a bit more of a WYSIWYG feel. You can always move your cursor “into” the underlying code by `Alt-clicking` it (even links and widgets, which we’ll get to later). + +While SilverBullet implements (most) of [CommonMark](https://commonmark.org/), it also adds a few extensions that are SilverBullet-specific. Most notably, it adds the `${Lua expression}` syntax to render Lua expressions inline. + +Using SilverBullet’s Lua APIs, you can do all kinds of cool stuff. For instance, query your space for the last 3 modified pages. _Exciting!_ + +${query[[from index.tag "page" order by lastModified desc limit 3]]} + +There’s a whole world out there to explore, but let’s not get ahead of ourselves. First, have some fun and add some content to your fresh space. + +Then we’ll talk. + +# What next? +You can find more information on SilverBullet’s feature set on its [official website](https://v2.silverbullet.md/). Also, be sure to join the [SilverBullet community](https://community.silverbullet.md/) to interact with fellow SilverBullet explorers. diff --git a/server/disk_space_primitives.go b/server/disk_space_primitives.go index 981b94c2..b0d111cd 100644 --- a/server/disk_space_primitives.go +++ b/server/disk_space_primitives.go @@ -152,7 +152,7 @@ func (d *DiskSpacePrimitives) FetchFileList() ([]FileMeta, error) { } // GetFileMeta implements SpacePrimitives.GetFileMeta -func (d *DiskSpacePrimitives) GetFileMeta(path string, observing bool) (FileMeta, error) { +func (d *DiskSpacePrimitives) GetFileMeta(path string) (FileMeta, error) { localPath, err := d.filenameToPath(path) if err != nil { return FileMeta{}, err @@ -223,7 +223,7 @@ func (d *DiskSpacePrimitives) WriteFile(path string, data []byte, meta *FileMeta } // Return actual metadata - return d.GetFileMeta(path, false) + return d.GetFileMeta(path) } // DeleteFile implements SpacePrimitives.DeleteFile diff --git a/server/embed_space_primitives.go b/server/embed_space_primitives.go index 1926e3f7..f517ad72 100644 --- a/server/embed_space_primitives.go +++ b/server/embed_space_primitives.go @@ -98,7 +98,7 @@ func (e *ReadOnlyFallthroughSpacePrimitives) FetchFileList() ([]FileMeta, error) } // GetFileMeta implements SpacePrimitives.GetFileMeta -func (e *ReadOnlyFallthroughSpacePrimitives) GetFileMeta(path string, observing bool) (FileMeta, error) { +func (e *ReadOnlyFallthroughSpacePrimitives) GetFileMeta(path string) (FileMeta, error) { embedPath := e.pathToEmbedPath(path) // Try to get file info from filesystem @@ -112,7 +112,7 @@ func (e *ReadOnlyFallthroughSpacePrimitives) GetFileMeta(path string, observing } // If not found in fs.FS, fall back - return e.fallthroughSpacePrimitives.GetFileMeta(path, observing) + return e.fallthroughSpacePrimitives.GetFileMeta(path) } // ReadFile implements SpacePrimitives.ReadFile diff --git a/server/fs.go b/server/fs.go index e9025167..4a1c8b5b 100644 --- a/server/fs.go +++ b/server/fs.go @@ -62,7 +62,7 @@ func handleFsGet(w http.ResponseWriter, r *http.Request) { if r.Header.Get("X-Get-Meta") != "" { // Getting meta via GET request - meta, err := spaceConfig.SpacePrimitives.GetFileMeta(path, false) + meta, err := spaceConfig.SpacePrimitives.GetFileMeta(path) if err != nil { if err == ErrNotFound { http.NotFound(w, r) diff --git a/server/space_primitive_testing.go b/server/space_primitive_testing.go index 7b607033..5428e981 100644 --- a/server/space_primitive_testing.go +++ b/server/space_primitive_testing.go @@ -135,7 +135,7 @@ func testFileOverwriting(t *testing.T, spacePrimitives SpacePrimitives) { _, err := spacePrimitives.WriteFile("overwrite.txt", originalContent, nil) assert.NoError(t, err, "Failed to write original file") - _, err = spacePrimitives.GetFileMeta("overwrite.txt", false) + _, err = spacePrimitives.GetFileMeta("overwrite.txt") assert.NoError(t, err, "Failed to get original meta") updatedContent := []byte("Updated") @@ -262,7 +262,7 @@ func testErrorHandling(t *testing.T, spacePrimitives SpacePrimitives) { err = spacePrimitives.DeleteFile("nonexistent.txt") assert.Equal(t, ErrNotFound, err, "Expected ErrNotFound when deleting non-existent file") - _, err = spacePrimitives.GetFileMeta("nonexistent.txt", false) + _, err = spacePrimitives.GetFileMeta("nonexistent.txt") assert.Equal(t, ErrNotFound, err, "Expected ErrNotFound for non-existent file meta") } diff --git a/server/types.go b/server/types.go index 8f449dcb..1474dc0a 100644 --- a/server/types.go +++ b/server/types.go @@ -59,8 +59,7 @@ type SpacePrimitives interface { FetchFileList() ([]FileMeta, error) // GetFileMeta retrieves metadata for a specific file - // observing parameter hints that this file is under regular observation - GetFileMeta(path string, observing bool) (FileMeta, error) + GetFileMeta(path string) (FileMeta, error) // ReadFile reads a file and returns its data and metadata ReadFile(path string) ([]byte, FileMeta, error)