Started UI for editing images

This commit is contained in:
Leon Mika 2026-03-25 21:09:57 +11:00
parent 8c371ccae9
commit 18f9f49c0a
10 changed files with 191 additions and 9 deletions

View file

@ -31,19 +31,24 @@ $container-max-widths: (
font-size: 0.9rem;
}
// Post form
// Large editor
//
// Used for edit canvases which take up the entire window
// Post edit page styling
.post-edit-page {
.large-editor {
height: 100vh;
}
.post-edit-page main {
.large-editor main {
display: flex;
flex-direction: column;
overflow: hidden;
}
// Post form
// Post edit page styling
.post-edit-page .post-form {
flex: 1;
display: flex;

View file

@ -0,0 +1,59 @@
import Handlebars from "handlebars";
import {Controller} from "@hotwired/stimulus";
const processorFrame = Handlebars.compile(`
<div class="card mb-3">
<div class="card-header d-flex justify-content-between">
<span>{{name}}</span>
<a href="#" class="btn btn-sm btn-secondary float-end">X</a>
</div>
<div class="card-body">
{{{props}}}
</div>
</div>
`);
const processors = [
{
name: "shadow",
label: "Shadow",
template: Handlebars.compile(`This processor has no properties.`),
},
{
name: "resize",
label: "Resize",
template: Handlebars.compile(`
<div class="mb-3">
<label for="{{id}}_width" class="form-label">Width</label>
<input name="width" class="form-control" id="{{id}}_width">
</div>
<div class="mb-3">
<label for="{{id}}_height" class="form-label">Height</label>
<input name="width" class="form-control" id="{{id}}_width">
</div>
`),
}
];
export default class UploadEditController extends Controller {
static targets = ['processList'];
connect() {
this._rebuildProcessList();
}
_rebuildProcessList() {
let el = this.processListTarget;
// TEMP
let cardTemplate = processors[0].template({
"id": "shadow",
});
let cardOuter = processorFrame({
name: processors[0].label,
props: cardTemplate,
});
el.innerHTML = cardOuter;
// END TEMP
}
}

View file

@ -8,6 +8,7 @@ import LogoutController from "./controllers/logout";
import FirstRunController from "./controllers/firstrun";
import UploadController from "./controllers/upload";
import ShowUploadController from "./controllers/show_upload";
import EditUploadController from "./controllers/edit_upload";
import PagelistController from "./controllers/pagelist";
window.Stimulus = Application.start()
@ -18,6 +19,7 @@ Stimulus.register("logout", LogoutController);
Stimulus.register("first-run", FirstRunController);
Stimulus.register("upload", UploadController);
Stimulus.register("show-upload", ShowUploadController);
Stimulus.register("edit-upload", EditUploadController);
Stimulus.register("pagelist", PagelistController);
feather.replace();

View file

@ -149,6 +149,7 @@ Starting weiro without any arguments will start the server.
siteGroup.Post("/uploads/pending/:guid", uh.UploadPart)
siteGroup.Post("/uploads/pending/:guid/finalize", uh.UploadComplete)
siteGroup.Delete("/uploads/:uploadID", uh.Delete)
siteGroup.Get("/uploads/:uploadID/edit", uh.Edit)
siteGroup.Get("/settings", ssh.General)
siteGroup.Post("/settings", ssh.UpdateGeneral)

View file

@ -75,7 +75,7 @@ func (ph PostsHandler) New(c fiber.Ctx) error {
"post": p,
"categories": cats,
"selectedCategories": map[int64]bool{},
"bodyClass": "post-edit-page",
"bodyClass": "large-editor",
})
}
@ -116,7 +116,7 @@ func (ph PostsHandler) Edit(c fiber.Ctx) error {
"post": post,
"categories": cats,
"selectedCategories": selectedCategories,
"bodyClass": "post-edit-page",
"bodyClass": "large-editor",
})
}))
}

View file

@ -162,3 +162,24 @@ func (uh UploadsHandler) UploadComplete(c fiber.Ctx) error {
return c.Status(fiber.StatusAccepted).JSON(fiber.Map{})
}
func (uh UploadsHandler) Edit(c fiber.Ctx) error {
uploadIDStr := c.Params("uploadID")
if uploadIDStr == "" {
return fiber.ErrBadRequest
}
uploadID, err := strconv.ParseInt(uploadIDStr, 10, 64)
if err != nil {
return fiber.ErrBadRequest
}
upload, err := uh.UploadsService.FetchUpload(c.Context(), uploadID)
if err != nil {
return err
}
return c.Render("uploads/edit", fiber.Map{
"upload": upload,
"bodyClass": "large-editor",
})
}

67
package-lock.json generated
View file

@ -8,7 +8,8 @@
"@hotwired/stimulus": "^3.2.2",
"bootstrap": "^5.3.8",
"esbuild-sass-plugin": "^3.6.0",
"feather-icons": "^4.29.2"
"feather-icons": "^4.29.2",
"handlebars": "^4.7.8"
},
"devDependencies": {
"esbuild": "0.27.3"
@ -892,6 +893,27 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/handlebars": {
"version": "4.7.8",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
"integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==",
"license": "MIT",
"dependencies": {
"minimist": "^1.2.5",
"neo-async": "^2.6.2",
"source-map": "^0.6.1",
"wordwrap": "^1.0.0"
},
"bin": {
"handlebars": "bin/handlebars"
},
"engines": {
"node": ">=0.4.7"
},
"optionalDependencies": {
"uglify-js": "^3.1.4"
}
},
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@ -958,6 +980,21 @@
"node": ">=0.10.0"
}
},
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/neo-async": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
"license": "MIT"
},
"node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
@ -1395,6 +1432,15 @@
"node": ">=14.0.0"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@ -1462,12 +1508,31 @@
"license": "0BSD",
"peer": true
},
"node_modules/uglify-js": {
"version": "3.19.3",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz",
"integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==",
"license": "BSD-2-Clause",
"optional": true,
"bin": {
"uglifyjs": "bin/uglifyjs"
},
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/varint": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz",
"integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==",
"license": "MIT",
"peer": true
},
"node_modules/wordwrap": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
"integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
"license": "MIT"
}
}
}

View file

@ -6,6 +6,7 @@
"@hotwired/stimulus": "^3.2.2",
"bootstrap": "^5.3.8",
"esbuild-sass-plugin": "^3.6.0",
"feather-icons": "^4.29.2"
"feather-icons": "^4.29.2",
"handlebars": "^4.7.8"
}
}

View file

@ -1,5 +1,5 @@
{{ $isPublished := ne .post.State 1 }}
<main class="flex-grow-1 position-relative">
<main class="flex-grow-1 position-relative post-edit-page">
<form action="/sites/{{.site.ID}}/posts" method="post" class="container-fluid post-form py-2"
data-controller="postedit"
data-action="keydown.meta+s->postedit#save keydown.meta+enter->postedit#publish"

28
views/uploads/edit.html Normal file
View file

@ -0,0 +1,28 @@
<main class="flex-grow-1 flex-shrink-1">
<div class="flex-grow-1 flex-shrink-1 d-flex flex-column">
<div class="row flex-grow-1 flex-shrink-1 m-0" data-controller="edit-upload">
<figure class="col-md-9 p-3">
<img src="{{ .upload.URL }}" alt="{{ .upload.Upload.Alt }}" class="img-fluid">
</figure>
<div class="col-md-3 p-3">
<div data-edit-upload-target="processList"></div>
<div class="text-center">
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
Add Processor
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Another action</a></li>
<li><a class="dropdown-item" href="#">Something else here</a></li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-9">
Actions go here
</div>
</main>