Compare commits

...

14 Commits

Author SHA1 Message Date
SngAbc
c314ceb7e1
Merge a81e8adde78e4d608b40d417fdafd2ca3005bdcc into 3cc64b576429b050ea356c3c76b8bf72f4a5b9b7 2025-11-28 23:44:18 +02:00
Elian Doran
3cc64b5764
fix(mobile/context_menu): note color picker not working
Some checks are pending
Checks / main (push) Waiting to run
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Dev / Test development (push) Waiting to run
Dev / Build Docker image (push) Blocked by required conditions
Dev / Check Docker build (Dockerfile) (push) Blocked by required conditions
Dev / Check Docker build (Dockerfile.alpine) (push) Blocked by required conditions
/ Check Docker build (Dockerfile) (push) Waiting to run
/ Check Docker build (Dockerfile.alpine) (push) Waiting to run
/ Build Docker images (Dockerfile, ubuntu-24.04-arm, linux/arm64) (push) Blocked by required conditions
/ Build Docker images (Dockerfile.alpine, ubuntu-latest, linux/amd64) (push) Blocked by required conditions
/ Build Docker images (Dockerfile.legacy, ubuntu-24.04-arm, linux/arm/v7) (push) Blocked by required conditions
/ Build Docker images (Dockerfile.legacy, ubuntu-24.04-arm, linux/arm/v8) (push) Blocked by required conditions
/ Merge manifest lists (push) Blocked by required conditions
playwright / E2E tests on linux-arm64 (push) Waiting to run
playwright / E2E tests on linux-x64 (push) Waiting to run
2025-11-28 23:43:16 +02:00
Elian Doran
19cf07564f
style(mobile/context_menu): taller height + small animation when expanding items 2025-11-28 23:21:31 +02:00
Elian Doran
5847ce5c14
feat(dev): enable CSS source maps 2025-11-28 23:21:14 +02:00
Elian Doran
a7ad45635e
style(mobile/context_menu): clean up border radiuses 2025-11-28 21:27:06 +02:00
Elian Doran
781215394e
refactor(mobile/context_menu): unify styles 2025-11-28 21:24:51 +02:00
Elian Doran
0aafdca999
style(mobile/context_menu): improve backdrop 2025-11-28 21:21:07 +02:00
Elian Doran
18d3cb6f0c
style(mobile/context_menu): dark submenu style 2025-11-28 21:14:06 +02:00
Elian Doran
263a96e8b7
style(mobile/context_menu): improve submenu style 2025-11-28 20:57:42 +02:00
Elian Doran
27d5009486
style(mobile/context_menu): disable hover color 2025-11-28 20:44:29 +02:00
SiriusXT
a81e8adde7 client/pageurl: adjust Info bar order
Some checks failed
Checks / main (push) Has been cancelled
2025-11-14 20:22:51 +08:00
SiriusXT
5aec9229d4 chore: remove unnecessary console.log 2025-11-14 18:16:59 +08:00
SiriusXT
0c954322e4 i18n: remove unused translation note_properties.info 2025-11-14 18:08:20 +08:00
SiriusXT
9580d636cf client/pageurl: migrate note origin to info bar 2025-11-14 17:20:35 +08:00
23 changed files with 115 additions and 88 deletions

View File

@ -30,6 +30,7 @@ import ScrollingContainer from "../widgets/containers/scrolling_container.js";
import ScrollPadding from "../widgets/scroll_padding.js";
import SearchResult from "../widgets/search_result.jsx";
import SharedInfo from "../widgets/shared_info.jsx";
import OriginInfo from "../widgets/note_origin.jsx";
import SpacerWidget from "../widgets/spacer.js";
import SplitNoteContainer from "../widgets/containers/split_note_container.js";
import SqlResults from "../widgets/sql_result.js";
@ -139,6 +140,7 @@ export default class DesktopLayout {
.filling()
.child(new ContentHeader()
.child(<ReadOnlyNoteInfoBar />)
.child(<OriginInfo />)
.child(<SharedInfo />)
)
.child(<PromotedAttributes />)

View File

@ -8,6 +8,7 @@ import Color, { ColorInstance } from "color";
import Debouncer from "../../utils/debouncer";
import FNote from "../../entities/fnote";
import froca from "../../services/froca";
import { isMobile } from "../../services/utils";
const COLOR_PALETTE = [
"#e64d4d", "#e6994d", "#e5e64d", "#99e64d", "#4de64d", "#4de699",
@ -62,13 +63,13 @@ export default function NoteColorPicker(props: NoteColorPickerProps) {
} else {
attributes.removeOwnedLabelByName(note, "color");
}
setCurrentColor(color);
}
}, [note, currentColor]);
return <div className="note-color-picker">
<ColorCell className="color-cell-reset"
tooltip={t("note-color.clear-color")}
color={null}
@ -81,8 +82,8 @@ export default function NoteColorPicker(props: NoteColorPickerProps) {
<path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" />
</svg>
</ColorCell>
{COLOR_PALETTE.map((color) => (
<ColorCell key={color}
tooltip={t("note-color.set-color")}
@ -128,7 +129,6 @@ function CustomColorCell(props: ColorCellProps) {
const colorInput = useRef<HTMLInputElement>(null);
const colorInputDebouncer = useRef<Debouncer<string | null> | null>(null);
const callbackRef = useRef(props.onSelect);
const isSafari = useRef(/^((?!chrome|android).)*safari/i.test(navigator.userAgent));
useEffect(() => {
colorInputDebouncer.current = new Debouncer(250, (color) => {
@ -160,13 +160,13 @@ function CustomColorCell(props: ColorCellProps) {
}, [pickedColor]);
return <div style={`--foreground: ${getForegroundColor(props.color)};`}
onClick={(e) => {
// The color picker dropdown will close on Safari if the parent context menu is
onClick={isMobile() ? (e) => {
// The color picker dropdown will close on some browser if the parent context menu is
// dismissed, so stop the click propagation to prevent dismissing the menu.
isSafari.current && e.stopPropagation();
}}>
e.stopPropagation();
} : undefined}>
<ColorCell {...props}
color={pickedColor}
color={pickedColor}
className={clsx("custom-color-cell", {
"custom-color-cell-empty": (pickedColor === null)
})}
@ -201,4 +201,4 @@ function tryParseColor(colorStr: string): ColorInstance | null {
}
return null;
}
}

View File

@ -1316,7 +1316,7 @@ body.mobile #context-menu-container.mobile-bottom-menu {
inset-inline-end: 0 !important;
bottom: 0 !important;
top: unset !important;
max-height: 70vh;
max-height: 90vh;
overflow: auto !important;
user-select: none;
-webkit-user-select: none;

View File

@ -119,17 +119,6 @@ body.backdrop-effects-disabled {
font-size: 0.9rem !important;
}
body.mobile .dropdown-menu {
backdrop-filter: var(--dropdown-backdrop-filter);
border-radius: var(--dropdown-border-radius);
position: relative;
}
body.mobile .dropdown-menu .dropdown-menu {
backdrop-filter: unset !important;
border-radius: unset !important;
}
body.desktop .dropdown-menu::before,
:root .ck.ck-dropdown__panel::before,
:root .excalidraw .popover::before,
@ -157,17 +146,12 @@ body.desktop .dropdown-submenu .dropdown-menu::before {
content: unset;
}
body.mobile .dropdown-submenu .dropdown-menu {
background: transparent !important;
}
body.desktop .dropdown-submenu .dropdown-menu {
backdrop-filter: var(--dropdown-backdrop-filter);
background: transparent;
}
.dropdown-item,
body.mobile .dropdown-submenu .dropdown-toggle,
.excalidraw .context-menu .context-menu-item {
--menu-item-start-padding: 8px;
--menu-item-end-padding: 22px;
@ -201,10 +185,6 @@ body.mobile .dropdown-item:not(:last-of-type) {
margin-bottom: 0.5em;
}
body.mobile .dropdown-submenu:hover {
background: transparent !important;
}
html body .dropdown-item.disabled,
html body .dropdown-item[disabled] {
color: var(--menu-text-color) !important;
@ -322,13 +302,25 @@ body.desktop .dropdown-menu.static .dropdown-item.active {
}
/* #region Mobile tweaks for dropdown menus */
body.mobile #context-menu-cover {
transition: background-color 150ms ease-in;
&.show {
background: rgba(0, 0, 0, 0.7);
}
}
body.mobile .dropdown-menu {
--dropdown-menu-padding-vertical: 0.7em;
--dropdown-menu-padding-horizontal: 1em;
--hover-item-background-color: var(--card-background-color);
font-size: 1em !important;
backdrop-filter: var(--dropdown-backdrop-filter);
border-radius: var(--dropdown-border-radius) var(--dropdown-border-radius) 0 0;
position: relative;
.dropdown-toggle::after {
top: var(--dropdown-menu-padding-vertical);
top: 0.5em;
right: var(--dropdown-menu-padding-horizontal);
transform: translateX(50%) rotate(90deg);
}
@ -366,6 +358,37 @@ body.mobile .dropdown-menu {
.dropdown-divider {
visibility: hidden;
}
.dropdown-submenu {
padding: 0 !important;
backdrop-filter: unset !important;
.dropdown-toggle {
padding: var(--dropdown-menu-padding-vertical) var(--dropdown-menu-padding-horizontal);
}
.dropdown-menu {
--menu-background-color: rgba(0, 0, 0, 0.15);
border-radius: 0;
max-height: 0;
transition: max-height 100ms ease-in;
display: block !important;
&.show {
max-height: 1000px;
}
.dropdown-item {
background: transparent;
}
}
&.submenu-open {
.dropdown-toggle {
padding-bottom: var(--dropdown-menu-padding-vertical);
}
}
}
}
/* #endregion */

View File

@ -511,7 +511,7 @@
"title": "الملاحظات المعدلة"
},
"note_properties": {
"info": "معلومات"
"this_note_was_originally_taken_from": ""
},
"backend_log": {
"refresh": "تحديث"

View File

@ -840,8 +840,7 @@
"search": "搜索"
},
"note_properties": {
"this_note_was_originally_taken_from": "笔记来源:",
"info": "信息"
"this_note_was_originally_taken_from": "笔记来源:"
},
"owned_attribute_list": {
"owned_attributes": "拥有的属性"

View File

@ -833,8 +833,7 @@
"search": "Suchen"
},
"note_properties": {
"this_note_was_originally_taken_from": "Diese Notiz stammt ursprünglich aus:",
"info": "Info"
"this_note_was_originally_taken_from": "Diese Notiz stammt ursprünglich aus:"
},
"owned_attribute_list": {
"owned_attributes": "Eigene Attribute"

View File

@ -840,8 +840,7 @@
"search": "Search"
},
"note_properties": {
"this_note_was_originally_taken_from": "This note was originally taken from:",
"info": "Info"
"this_note_was_originally_taken_from": "This note was originally taken from:"
},
"owned_attribute_list": {
"owned_attributes": "Owned Attributes"

View File

@ -840,8 +840,7 @@
"search": "Buscar"
},
"note_properties": {
"this_note_was_originally_taken_from": "Esta nota fue tomada originalmente de:",
"info": "Información"
"this_note_was_originally_taken_from": "Esta nota fue tomada originalmente de:"
},
"owned_attribute_list": {
"owned_attributes": "Atributos propios"

View File

@ -834,8 +834,7 @@
"search": "Recherche"
},
"note_properties": {
"this_note_was_originally_taken_from": "Cette note est initialement extraite de :",
"info": "Infos"
"this_note_was_originally_taken_from": "Cette note est initialement extraite de :"
},
"owned_attribute_list": {
"owned_attributes": "Attributs propres"

View File

@ -1409,8 +1409,7 @@
"search": "Ricerca"
},
"note_properties": {
"this_note_was_originally_taken_from": "Questa nota è stata originariamente tratta da:",
"info": "Informazioni"
"this_note_was_originally_taken_from": "Questa nota è stata originariamente tratta da:"
},
"owned_attribute_list": {
"owned_attributes": "Attributi posseduti"

View File

@ -692,7 +692,6 @@
"outside_hoisted": "このパスはホイストされたノートの外側にあるため、ホイストを解除する必要があります。"
},
"note_properties": {
"info": "情報",
"this_note_was_originally_taken_from": "このノートは元々以下から引用したものです:"
},
"similar_notes": {

View File

@ -447,8 +447,7 @@
"search": "Szukaj"
},
"note_properties": {
"this_note_was_originally_taken_from": "Ta notatka oryginalnie została wzięta z:",
"info": "Info"
"this_note_was_originally_taken_from": "Ta notatka oryginalnie została wzięta z:"
},
"owned_attribute_list": {
"owned_attributes": "Posiadane atrybuty"

View File

@ -809,8 +809,7 @@
"search": "Pesquisar"
},
"note_properties": {
"this_note_was_originally_taken_from": "Esta nota foi originalmente obtida de:",
"info": "Informações"
"this_note_was_originally_taken_from": "Esta nota foi originalmente obtida de:"
},
"owned_attribute_list": {
"owned_attributes": "Atributos próprios"

View File

@ -1075,8 +1075,7 @@
"outside_hoisted": "Este caminho está fora de uma nota fixada e você teria que desafixar."
},
"note_properties": {
"this_note_was_originally_taken_from": "Esta nota foi originalmente obtida de:",
"info": "Informações"
"this_note_was_originally_taken_from": "Esta nota foi originalmente obtida de:"
},
"promoted_attributes": {
"promoted_attributes": "Atributos Promovidos",

View File

@ -910,7 +910,6 @@
"title": "Căile notiței"
},
"note_properties": {
"info": "Informații",
"this_note_was_originally_taken_from": "Această notiță a fost preluată original de la:"
},
"note_type_chooser": {

View File

@ -1066,7 +1066,6 @@
"archived": "Архивировано"
},
"note_properties": {
"info": "Информация",
"this_note_was_originally_taken_from": "Эта заметка была первоначально взята из:"
},
"promoted_attributes": {

View File

@ -837,8 +837,7 @@
"search": "搜尋"
},
"note_properties": {
"this_note_was_originally_taken_from": "筆記來源:",
"info": "資訊"
"this_note_was_originally_taken_from": "筆記來源:"
},
"owned_attribute_list": {
"owned_attributes": "自有屬性"

View File

@ -938,8 +938,7 @@
"outside_hoisted": "Цей шлях знаходиться поза межами закріпленої нотатки і вам доведеться відкріпити."
},
"note_properties": {
"this_note_was_originally_taken_from": "Цю нотатку було спочатку взято з:",
"info": "Інформація"
"this_note_was_originally_taken_from": "Цю нотатку було спочатку взято з:"
},
"owned_attribute_list": {
"owned_attributes": "Власні Атрибути"

View File

@ -0,0 +1,43 @@
import { t } from "../services/i18n";
import { useNoteContext, useTriliumEvent, useTriliumOption } from "./react/hooks";
import { useEffect, useState } from "preact/hooks";
import attributes from "../services/attributes";
import InfoBar from "./react/InfoBar";
import RawHtml from "./react/RawHtml";
import FNote from "../entities/fnote";
export default function OriginInfo() {
const { note } = useNoteContext();
const [link, setLink] = useState<string>();
function refresh() {
if (!note) return;
const pageUrl = getPageUrl(note);
if (!pageUrl) {
setLink(undefined);
return;
}
setLink(`<a href="${pageUrl}" class="external tn-link">${pageUrl}</a>`);
}
useEffect(refresh, [note]);
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
if (loadResults.getAttributeRows().find((attr) => attr.type === "label" && attr.name?.toString() === "pageUrl" && attributes.isAffecting(attr, note))) {
refresh();
}
});
return (
<InfoBar className="origin-info-widget" type="subtle" style={{ display: (!link) ? "none" : undefined }}>
{link && (
<RawHtml
html={`${t("note_properties.this_note_was_originally_taken_from")} ${link}`}
/>
)}
</InfoBar>
)
}
function getPageUrl(note: FNote) {
return note.getOwnedLabelValue("pageUrl");
}

View File

@ -1,20 +0,0 @@
import { t } from "../../services/i18n";
import { useNoteLabel } from "../react/hooks";
import { TabContext } from "./ribbon-interface";
/**
* TODO: figure out better name or conceptualize better.
*/
export default function NotePropertiesTab({ note }: TabContext) {
const [ pageUrl ] = useNoteLabel(note, "pageUrl");
return (
<div className="note-properties-widget" style={{ padding: "12px", color: "var(--muted-text-color)" }}>
{ pageUrl && (
<div style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
{t("note_properties.this_note_was_originally_taken_from")} <a href={pageUrl} class="page-url external">{pageUrl}</a>
</div>
)}
</div>
)
}

View File

@ -1,6 +1,5 @@
import ScriptTab from "./ScriptTab";
import EditedNotesTab from "./EditedNotesTab";
import NotePropertiesTab from "./NotePropertiesTab";
import NoteInfoTab from "./NoteInfoTab";
import SimilarNotesTab from "./SimilarNotesTab";
import FilePropertiesTab from "./FilePropertiesTab";
@ -59,13 +58,6 @@ export const RIBBON_TAB_DEFINITIONS: TabConfiguration[] = [
show: ({ note }) => note?.type === "book" || note?.type === "search",
toggleCommand: "toggleRibbonTabBookProperties"
},
{
title: t("note_properties.info"),
icon: "bx bx-info-square",
content: NotePropertiesTab,
show: ({ note }) => !!note?.getLabelValue("pageUrl"),
activate: true
},
{
title: t("file_properties.title"),
icon: "bx bx-file",

View File

@ -27,7 +27,8 @@ async function register(app: express.Application) {
appType: "custom",
cacheDir: path.join(srcRoot, "../../.cache/vite"),
base: `/${assetUrlFragment}/`,
root: clientDir
root: clientDir,
css: { devSourcemap: true }
});
app.use(`/${assetUrlFragment}/`, (req, res, next) => {
req.url = `/${assetUrlFragment}` + req.url;