Merge branch 'enhance/mobile-silk' of https://github.com/logseq/logseq into enhance/mobile-silk

This commit is contained in:
charlie
2025-07-15 13:01:34 +08:00
12 changed files with 357 additions and 105 deletions

View File

@@ -109,6 +109,18 @@
(def parallax-page-stack-topbar-title-outlet (silkhq-wrap "ParallaxPageStack.TopBarTitleOutlet"))
(def parallax-page-stack-topbar-title-container (silkhq-wrap "ParallaxPageStack.TopBarTitleContainer"))
(def page (silkhq-wrap "Page.Root"))
(def page-portal (silkhq-wrap "Page.Portal"))
(def page-handle (silkhq-wrap "Page.Handle"))
(def page-content (silkhq-wrap "Page.Content"))
(def page-title (silkhq-wrap "Page.Title"))
(def page-description (silkhq-wrap "Page.Description"))
(def page-trigger (silkhq-wrap "Page.Trigger"))
(def page-outlet (silkhq-wrap "Page.Outlet"))
(def page-backdrop (silkhq-wrap "Page.Backdrop"))
(def page-view (silkhq-wrap "Page.View"))
(def card-sheet (silkhq-wrap "CardSheet.Root"))
(def card-sheet-portal (silkhq-wrap "CardSheet.Portal"))
(def card-sheet-handle (silkhq-wrap "CardSheet.Handle"))

View File

@@ -0,0 +1,16 @@
.Page-view {
/* SELF-LAYOUT */
z-index: 1;
/* Adding 60px to make it fully visible below iOS Safari's bottom UI */
height: calc(var(--silk-100-lvh-dvh-pct) + 60px);
}
.Page-content {
/* SELF-LAYOUT */
box-sizing: border-box;
width: 100%;
/* APPEARANCE */
background-color: white;
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
}

View File

@@ -0,0 +1,104 @@
import React from 'react'
import { Sheet } from '@silk-hq/components'
import './Page.css'
// ================================================================================================
// Root
// ================================================================================================
type SheetRootProps = React.ComponentPropsWithoutRef<typeof Sheet.Root>;
type PageRootProps = Omit<SheetRootProps, 'license'> & {
license?: SheetRootProps['license'];
};
const PageRoot = React.forwardRef<React.ElementRef<typeof Sheet.Root>, PageRootProps>(
({ children, ...restProps }, ref) => {
return (
<Sheet.Root license="commercial" {...restProps} ref={ref}>
{children}
</Sheet.Root>
)
}
)
PageRoot.displayName = 'Page.Root'
// ================================================================================================
// View
// ================================================================================================
const PageView = React.forwardRef<
React.ElementRef<typeof Sheet.View>,
React.ComponentPropsWithoutRef<typeof Sheet.View>
>(({ children, className, ...restProps }, ref) => {
return (
<Sheet.View
className={`Page-view ${className ?? ''}`.trim()}
contentPlacement="right"
swipeOvershoot={false}
nativeEdgeSwipePrevention={true}
{...restProps}
ref={ref}
>
{children}
</Sheet.View>
)
})
PageView.displayName = 'Page.View'
// ================================================================================================
// Backdrop
// ================================================================================================
const PageBackdrop = React.forwardRef<
React.ElementRef<typeof Sheet.Backdrop>,
React.ComponentPropsWithoutRef<typeof Sheet.Backdrop>
>(({ className, ...restProps }, ref) => {
return (
<Sheet.Backdrop
className={`Page-backdrop ${className ?? ''}`.trim()}
{...restProps}
ref={ref}
/>
)
})
PageBackdrop.displayName = 'Page.Backdrop'
// ================================================================================================
// Content
// ================================================================================================
const PageContent = React.forwardRef<
React.ElementRef<typeof Sheet.Content>,
React.ComponentPropsWithoutRef<typeof Sheet.Content>
>(({ children, className, ...restProps }, ref) => {
return (
<Sheet.Content className={`Page-content ${className ?? ''}`.trim()} {...restProps} ref={ref}>
{children}
</Sheet.Content>
)
})
PageContent.displayName = 'Page.Content'
// ================================================================================================
// Unchanged Components
// ================================================================================================
const PagePortal = Sheet.Portal
const PageTrigger = Sheet.Trigger
const PageHandle = Sheet.Handle
const PageOutlet = Sheet.Outlet
const PageTitle = Sheet.Title
const PageDescription = Sheet.Description
export const Page = {
Root: PageRoot,
Portal: PagePortal,
View: PageView,
Backdrop: PageBackdrop,
Content: PageContent,
Trigger: PageTrigger,
Handle: PageHandle,
Outlet: PageOutlet,
Title: PageTitle,
Description: PageDescription,
}

View File

@@ -7,6 +7,7 @@ import { SheetWithStacking, SheetWithStackingStack } from './SheetWithStacking'
import { ParallaxPage, ParallaxPageStack } from './ParallaxPage'
import { Toast } from './Toast'
import { Card } from './Card'
import { Page } from './Page'
declare global {
var LSSilkhq: any
@@ -18,7 +19,7 @@ const silkhq = {
SheetWithStacking, SheetWithDetent,
SheetWithStackingStack,
ParallaxPage, ParallaxPageStack,
Toast, CardSheet: Card,
Toast, CardSheet: Card, Page
}
window.LSSilkhq = silkhq

View File

@@ -695,7 +695,6 @@
(rum/defcs ^:large-vars/cleanup-todo page-inner <
(rum/local false ::mouse-down?)
(rum/local false ::hover?)
"The inner div of page reference component
page-name-in-block is the overridable name of the page (legacy)
@@ -707,8 +706,7 @@
:or {with-parent? true}
:as config}
page-entity children label]
(let [*hover? (::hover? state)
*mouse-down? (::mouse-down? state)
(let [*mouse-down? (::mouse-down? state)
tag? (:tag? config)
page-name (when (:block/title page-entity)
(util/page-name-sanity-lc (:block/title page-entity)))
@@ -728,8 +726,6 @@
:draggable true
:on-drag-start (fn [e]
(editor-handler/block->data-transfer! page-name e true))
:on-mouse-over #(reset! *hover? true)
:on-mouse-leave #(reset! *hover? false)
:on-pointer-down (fn [^js e]
(cond
(util/link? (.-target e))
@@ -833,26 +829,18 @@
(let [*el-trigger (hooks/use-ref nil)]
(hooks/use-effect!
(fn []
(when-not (state/editing?)
(when (true? visible?)
(shui/popup-show!
(hooks/deref *el-trigger) render
{:root-props {:onOpenChange (fn [v] (set-visible! v))
:modal false}
:content-props {:class "ls-preview-popup"
:onInteractOutside (fn [^js e] (.preventDefault e))
:onEscapeKeyDown (fn [^js e]
(when (state/editing?)
(.preventDefault e)
(some-> (hooks/deref *el-popup) (.focus))))}
:as-dropdown? false}))
(when (false? visible?)
(shui/popup-hide!)
(when (state/get-edit-block)
(state/clear-edit!)))
(hooks/set-ref! *timer nil)
(hooks/set-ref! *timer1 nil))
(when (true? visible?)
(shui/popup-show!
(hooks/deref *el-trigger) render
{:root-props {:onOpenChange (fn [v] (set-visible! v))
:modal false}
:content-props {:class "ls-preview-popup"
:onInteractOutside (fn [^js e] (.preventDefault e))
:onEscapeKeyDown (fn [^js e]
(when (state/editing?)
(.preventDefault e)
(some-> (hooks/deref *el-popup) (.focus))))}
:as-dropdown? false}))
;; teardown
(fn []
@@ -862,17 +850,17 @@
[:span.preview-ref-link
{:ref *el-trigger
:on-mouse-enter (fn [^js e]
(when (= (some-> (.-target e) (.closest ".preview-ref-link"))
(hooks/deref *el-trigger))
(let [timer (hooks/deref *timer)
timer1 (hooks/deref *timer1)]
(when-not timer
(hooks/set-ref! *timer
(js/setTimeout #(set-visible! true) 1000)))
(when timer1
(js/clearTimeout timer1)
(hooks/set-ref! *timer1 nil)))))
:on-mouse-move (fn [^js e]
(when (= (some-> (.-target e) (.closest ".preview-ref-link"))
(hooks/deref *el-trigger))
(let [timer (hooks/deref *timer)
timer1 (hooks/deref *timer1)]
(when-not timer
(hooks/set-ref! *timer
(js/setTimeout #(set-visible! true) 1000)))
(when timer1
(js/clearTimeout timer1)
(hooks/set-ref! *timer1 nil)))))
:on-mouse-leave (fn []
(let [timer (hooks/deref *timer)
timer1 (hooks/deref *timer1)]
@@ -941,8 +929,7 @@
(if (boolean? in-popup?)
(if (and (not (:preview? config))
(not in-popup?)
(or (not manual?) open?)
(not (state/editing?)))
(or (not manual?) open?))
(popup-preview-impl children
{:visible? visible? :set-visible! set-visible!
:*timer *timer :*timer1 *timer1
@@ -2655,13 +2642,7 @@
selection-blocks (state/get-selection-blocks)
starting-block (state/get-selection-start-block-or-first)
mobile-selection? (and (util/capacitor-new?) (seq selection-blocks))
block-dom-element (util/rec-get-node target "ls-block")
cursor-range (if (util/ios?)
(:block/title block)
(some-> block-dom-element
(dom/by-class "block-content-inner")
first
util/caret-range))]
block-dom-element (util/rec-get-node target "ls-block")]
(if mobile-selection?
(let [ids (set (state/get-selection-block-ids))]
@@ -2722,7 +2703,13 @@
(->> title
(property-file/remove-built-in-properties-when-file-based
(state/get-current-repo) format)
(drawer/remove-logbook)))]
(drawer/remove-logbook)))
cursor-range (if (util/ios?)
(:block/title block)
(some-> block-dom-element
(dom/by-class "block-content-inner")
first
util/caret-range))]
(state/set-editing!
edit-input-id
content

View File

@@ -1,6 +1,7 @@
(ns frontend.handler.db-based.rtc-flows
"Flows related to RTC"
(:require [frontend.common.missionary :as c.m]
[frontend.common.thread-api :as thread-api :refer [def-thread-api]]
[frontend.flows :as flows]
[frontend.mobile.flows :as mobile-flows]
[frontend.state :as state]
@@ -94,6 +95,10 @@ conditions:
(assert (some? repo))
(reset! *rtc-start-trigger repo))
(def-thread-api :thread-api/rtc-start-request
[repo]
(trigger-rtc-start repo))
(def ^:private document-visible&rtc-not-running-flow
(m/ap
(let [visibility (m/?< flows/document-visibility-state-flow)]

View File

@@ -10,4 +10,4 @@
(if config/dev?
(log/set-levels {:glogi/root :info})
(log/set-levels {:glogi/root :warn}))
(log/set-levels {:glogi/root :info}))

View File

@@ -33,6 +33,7 @@
[frontend.worker.thread-atom]
[frontend.worker.util :as worker-util]
[goog.object :as gobj]
[lambdaisland.glogi :as log]
[lambdaisland.glogi.console :as glogi-console]
[logseq.common.util :as common-util]
[logseq.db :as ldb]
@@ -841,6 +842,7 @@
(into {})
bean/->js)]
(glogi-console/install!)
(log/set-levels {:glogi/root :info})
(check-worker-scope!)
(outliner-register-op-handlers!)
(<ratelimit-file-writes!)

View File

@@ -247,6 +247,7 @@
started-dfv
(m/sp
(try
(log/info :rtc :loop-starting)
;; init run to open a ws
(m/? get-ws-create-task)
(started-dfv true)
@@ -368,7 +369,10 @@
rtc-loop-task
:fail (fn [e]
(reset! *last-stop-exception e)
(log/info :rtc-loop-task e)))
(log/info :rtc-loop-task e)
(when (= :rtc.exception/ws-timeout (some-> e ex-data :type))
;; if fail reason is websocket-timeout, try to restart rtc
(worker-state/<invoke-main-thread :thread-api/rtc-start-request repo))))
start-ex (m/? onstarted-task)]
(if (instance? ExceptionInfo start-ex)
(do

View File

@@ -1,5 +1,6 @@
:root {
--ls-page-title-size: 26px;
--silk-topbar-height: 48px;
}
html.is-native-ios {
@@ -334,7 +335,7 @@ html[data-color=logseq] {
/* silk styles */
.app-silk-index-scroll-content {
@apply bg-white min-h-[100svh];
@apply bg-gray-01 min-h-[100svh];
}
.app-silk-scroll-view {
@@ -356,6 +357,58 @@ html[data-color=logseq] {
.app-silk-index-container {
@apply p-4 flex flex-col gap-3;
padding-top: var(--safe-area-inset-top);
padding-top: calc(var(--safe-area-inset-top, 0px) + var(--silk-topbar-height) + 10px);
}
.app-silk-topbar {
@apply fixed top-0 left-0 w-full border-b bg-gray-02 justify-between
items-center overflow-hidden px-2 grid grid-cols-8 gap-4;
padding-bottom: 10px;
padding-top: calc(var(--safe-area-inset-top, 0px) + 10px);
> .as-left, .as-right {
@apply flex items-center col-span-2 gap-2;
.ui__button {
@apply opacity-50 px-1;
&:active {
@apply opacity-80;
}
}
}
> .as-right {
@apply justify-end px-1;
}
> .title {
@apply font-semibold overflow-hidden text-ellipsis whitespace-nowrap col-span-4
block text-center;
}
}
.app-silk-tabs {
@apply flex items-center h-[54px] border-t overflow-hidden
bg-gray-02 fixed left-0 bottom-0 w-full z-10 dark:bg-gray-01;
bottom: 0;
> .as-item {
@apply flex flex-1 flex-col items-center pb-1 transition-opacity;
@apply opacity-40 active:opacity-70;
&.active {
@apply text-accent-10 opacity-100;
> small {
@apply font-semibold;
}
}
> small {
@apply text-[9px] -mt-2;
}
}
}

View File

@@ -1,6 +1,7 @@
(ns mobile.components.demos
(:require [logseq.shui.ui :as shui]
[rum.core :as rum]
[mobile.components.ui-silk :as ui-silk]
[logseq.shui.silkhq :as silkhq]))
(rum/defc depth-view-example
@@ -43,66 +44,98 @@
(silkhq/stacking-sheet-portal
(stacking-view-example {:nested? false}))))])))
(rum/defc page-view-example
[]
(silkhq/page-portal
(silkhq/page-view
(silkhq/page-backdrop)
(silkhq/page-content
(silkhq/scroll {:as-child true}
(silkhq/scroll-view
{:class "h-full"}
(silkhq/scroll-content {:as-child true}
[:article.p-6
(for [_ (range 80)]
[:h2.text-lg.font-medium.my-4.bg-green-100
"inner page"])])))))))
(rum/defc silkhq-demos-page
[]
(silkhq/depth-sheet-stack {:as-child true}
(silkhq/depth-sheet-scenery-outlets
;; as root page
(silkhq/parallax-page-stack {:as-child true}
(silkhq/parallax-page-stack-scenery-outlet {:as-child true}
(silkhq/scroll {:as-child true}
(silkhq/scroll-view
{:safeArea "none"
:pageScroll true
:nativePageScrollReplacement true}
(silkhq/scroll-content {:class "app-silk-index-scroll-content"}
[:div.app-silk-index-container
[:h2.text-xl.font-semibold.pt-4 "Silk sheets demos"]
(silkhq/scroll {:as-child true}
(silkhq/scroll-view
{:safeArea "none"
:pageScroll true
:nativePageScrollReplacement true}
(silkhq/scroll-content {:class "app-silk-index-scroll-content"}
[:div.app-silk-index-container
[:h2.text-xl.font-semibold.pt-2 "Silk sheets demos"]
;; Bottom Sheet case
(silkhq/bottom-sheet
(silkhq/bottom-sheet-trigger
{:class "w-full"}
(shui/button {:variant :secondary :class "w-full"} "0. Static Bottom Sheet"))
(silkhq/bottom-sheet-portal
(silkhq/bottom-sheet-view
(silkhq/bottom-sheet-backdrop)
(silkhq/bottom-sheet-content
{:class "flex flex-col items-center p-2"}
(silkhq/bottom-sheet-handle)
[:div.py-60.flex
[:h1.my-4.text-2xl "hello silkhq"]]))))
;; Bottom Sheet case
(silkhq/bottom-sheet
(silkhq/bottom-sheet-trigger
{:class "w-full"}
(shui/button {:variant :secondary :class "w-full"} "0. Static Bottom Sheet"))
(silkhq/bottom-sheet-portal
(silkhq/bottom-sheet-view
(silkhq/bottom-sheet-backdrop)
(silkhq/bottom-sheet-content
{:class "flex flex-col items-center p-2"}
(silkhq/bottom-sheet-handle)
[:div.py-60.flex
[:h1.my-4.text-2xl "hello silkhq"]]))))
;; Detent Sheet case
(silkhq/detent-sheet
(silkhq/detent-sheet-trigger
{:class "w-full"}
(shui/button {:variant :secondary :class "w-full"} "1. Detent Bottom Sheet"))
(silkhq/detent-sheet-portal
(silkhq/detent-sheet-view
(silkhq/detent-sheet-backdrop)
(silkhq/detent-sheet-content
{:class "flex flex-col items-center p-2"}
(silkhq/detent-sheet-handle)
[:div.py-60.flex
[:h1.my-4.text-2xl "hello silkhq"]]))))
;; Detent Sheet case
(silkhq/detent-sheet
(silkhq/detent-sheet-trigger
{:class "w-full"}
(shui/button {:variant :secondary :class "w-full"} "1. Detent Bottom Sheet"))
(silkhq/detent-sheet-portal
(silkhq/detent-sheet-view
(silkhq/detent-sheet-backdrop)
(silkhq/detent-sheet-content
{:class "flex flex-col items-center p-2"}
(silkhq/detent-sheet-handle)
[:div.py-60.flex
[:h1.my-4.text-2xl "hello silkhq"]]))))
;; Depth Sheet case
(silkhq/depth-sheet
(silkhq/depth-sheet-trigger
{:class "w-full"}
(shui/button {:variant :secondary :class "w-full"} "2. Depth Bottom Sheet"))
(silkhq/depth-sheet-portal
(depth-view-example {:nested? false})))
;; Depth Sheet case
(silkhq/depth-sheet
(silkhq/depth-sheet-trigger
{:class "w-full"}
(shui/button {:variant :secondary :class "w-full"} "2. Depth Bottom Sheet"))
(silkhq/depth-sheet-portal
(depth-view-example {:nested? false})))
;; Stacking depth sheet case
(silkhq/stacking-sheet-stack
{:as-child true}
(silkhq/stacking-sheet
(silkhq/stacking-sheet-trigger
{:class "w-full"}
(shui/button {:variant :secondary :class "w-full"} "3. Stacking Bottom Sheet"))
;; Stacking depth sheet case
(silkhq/stacking-sheet-stack
{:as-child true}
(silkhq/stacking-sheet
(silkhq/stacking-sheet-trigger
{:class "w-full"}
(shui/button {:variant :secondary :class "w-full"} "3. Stacking Bottom Sheet"))
(silkhq/stacking-sheet-portal
(stacking-view-example {:nested? false}))))
]))))))))
(silkhq/stacking-sheet-portal
(stacking-view-example {:nested? false}))))
;; parallax page
(silkhq/page
(silkhq/page-trigger
{:class "w-full"}
(shui/button {:variant :secondary :class "w-full"} "4. Single page"))
(page-view-example))])))
;; app topbar
(ui-silk/app-silk-topbar
{:title "Silk Demos "
:left-render (shui/button {:variant :icon :size :sm}
(shui/tabler-icon "chevron-left" {:size 22}))
:right-render [:<>
(shui/button {:variant :icon :size :sm}
(shui/tabler-icon "plus" {:size 22}))
(shui/button {:variant :icon :size :sm}
(shui/tabler-icon "dots" {:size 22}))]})
;; app tabs
(ui-silk/app-silk-tabs)
)))

View File

@@ -0,0 +1,35 @@
(ns mobile.components.ui-silk
(:require [logseq.shui.ui :as shui]
[rum.core :as rum]))
(rum/defc app-silk-topbar
[{:keys [left-render right-render title]}]
[:div.app-silk-topbar
[:div.as-left (if (fn? left-render)
(left-render) left-render)]
[:strong.title title]
[:div.as-right (if (fn? right-render)
(right-render) right-render)]])
(rum/defc app-silk-tabs []
[:div.app-silk-tabs
[:span.as-item.active
(shui/button {:variant :icon}
(shui/tabler-icon "home" {:size 23}))
[:small "Journals"]]
[:span.as-item
(shui/button {:variant :icon}
(shui/tabler-icon "search" {:size 23}))
[:small "Search"]]
[:span.as-item
(shui/button {:variant :icon}
(shui/tabler-icon "plus" {:size 23}))
[:small "Quick add"]]
[:span.as-item
(shui/button {:variant :icon}
(shui/tabler-icon "settings" {:size 23}))
[:small "Settings"]]
[:span.as-item
(shui/button {:variant :icon}
(shui/tabler-icon "bug" {:size 23}))
[:small "Demos"]]])