@funhub/platform 0.1.108 → 0.1.109

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/components/biz/business/card-grid/variants/card-grid-2x3-infinite/runtime/client.mjs +1 -1
  2. package/dist/components/biz/business/card-grid/variants/card-grid-3x3-infinite/runtime/client.mjs +1 -1
  3. package/dist/components/biz/business/home-recommend/shared/home-recommend-base.mjs +1 -1
  4. package/dist/components/biz/business/post-card/variants/basic-post-card/runtime/client.mjs +1 -1
  5. package/dist/components/biz/business/profile/profile-main/shell.mjs +1 -1
  6. package/dist/components/pages/home/client.mjs +1 -1
  7. package/dist/components/ui/badge.d.mts +1 -1
  8. package/dist/components/ui/button-group.d.mts +1 -1
  9. package/dist/components/ui/button.d.mts +2 -2
  10. package/dist/components/ui/field.d.mts +1 -1
  11. package/dist/components/ui/infinite-scroll.d.mts +0 -5
  12. package/dist/components/ui/infinite-scroll.mjs +1 -1
  13. package/dist/components/ui/input-group.d.mts +1 -1
  14. package/dist/components/ui/item.d.mts +1 -1
  15. package/dist/hooks/use-sticky.mjs +1 -1
  16. package/dist/utils/helper.mjs +1 -1
  17. package/package.json +2 -6
  18. package/bin/index.mjs +0 -323
  19. package/templates/base/_meta.json +0 -4
  20. package/templates/base/template/README.md.template +0 -32
  21. package/templates/base/template/__dot__gitignore +0 -6
  22. package/templates/base/template/app/[locale]/layout.tsx.template +0 -36
  23. package/templates/base/template/app/[locale]/page.tsx.template +0 -24
  24. package/templates/base/template/app/globals.css +0 -26
  25. package/templates/base/template/app/layout.tsx.template +0 -38
  26. package/templates/base/template/eslint.config.mjs.template +0 -68
  27. package/templates/base/template/i18n/request.ts.template +0 -3
  28. package/templates/base/template/next-env.d.ts.template +0 -4
  29. package/templates/base/template/next.config.ts.template +0 -21
  30. package/templates/base/template/package.json.template +0 -37
  31. package/templates/base/template/postcss.config.js.template +0 -18
  32. package/templates/base/template/proxy.ts.template +0 -18
  33. package/templates/base/template/tailwind.config.cjs.template +0 -13
  34. package/templates/base/template/tsconfig.json.template +0 -43
@@ -1,2 +1,2 @@
1
1
 
2
- "use client";import{CardSpec as e}from"../../../../../utils/constants/card-spec.mjs";import{mergeStyles as t}from"../../../../../utils/styles/helpers.mjs";import{transformContentListToCardItems as n}from"../../../../../utils/transformers/card-grid.mjs";import{gContentGetAppChannelBlockData as r}from"../../../../../../../service/generated/client.mjs";import{Box as i}from"../../../../../../ui/box.mjs";import{InfiniteScroll as a}from"../../../../../../ui/infinite-scroll.mjs";import{Text as o}from"../../../../../../ui/text.mjs";import{usePaginated as s}from"../../../../../../../hooks/query/use-query.mjs";import c from"../../../../../basics/card-layout/runtime/client.mjs";import{useLayoutEffect as l,useMemo as u,useRef as d,useState as f}from"react";import{jsx as p,jsxs as m}from"react/jsx-runtime";const h={},g={list:[],title:``,cursor:``,block_id:``};function _({props:_=h,styles:v,data:y=g}){let b=e.V2_LARGE,{gap:x=8}=_||{},{title:S,cursor:C,block_id:w,list:T}=y,E=v?t(v,{}):void 0,D=T&&T.length>0,O=u(()=>{if(!D)return;let e=C&&C.trim()?C:void 0;return{pages:[{data:{block:{data:{videos:T,cursor:C||``}}}}],pageParams:[e]}},[D,T,C]),{data:k,fetchNextPage:A,hasNextPage:j}=s({key:[`card-grid-2x3-infinite`,w],queryFn:e=>r({block_id:w,cursor:e||void 0,page_size:20}),getNextPageParam:e=>{let t=e.data?.block?.data?.cursor;return t&&t.trim()?t:void 0},options:{enabled:!!w,initialData:O,refetchOnMount:!1,refetchOnWindowFocus:!1,refetchOnReconnect:!1},initialPageParam:D&&C&&C.trim()?C:void 0}),M=d(T),[N,P]=f(!1);l(()=>{P(!0)},[]);let F=u(()=>{let e=[...M.current];if(!N)return e;let t=[...e],n=k?.pages;if(n&&n.length>1)for(let e=1;e<n.length;e++){let r=n[e].data?.block?.data?.videos||[];t.push(...r)}return t},[N,k?.pages]),I=u(()=>n(F,`view-duration`),[F]);return m(i,{children:[S&&p(o,{as:`h2`,className:`text-[18px] font-bold text-text1 mb-[10px] px-[12px]`,children:S}),p(a,{loadMore:()=>A().then(()=>void 0),hasMore:j===!0||j===void 0&&!!C&&T.length>0,initialLoad:!0,rootId:`app-scroll-container`,threshold:1e3,className:``,children:p(c,{props:{cardSpec:b,gap:x},data:I,styles:E})})]})}export{_ as default};
2
+ "use client";import{CardSpec as e}from"../../../../../utils/constants/card-spec.mjs";import{mergeStyles as t}from"../../../../../utils/styles/helpers.mjs";import{transformContentListToCardItems as n}from"../../../../../utils/transformers/card-grid.mjs";import{gContentGetAppChannelBlockData as r}from"../../../../../../../service/generated/client.mjs";import{Box as i}from"../../../../../../ui/box.mjs";import{InfiniteScroll as a}from"../../../../../../ui/infinite-scroll.mjs";import{Text as o}from"../../../../../../ui/text.mjs";import{usePaginated as s}from"../../../../../../../hooks/query/use-query.mjs";import c from"../../../../../basics/card-layout/runtime/client.mjs";import{useLayoutEffect as l,useMemo as u,useRef as d,useState as f}from"react";import{jsx as p,jsxs as m}from"react/jsx-runtime";const h={},g={list:[],title:``,cursor:``,block_id:``};function _({props:_=h,styles:v,data:y=g}){let b=e.V2_LARGE,{gap:x=8}=_||{},{title:S,cursor:C,block_id:w,list:T}=y,E=v?t(v,{}):void 0,D=T&&T.length>0,O=u(()=>{if(!D)return;let e=C&&C.trim()?C:void 0;return{pages:[{data:{block:{data:{videos:T,cursor:C||``}}}}],pageParams:[e]}},[D,T,C]),{data:k,fetchNextPage:A,hasNextPage:j}=s({key:[`card-grid-2x3-infinite`,w],queryFn:e=>r({block_id:w,cursor:e||void 0,page_size:20}),getNextPageParam:e=>{let t=e.data?.block?.data?.cursor;return t&&t.trim()?t:void 0},options:{enabled:!!w,initialData:O,refetchOnMount:!1,refetchOnWindowFocus:!1,refetchOnReconnect:!1},initialPageParam:D&&C&&C.trim()?C:void 0}),M=d(T),[N,P]=f(!1);l(()=>{P(!0)},[]);let F=u(()=>{let e=[...M.current];if(!N)return e;let t=[...e],n=k?.pages;if(n&&n.length>1)for(let e=1;e<n.length;e++){let r=n[e].data?.block?.data?.videos||[];t.push(...r)}return t},[N,k?.pages]),I=u(()=>n(F,`view-duration`),[F]);return m(i,{children:[S&&p(o,{as:`h2`,className:`text-[18px] font-bold text-text1 mb-[10px] px-[12px]`,children:S}),p(a,{loadMore:()=>A().then(()=>void 0),hasMore:j===!0||j===void 0&&!!C&&T.length>0,initialLoad:!0,threshold:1e3,className:``,children:p(c,{props:{cardSpec:b,gap:x},data:I,styles:E})})]})}export{_ as default};
@@ -1,2 +1,2 @@
1
1
 
2
- "use client";import{CardSpec as e}from"../../../../../utils/constants/card-spec.mjs";import{mergeStyles as t}from"../../../../../utils/styles/helpers.mjs";import{transformContentListToCardItems as n}from"../../../../../utils/transformers/card-grid.mjs";import{gContentGetAppChannelBlockData as r}from"../../../../../../../service/generated/client.mjs";import{Box as i}from"../../../../../../ui/box.mjs";import{InfiniteScroll as a}from"../../../../../../ui/infinite-scroll.mjs";import{Text as o}from"../../../../../../ui/text.mjs";import{usePaginated as s}from"../../../../../../../hooks/query/use-query.mjs";import c from"../../../../../basics/card-layout/runtime/client.mjs";import{useLayoutEffect as l,useMemo as u,useRef as d,useState as f}from"react";import{jsx as p,jsxs as m}from"react/jsx-runtime";const h={},g={list:[],title:``,cursor:``,block_id:``};function _({props:_=h,styles:v,data:y=g}){let b=e.V3_SMALL,{gap:x=8}=_||{},{title:S,cursor:C,block_id:w,list:T}=y,E=v?t(v,{}):void 0,D=T&&T.length>0,O=u(()=>{if(!D)return;let e=C&&C.trim()?C:void 0;return{pages:[{data:{block:{data:{videos:T,cursor:C||``}}}}],pageParams:[e]}},[D,T,C]),{data:k,fetchNextPage:A,hasNextPage:j}=s({key:[`card-grid-3x3-infinite`,w],queryFn:e=>r({block_id:w,cursor:e||void 0,page_size:21}),getNextPageParam:e=>{let t=e.data?.block?.data?.cursor;return t&&t.trim()?t:void 0},options:{enabled:!!w,initialData:O,refetchOnMount:!1,refetchOnWindowFocus:!1,refetchOnReconnect:!1},initialPageParam:D&&C&&C.trim()?C:void 0}),M=d(T),[N,P]=f(!1);l(()=>{P(!0)},[]);let F=u(()=>{let e=[...M.current];if(!N)return e;let t=[...e],n=k?.pages;if(n&&n.length>1)for(let e=1;e<n.length;e++){let r=n[e].data?.block?.data?.videos||[];t.push(...r)}return t},[N,k?.pages]),I=u(()=>n(F,`view-duration`),[F]);return m(i,{children:[S&&p(o,{as:`h2`,className:`text-[18px] font-bold text-text1 mb-[10px] px-[12px]`,children:S}),p(a,{loadMore:()=>A().then(()=>void 0),hasMore:j===!0||j===void 0&&!!C&&T.length>0,className:``,initialLoad:!0,rootId:`app-scroll-container`,threshold:1e3,children:p(c,{props:{cardSpec:b,gap:x},data:I,styles:E})})]})}export{_ as default};
2
+ "use client";import{CardSpec as e}from"../../../../../utils/constants/card-spec.mjs";import{mergeStyles as t}from"../../../../../utils/styles/helpers.mjs";import{transformContentListToCardItems as n}from"../../../../../utils/transformers/card-grid.mjs";import{gContentGetAppChannelBlockData as r}from"../../../../../../../service/generated/client.mjs";import{Box as i}from"../../../../../../ui/box.mjs";import{InfiniteScroll as a}from"../../../../../../ui/infinite-scroll.mjs";import{Text as o}from"../../../../../../ui/text.mjs";import{usePaginated as s}from"../../../../../../../hooks/query/use-query.mjs";import c from"../../../../../basics/card-layout/runtime/client.mjs";import{useLayoutEffect as l,useMemo as u,useRef as d,useState as f}from"react";import{jsx as p,jsxs as m}from"react/jsx-runtime";const h={},g={list:[],title:``,cursor:``,block_id:``};function _({props:_=h,styles:v,data:y=g}){let b=e.V3_SMALL,{gap:x=8}=_||{},{title:S,cursor:C,block_id:w,list:T}=y,E=v?t(v,{}):void 0,D=T&&T.length>0,O=u(()=>{if(!D)return;let e=C&&C.trim()?C:void 0;return{pages:[{data:{block:{data:{videos:T,cursor:C||``}}}}],pageParams:[e]}},[D,T,C]),{data:k,fetchNextPage:A,hasNextPage:j}=s({key:[`card-grid-3x3-infinite`,w],queryFn:e=>r({block_id:w,cursor:e||void 0,page_size:21}),getNextPageParam:e=>{let t=e.data?.block?.data?.cursor;return t&&t.trim()?t:void 0},options:{enabled:!!w,initialData:O,refetchOnMount:!1,refetchOnWindowFocus:!1,refetchOnReconnect:!1},initialPageParam:D&&C&&C.trim()?C:void 0}),M=d(T),[N,P]=f(!1);l(()=>{P(!0)},[]);let F=u(()=>{let e=[...M.current];if(!N)return e;let t=[...e],n=k?.pages;if(n&&n.length>1)for(let e=1;e<n.length;e++){let r=n[e].data?.block?.data?.videos||[];t.push(...r)}return t},[N,k?.pages]),I=u(()=>n(F,`view-duration`),[F]);return m(i,{children:[S&&p(o,{as:`h2`,className:`text-[18px] font-bold text-text1 mb-[10px] px-[12px]`,children:S}),p(a,{loadMore:()=>A().then(()=>void 0),hasMore:j===!0||j===void 0&&!!C&&T.length>0,className:``,initialLoad:!0,threshold:1e3,children:p(c,{props:{cardSpec:b,gap:x},data:I,styles:E})})]})}export{_ as default};
@@ -1,2 +1,2 @@
1
1
 
2
- "use client";import{pContentGetPopularVideoList as e}from"../../../../../service/generated/client.mjs";import{Image as t}from"../../../../ui/image.mjs";import{Box as n}from"../../../../ui/box.mjs";import{Text as r}from"../../../../ui/text.mjs";import i from"../../../../../assets/icons/detail/video_flag.mjs";import{MiniThreeCard as a}from"../../../../common/cards/mini-three-card.mjs";import{HOME_RECOMMEND_DEFAULT_GRID_COLUMNS as o,HOME_RECOMMEND_DEFAULT_GRID_ROWS as s,HOME_RECOMMEND_DEFAULT_PLAY_COUNT_DELTA as c,HOME_RECOMMEND_DEFAULT_SHOW_EPISODE as l,HOME_RECOMMEND_DEFAULT_SHOW_PLAY_COUNT as u,HOME_RECOMMEND_MAX_GRID_COLUMNS as ee,HOME_RECOMMEND_MAX_GRID_ROWS as te}from"./home-recommend-default-config.mjs";import{HOME_RECOMMEND_CARD_CONFIG_FIELD as d}from"./home-recommend-preview.mjs";import{useCallback as f,useEffect as p,useLayoutEffect as m,useMemo as h,useRef as g,useState as _}from"react";import{jsx as v,jsxs as y}from"react/jsx-runtime";import{useRouter as ne}from"next/navigation";const re={},ie={list:[]};function b({variant:t,props:i=re,data:a=ie,mode:d=`renderer`}){let b=d===`editor`,S=ne(),{list:C,title:se,cursor:T}=a,E=h(()=>Array.isArray(C)?C:[],[C]),D=h(()=>Array.isArray(a.icons)?a.icons:[],[a.icons]),{rows:O,columns:ce,moreLink:k,title:A,smartTagEnabled:j,smartTagIds:M,catId:ue,showPlayCount:de,playCountDelta:fe,showEpisode:pe,enableInfinite:me}=i,F=me===!0&&j===!0,I=!b&&F&&E.length>=16,L=h(()=>Array.isArray(M)?M.map(e=>String(e||``).trim()).filter(Boolean):[],[M]),R=typeof ue==`string`?ue.trim():``,[z,he]=_(0),[ge,_e]=_(E),[ve,ye]=_(D),[B,V]=_(()=>N(T)),[be,xe]=_(!1),Se=g(null),H=g(null),U=g(!1);p(()=>{_e(E),ye(D),V(N(T))},[T,D,E]);let Ce=oe(O,s,te),W=oe(ce,o,ee),G=F?16:t===`grid`?Ce*W:16,we=Ce*W,Te=b&&t===`grid`,K=Te?ge.slice(0,we):ge,q=Te?ve.slice(0,we):ve,Ee=t===`waterfall`?6:4,J=K.length>0?Math.max(0,K.length-Ee):-1,De=!!B,Oe=A&&A.trim()||se||``,Y=de??u,X=Number(fe??c),Z=pe??l,ke=!F,Q=ke?ae(k):``,Ae=ke&&!!Q,je=f(async()=>{let t=B;if(!(!I||!t||U.current)){U.current=!0,xe(!0);try{let n=await e({cursor:t,page_size:G,tags:L.length>0?L:void 0,cat_id:R||void 0}),r=Array.isArray(n.data?.videos)?n.data.videos:[],i=N(n.data?.cursor);r.length>0&&_e(e=>e.concat(r)),V(e=>{if(!(!r.length||!i))return i===e?void 0:i})}catch(e){console.error(`推荐位加载下一页失败(client)`,e),V(void 0)}finally{U.current=!1,xe(!1)}}},[B,R,G,L,I]),Me=f(()=>{Q&&(Q.startsWith(`http`)?window.location.href=Q:S.push(Q))},[Q,S]);p(()=>{let e=H.current;if(!e||!I||!De)return;let t=document.getElementById(`app-scroll-container`)||null,n=new IntersectionObserver(e=>{!e[0]?.isIntersecting||be||je()},{root:t,threshold:0});return n.observe(e),()=>{n.disconnect()}},[je,De,be,K.length,G,I,J]),m(()=>{let e=Se.current;if(!e||t!==`waterfall`)return;let n=()=>{let t=e.clientWidth||0;he(t>0?(t-12)/2:0)};n();let r=new ResizeObserver(n);return r.observe(e),()=>{r.disconnect()}},[t]);let $=h(()=>t===`waterfall`?le(K,q,z):{left:[],right:[]},[z,q,K,t]);return K.length?y(n,{className:`w-full mt-[16px]`,children:[y(n,{className:`flex items-center justify-between px-[12px] mb-[8px]`,children:[v(r,{className:`flex-1 min-w-0 text-text1 text-[18px] leading-[26px] font-bold line-clamp-1`,children:Oe}),Ae&&v(r,{className:`text-text3 text-[12px] leading-[18px] cursor-pointer`,onClick:Me,children:`更多`})]}),t===`grid`&&v(n,{className:`w-full flex flex-wrap px-[12px]`,children:K.map((e,n)=>{let r=q[n],i=w(e),a=Y&&(i?.showPlayCount??!0),o=i?.playCountDelta??X,s=i?.showEpisode??Z,c=n%W,l=W>1?{paddingLeft:12*c/W,paddingRight:12*(W-c-1)/W}:void 0;return v(`div`,{ref:n===J?H:void 0,className:`min-w-0`,style:{width:`${100/W}%`,...l||{}},children:v(x,{variant:t,item:e,icon:r,index:n,showPlayCount:a,playCountDelta:o,showEpisode:s,widthStyle:{width:`100%`}})},P(e,n))})}),t===`waterfall`&&y(n,{ref:Se,className:`w-full px-[12px] flex gap-[12px]`,children:[v(n,{className:`flex-1 min-w-0 flex flex-col`,children:$.left.map(e=>{let n=w(e.item),r=Y&&(n?.showPlayCount??!0),i=n?.playCountDelta??X,a=n?.showEpisode??Z;return v(`div`,{ref:e.index===J?H:void 0,children:v(x,{variant:t,item:e.item,icon:e.icon,index:e.index,showPlayCount:r,playCountDelta:i,showEpisode:a,widthStyle:{width:`100%`}})},P(e.item,e.index))})}),v(n,{className:`flex-1 min-w-0 flex flex-col`,children:$.right.map(e=>{let n=w(e.item),r=Y&&(n?.showPlayCount??!0),i=n?.playCountDelta??X,a=n?.showEpisode??Z;return v(`div`,{ref:e.index===J?H:void 0,children:v(x,{variant:t,item:e.item,icon:e.icon,index:e.index,showPlayCount:r,playCountDelta:i,showEpisode:a,widthStyle:{width:`100%`}})},P(e.item,e.index))})})]})]}):null}function x({variant:e,item:t,icon:o,index:s,showPlayCount:c,playCountDelta:l,showEpisode:u,widthStyle:ee}){let te=se(t),d=t.name||t?.title||``,f=O(ce(t.static?.browse_cnt??0,l)),p=E(t,u),m=j(t,o,s);return v(a,{url:te,text:d,className:`w-full`,style:ee,topRightChild:m?v(S,{url:m}):void 0,bottomLeftChild:c?y(n,{className:`h-[20px] leading-[20px] text-[#fff] text-[12px] px-[4px] rounded-[4px] bg-[#333] ml-[4px] flex items-center`,children:[v(i,{className:`w-[16px] h-[auto] text-[#fff] mr-[4px]`}),f]}):null,bottomRightChild:u?p&&v(r,{className:`text-[#fff] text-[12px] leading-[18px] pr-2`,children:p}):null,textChild:v(n,{className:`py-[4px]`,children:v(r,{as:`h3`,className:`text-text1 text-[16px] leading-[24px] ${e===`waterfall`?`break-words line-clamp-3`:`line-clamp-1`}`,children:d})}),linkPath:C(t)})}function S({url:e}){return v(n,{className:`relative w-[40px] h-[20px]`,children:v(t,{src:e,alt:`corner`,width:40,height:20,className:`w-full h-auto`})})}function C(e){let t=encodeURIComponent(e.name||``);return`/video/${e.id||``}/${t}/episode/1`}function ae(e){return e?e.trim():``}function oe(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<=0?t:Math.min(n,Math.floor(r))}function w(e){return e[d]}function se(e){return e.img_y||e.img_x||e?.coverUrl}function T(e){return typeof e==`string`?e:``}function E(e,t){if(!t)return``;if(D(e)){let t=Number(e.episode_cnt||e.links?.length||0);return t>0?`全${t}集`:``}return A(Number(e.duration||0))}function D(e){if(Number(e.type??0)===2)return!0;let t=e.is_more_link;if(t===!0||t===`y`||t===`1`)return!0;let n=Number(e.episode_cnt||e.links?.length||0);return Number.isFinite(n)&&n>1}function O(e){return!Number.isFinite(e)||e<=0?`0`:e>=1e6?`${k(e/1e6)}M`:e>=1e3?`${k(e/1e3)}k`:Math.floor(e).toString()}function ce(e,t){let n=Number(e||0),r=Number(t||0);return!Number.isFinite(n)||!Number.isFinite(r)?0:Math.max(0,n+r)}function k(e){let t=e.toFixed(1);return t.endsWith(`.0`)?t.slice(0,-2):t}function A(e){if(!Number.isFinite(e)||e<=0)return``;let t=Math.floor(e),n=Math.floor(t/3600),r=Math.floor(t%3600/60),i=t%60,a=e=>String(e).padStart(2,`0`);return n>0?`${a(n)}:${a(r)}:${a(i)}`:`${a(r)}:${a(i)}`}function j(e,t,n){return T(e.badge_url)||T(t?.material_url)||``}function le(e,t,n){let r=[],i=[],a=n<=0,o=0,s=0;return e.forEach((e,c)=>{let l=t[c];if(a){c%2==0?r.push({item:e,icon:l,index:c}):i.push({item:e,icon:l,index:c});return}let u=M(n,e);o<=s?(r.push({item:e,icon:l,index:c}),o+=u):(i.push({item:e,icon:l,index:c}),s+=u)}),{left:r,right:i}}function M(e,t){if(!e)return 0;let n=e*1.3461538461538463,r=E(t,!0)?18:0;return n+20+r+6+16}function N(e){if(typeof e==`string`)return e.trim()||void 0}function P(e,t){return e.id?`home-recommend-${e.id}`:`home-recommend-${t}`}export{b as default};
2
+ "use client";import{pContentGetPopularVideoList as e}from"../../../../../service/generated/client.mjs";import{Image as t}from"../../../../ui/image.mjs";import{Box as n}from"../../../../ui/box.mjs";import{Text as r}from"../../../../ui/text.mjs";import i from"../../../../../assets/icons/detail/video_flag.mjs";import{MiniThreeCard as a}from"../../../../common/cards/mini-three-card.mjs";import{HOME_RECOMMEND_DEFAULT_GRID_COLUMNS as o,HOME_RECOMMEND_DEFAULT_GRID_ROWS as s,HOME_RECOMMEND_DEFAULT_PLAY_COUNT_DELTA as c,HOME_RECOMMEND_DEFAULT_SHOW_EPISODE as l,HOME_RECOMMEND_DEFAULT_SHOW_PLAY_COUNT as u,HOME_RECOMMEND_MAX_GRID_COLUMNS as ee,HOME_RECOMMEND_MAX_GRID_ROWS as te}from"./home-recommend-default-config.mjs";import{HOME_RECOMMEND_CARD_CONFIG_FIELD as d}from"./home-recommend-preview.mjs";import{useCallback as f,useEffect as p,useLayoutEffect as m,useMemo as h,useRef as g,useState as _}from"react";import{jsx as v,jsxs as y}from"react/jsx-runtime";import{useRouter as ne}from"next/navigation";const re={},ie={list:[]};function b({variant:t,props:i=re,data:a=ie,mode:d=`renderer`}){let b=d===`editor`,S=ne(),{list:C,title:se,cursor:T}=a,E=h(()=>Array.isArray(C)?C:[],[C]),D=h(()=>Array.isArray(a.icons)?a.icons:[],[a.icons]),{rows:O,columns:ce,moreLink:k,title:A,smartTagEnabled:j,smartTagIds:M,catId:ue,showPlayCount:de,playCountDelta:fe,showEpisode:pe,enableInfinite:me}=i,F=me===!0&&j===!0,I=!b&&F&&E.length>=16,L=h(()=>Array.isArray(M)?M.map(e=>String(e||``).trim()).filter(Boolean):[],[M]),R=typeof ue==`string`?ue.trim():``,[z,he]=_(0),[ge,_e]=_(E),[ve,ye]=_(D),[B,V]=_(()=>N(T)),[be,xe]=_(!1),Se=g(null),H=g(null),U=g(!1);p(()=>{_e(E),ye(D),V(N(T))},[T,D,E]);let Ce=oe(O,s,te),W=oe(ce,o,ee),G=F?16:t===`grid`?Ce*W:16,we=Ce*W,Te=b&&t===`grid`,K=Te?ge.slice(0,we):ge,q=Te?ve.slice(0,we):ve,Ee=t===`waterfall`?6:4,J=K.length>0?Math.max(0,K.length-Ee):-1,De=!!B,Oe=A&&A.trim()||se||``,Y=de??u,X=Number(fe??c),Z=pe??l,ke=!F,Q=ke?ae(k):``,Ae=ke&&!!Q,je=f(async()=>{let t=B;if(!(!I||!t||U.current)){U.current=!0,xe(!0);try{let n=await e({cursor:t,page_size:G,tags:L.length>0?L:void 0,cat_id:R||void 0}),r=Array.isArray(n.data?.videos)?n.data.videos:[],i=N(n.data?.cursor);r.length>0&&_e(e=>e.concat(r)),V(e=>{if(!(!r.length||!i))return i===e?void 0:i})}catch(e){console.error(`推荐位加载下一页失败(client)`,e),V(void 0)}finally{U.current=!1,xe(!1)}}},[B,R,G,L,I]),Me=f(()=>{Q&&(Q.startsWith(`http`)?window.location.href=Q:S.push(Q))},[Q,S]);p(()=>{let e=H.current;if(!e||!I||!De)return;let t=new IntersectionObserver(e=>{!e[0]?.isIntersecting||be||je()},{root:null,threshold:0});return t.observe(e),()=>{t.disconnect()}},[je,De,be,K.length,G,I,J]),m(()=>{let e=Se.current;if(!e||t!==`waterfall`)return;let n=()=>{let t=e.clientWidth||0;he(t>0?(t-12)/2:0)};n();let r=new ResizeObserver(n);return r.observe(e),()=>{r.disconnect()}},[t]);let $=h(()=>t===`waterfall`?le(K,q,z):{left:[],right:[]},[z,q,K,t]);return K.length?y(n,{className:`w-full mt-[16px]`,children:[y(n,{className:`flex items-center justify-between px-[12px] mb-[8px]`,children:[v(r,{className:`flex-1 min-w-0 text-text1 text-[18px] leading-[26px] font-bold line-clamp-1`,children:Oe}),Ae&&v(r,{className:`text-text3 text-[12px] leading-[18px] cursor-pointer`,onClick:Me,children:`更多`})]}),t===`grid`&&v(n,{className:`w-full flex flex-wrap px-[12px]`,children:K.map((e,n)=>{let r=q[n],i=w(e),a=Y&&(i?.showPlayCount??!0),o=i?.playCountDelta??X,s=i?.showEpisode??Z,c=n%W,l=W>1?{paddingLeft:12*c/W,paddingRight:12*(W-c-1)/W}:void 0;return v(`div`,{ref:n===J?H:void 0,className:`min-w-0`,style:{width:`${100/W}%`,...l||{}},children:v(x,{variant:t,item:e,icon:r,index:n,showPlayCount:a,playCountDelta:o,showEpisode:s,widthStyle:{width:`100%`}})},P(e,n))})}),t===`waterfall`&&y(n,{ref:Se,className:`w-full px-[12px] flex gap-[12px]`,children:[v(n,{className:`flex-1 min-w-0 flex flex-col`,children:$.left.map(e=>{let n=w(e.item),r=Y&&(n?.showPlayCount??!0),i=n?.playCountDelta??X,a=n?.showEpisode??Z;return v(`div`,{ref:e.index===J?H:void 0,children:v(x,{variant:t,item:e.item,icon:e.icon,index:e.index,showPlayCount:r,playCountDelta:i,showEpisode:a,widthStyle:{width:`100%`}})},P(e.item,e.index))})}),v(n,{className:`flex-1 min-w-0 flex flex-col`,children:$.right.map(e=>{let n=w(e.item),r=Y&&(n?.showPlayCount??!0),i=n?.playCountDelta??X,a=n?.showEpisode??Z;return v(`div`,{ref:e.index===J?H:void 0,children:v(x,{variant:t,item:e.item,icon:e.icon,index:e.index,showPlayCount:r,playCountDelta:i,showEpisode:a,widthStyle:{width:`100%`}})},P(e.item,e.index))})})]})]}):null}function x({variant:e,item:t,icon:o,index:s,showPlayCount:c,playCountDelta:l,showEpisode:u,widthStyle:ee}){let te=se(t),d=t.name||t?.title||``,f=O(ce(t.static?.browse_cnt??0,l)),p=E(t,u),m=j(t,o,s);return v(a,{url:te,text:d,className:`w-full`,style:ee,topRightChild:m?v(S,{url:m}):void 0,bottomLeftChild:c?y(n,{className:`h-[20px] leading-[20px] text-[#fff] text-[12px] px-[4px] rounded-[4px] bg-[#333] ml-[4px] flex items-center`,children:[v(i,{className:`w-[16px] h-[auto] text-[#fff] mr-[4px]`}),f]}):null,bottomRightChild:u?p&&v(r,{className:`text-[#fff] text-[12px] leading-[18px] pr-2`,children:p}):null,textChild:v(n,{className:`py-[4px]`,children:v(r,{as:`h3`,className:`text-text1 text-[16px] leading-[24px] ${e===`waterfall`?`break-words line-clamp-3`:`line-clamp-1`}`,children:d})}),linkPath:C(t)})}function S({url:e}){return v(n,{className:`relative w-[40px] h-[20px]`,children:v(t,{src:e,alt:`corner`,width:40,height:20,className:`w-full h-auto`})})}function C(e){let t=encodeURIComponent(e.name||``);return`/video/${e.id||``}/${t}/episode/1`}function ae(e){return e?e.trim():``}function oe(e,t,n){let r=Number(e);return!Number.isFinite(r)||r<=0?t:Math.min(n,Math.floor(r))}function w(e){return e[d]}function se(e){return e.img_y||e.img_x||e?.coverUrl}function T(e){return typeof e==`string`?e:``}function E(e,t){if(!t)return``;if(D(e)){let t=Number(e.episode_cnt||e.links?.length||0);return t>0?`全${t}集`:``}return A(Number(e.duration||0))}function D(e){if(Number(e.type??0)===2)return!0;let t=e.is_more_link;if(t===!0||t===`y`||t===`1`)return!0;let n=Number(e.episode_cnt||e.links?.length||0);return Number.isFinite(n)&&n>1}function O(e){return!Number.isFinite(e)||e<=0?`0`:e>=1e6?`${k(e/1e6)}M`:e>=1e3?`${k(e/1e3)}k`:Math.floor(e).toString()}function ce(e,t){let n=Number(e||0),r=Number(t||0);return!Number.isFinite(n)||!Number.isFinite(r)?0:Math.max(0,n+r)}function k(e){let t=e.toFixed(1);return t.endsWith(`.0`)?t.slice(0,-2):t}function A(e){if(!Number.isFinite(e)||e<=0)return``;let t=Math.floor(e),n=Math.floor(t/3600),r=Math.floor(t%3600/60),i=t%60,a=e=>String(e).padStart(2,`0`);return n>0?`${a(n)}:${a(r)}:${a(i)}`:`${a(r)}:${a(i)}`}function j(e,t,n){return T(e.badge_url)||T(t?.material_url)||``}function le(e,t,n){let r=[],i=[],a=n<=0,o=0,s=0;return e.forEach((e,c)=>{let l=t[c];if(a){c%2==0?r.push({item:e,icon:l,index:c}):i.push({item:e,icon:l,index:c});return}let u=M(n,e);o<=s?(r.push({item:e,icon:l,index:c}),o+=u):(i.push({item:e,icon:l,index:c}),s+=u)}),{left:r,right:i}}function M(e,t){if(!e)return 0;let n=e*1.3461538461538463,r=E(t,!0)?18:0;return n+20+r+6+16}function N(e){if(typeof e==`string`)return e.trim()||void 0}function P(e,t){return e.id?`home-recommend-${e.id}`:`home-recommend-${t}`}export{b as default};
@@ -1,2 +1,2 @@
1
1
 
2
- "use client";import{gContentGetAppChannelBlockData as e}from"../../../../../../../service/generated/client.mjs";import{Box as t}from"../../../../../../ui/box.mjs";import{InfiniteScroll as n}from"../../../../../../ui/infinite-scroll.mjs";import{usePaginated as r}from"../../../../../../../hooks/query/use-query.mjs";import{UserActivityListItem as i}from"../../../../../../common/list/user-activity-list/user-activity-list-item.mjs";import{useLayoutEffect as a,useMemo as o,useRef as s,useState as c}from"react";import{jsx as l}from"react/jsx-runtime";const u={},d={list:[],cursor:``,block_id:``};function f({props:f=u,styles:p,data:m=d}){let{list:h,cursor:g,block_id:_}=m,v=h&&h.length>0,y=o(()=>{if(!v)return;let e=g&&g.trim()?g:void 0;return{pages:[{data:{block:{data:{contents:h,cursor:g||``}}}}],pageParams:[e]}},[v,h,g]),{data:b,fetchNextPage:x,hasNextPage:S}=r({key:[`basic-post-card`,_],queryFn:t=>e({block_id:_,cursor:t||void 0,page_size:12}),getNextPageParam:e=>{let t=e.data?.block?.data?.cursor;return t&&t.trim()?t:void 0},options:{enabled:!!_,initialData:y,refetchOnMount:!1,refetchOnWindowFocus:!1,refetchOnReconnect:!1},initialPageParam:v&&g&&g.trim()?g:void 0}),C=s(h),[w,T]=c(!1);a(()=>{T(!0)},[]);let[E,D]=c({}),O=o(()=>{let e=[...C.current];if(!w)return e.map(e=>{let t=e.content_id;if(!t)return e;let n=E[t];return n===void 0?e:{...e,is_collected:n}});let t=[...e],n=b?.pages;if(n&&n.length>1)for(let e=1;e<n.length;e++){let r=n[e].data?.block?.data?.contents||[];t.push(...r)}return t.map(e=>{let t=e.content_id;if(!t)return e;let n=E[t];return n===void 0?e:{...e,isCollected:n,is_collected:n}})},[b?.pages,w,E]);return l(t,{children:l(n,{loadMore:()=>x().then(()=>{}),hasMore:S===!0||S===void 0&&!!g&&h.length>0,initialLoad:!0,rootId:`app-scroll-container`,threshold:1e3,children:O.map(e=>l(i,{is_collected:e.is_collected,onCollectChange:(e,t)=>{e&&D(n=>({...n,[e]:t}))},...e},e.content_id||`item-${e.author?.user_id}-${e.create_time}`))})})}export{f as default};
2
+ "use client";import{gContentGetAppChannelBlockData as e}from"../../../../../../../service/generated/client.mjs";import{Box as t}from"../../../../../../ui/box.mjs";import{InfiniteScroll as n}from"../../../../../../ui/infinite-scroll.mjs";import{usePaginated as r}from"../../../../../../../hooks/query/use-query.mjs";import{UserActivityListItem as i}from"../../../../../../common/list/user-activity-list/user-activity-list-item.mjs";import{useLayoutEffect as a,useMemo as o,useRef as s,useState as c}from"react";import{jsx as l}from"react/jsx-runtime";const u={},d={list:[],cursor:``,block_id:``};function f({props:f=u,styles:p,data:m=d}){let{list:h,cursor:g,block_id:_}=m,v=h&&h.length>0,y=o(()=>{if(!v)return;let e=g&&g.trim()?g:void 0;return{pages:[{data:{block:{data:{contents:h,cursor:g||``}}}}],pageParams:[e]}},[v,h,g]),{data:b,fetchNextPage:x,hasNextPage:S}=r({key:[`basic-post-card`,_],queryFn:t=>e({block_id:_,cursor:t||void 0,page_size:12}),getNextPageParam:e=>{let t=e.data?.block?.data?.cursor;return t&&t.trim()?t:void 0},options:{enabled:!!_,initialData:y,refetchOnMount:!1,refetchOnWindowFocus:!1,refetchOnReconnect:!1},initialPageParam:v&&g&&g.trim()?g:void 0}),C=s(h),[w,T]=c(!1);a(()=>{T(!0)},[]);let[E,D]=c({}),O=o(()=>{let e=[...C.current];if(!w)return e.map(e=>{let t=e.content_id;if(!t)return e;let n=E[t];return n===void 0?e:{...e,is_collected:n}});let t=[...e],n=b?.pages;if(n&&n.length>1)for(let e=1;e<n.length;e++){let r=n[e].data?.block?.data?.contents||[];t.push(...r)}return t.map(e=>{let t=e.content_id;if(!t)return e;let n=E[t];return n===void 0?e:{...e,isCollected:n,is_collected:n}})},[b?.pages,w,E]);return l(t,{children:l(n,{loadMore:()=>x().then(()=>{}),hasMore:S===!0||S===void 0&&!!g&&h.length>0,initialLoad:!0,threshold:1e3,children:O.map(e=>l(i,{is_collected:e.is_collected,onCollectChange:(e,t)=>{e&&D(n=>({...n,[e]:t}))},...e},e.content_id||`item-${e.author?.user_id}-${e.create_time}`))})})}export{f as default};
@@ -1,2 +1,2 @@
1
1
 
2
- "use client";import{cn as e}from"../../../../../utils/cn.mjs";import{Button as t}from"../../../../ui/button.mjs";import{useUserStore as n}from"../../../../../store/modules/user-store.mjs";import{Box as r}from"../../../../ui/box.mjs";import{EmptyState as i}from"../../../../ui/empty.mjs";import{Tabs as a,TabsList as o,TabsTrigger as s}from"../../../../ui/tabs.mjs";import{Text as c}from"../../../../ui/text.mjs";import{useLoginModalStore as l}from"../../../../../store/modules/login-modal-store.mjs";import{profileMainMineTabOptions as u,profileMainProfileTabOptions as d}from"./variants.mjs";import{useEffect as f,useMemo as p,useRef as m}from"react";import{jsx as h,jsxs as g}from"react/jsx-runtime";import{useTranslations as _}from"next-intl";import{parseAsString as v,useQueryState as y}from"nuqs";function b(e){return e?[...u]:[...d]}function x(e,t){let n=b(t);return n.includes(e)?e:n[0]}function S(e){let t=window.getComputedStyle(e);return/auto|scroll|overlay/.test(t.overflowY)}function C(e){if(typeof window>`u`)return null;let t=e?.parentElement??null;for(;t;){if(S(t))return t;t=t.parentElement}let n=document.getElementById(`app-scroll-container`);return n instanceof HTMLElement?n:null}function w(e){return typeof window>`u`?0:e?e.scrollTop:window.scrollY||window.pageYOffset||0}function T(e,t){if(!(typeof window>`u`)){if(e){e.scrollTo({top:t});return}window.scrollTo({top:t,left:0})}}function E({profileId:u,currentUserId:d,isMine:S,renderMode:E,className:D,children:O}){let k=_(`components.pages.profile`),A=m(null),j=l(e=>e.open),M=n(e=>e.userId),N=b(S),[P,F]=y(`tab`,v.withDefault(`collect`)),I=p(()=>x(P,S),[S,P]),L=S?d||M||``:u,R=p(()=>`profile-scroll:${L||(S?`mine`:`guest`)}:${I}`,[S,I,L]);f(()=>{P!==I&&F(I,{shallow:!1})},[I,F,P]),f(()=>{if(typeof window>`u`)return;let e=C(A.current),t=window.sessionStorage.getItem(R),n=t?Number(t):0;Number.isFinite(n)&&n>0&&requestAnimationFrame(()=>{T(e,n)});let r=!1,i=()=>{r||(r=!0,requestAnimationFrame(()=>{window.sessionStorage.setItem(R,String(w(e))),r=!1}))};return e?(e.addEventListener(`scroll`,i,{passive:!0}),()=>{e.removeEventListener(`scroll`,i)}):(window.addEventListener(`scroll`,i,{passive:!0}),()=>{window.removeEventListener(`scroll`,i)})},[R]);let z={collect:k(`client.collect`),like:k(`client.like`),history:k(`client.history`)},B=O;return E===`login-required`?B=g(r,{className:`flex gap-4 flex-col items-center justify-center py-10 text-center`,children:[h(c,{className:`text-base text-text1`,children:k(`client.loginToView`)}),h(t,{size:`lg`,variant:`secondary`,onClick:j,children:k(`client.loginNow`)})]}):E===`privacy-collect`?B=h(i,{type:`no-content`,text:k(`client.collectionNotPublic`)}):E===`privacy-like`&&(B=h(i,{type:`no-content`,text:k(`client.likeNotPublic`)})),g(r,{ref:A,className:e(D),children:[h(r,{className:`border-b bg-bg1 sticky z-10 top-0 border-text1/5 h-10`,children:h(a,{value:I,onValueChange:e=>{F(e,{shallow:!1})},className:`w-fit`,children:h(o,{variant:`default`,className:`inline-flex h-9`,children:N.map(e=>h(s,{value:e,className:`p-2.5 !text-base !w-fit data-active:!text-lg data-active:font-bold`,children:z[e]},e))})})}),h(r,{className:`p-2`,children:B})]})}export{E as UserProfileMainShell};
2
+ "use client";import{cn as e}from"../../../../../utils/cn.mjs";import{Button as t}from"../../../../ui/button.mjs";import{useUserStore as n}from"../../../../../store/modules/user-store.mjs";import{Box as r}from"../../../../ui/box.mjs";import{EmptyState as i}from"../../../../ui/empty.mjs";import{Tabs as a,TabsList as o,TabsTrigger as s}from"../../../../ui/tabs.mjs";import{Text as c}from"../../../../ui/text.mjs";import{useLoginModalStore as l}from"../../../../../store/modules/login-modal-store.mjs";import{profileMainMineTabOptions as u,profileMainProfileTabOptions as d}from"./variants.mjs";import{useEffect as f,useMemo as p,useRef as m}from"react";import{jsx as h,jsxs as g}from"react/jsx-runtime";import{useTranslations as _}from"next-intl";import{parseAsString as v,useQueryState as y}from"nuqs";function b(e){return e?[...u]:[...d]}function x(e,t){let n=b(t);return n.includes(e)?e:n[0]}function S(e){let t=window.getComputedStyle(e);return/auto|scroll|overlay/.test(t.overflowY)}function C(e){if(typeof window>`u`)return null;let t=e?.parentElement??null;for(;t;){if(S(t))return t;t=t.parentElement}return null}function w(e){return typeof window>`u`?0:e?e.scrollTop:window.scrollY||window.pageYOffset||0}function T(e,t){if(!(typeof window>`u`)){if(e){e.scrollTo({top:t});return}window.scrollTo({top:t,left:0})}}function E({profileId:u,currentUserId:d,isMine:S,renderMode:E,className:D,children:O}){let k=_(`components.pages.profile`),A=m(null),j=l(e=>e.open),M=n(e=>e.userId),N=b(S),[P,F]=y(`tab`,v.withDefault(`collect`)),I=p(()=>x(P,S),[S,P]),L=S?d||M||``:u,R=p(()=>`profile-scroll:${L||(S?`mine`:`guest`)}:${I}`,[S,I,L]);f(()=>{P!==I&&F(I,{shallow:!1})},[I,F,P]),f(()=>{if(typeof window>`u`)return;let e=C(A.current),t=window.sessionStorage.getItem(R),n=t?Number(t):0;Number.isFinite(n)&&n>0&&requestAnimationFrame(()=>{T(e,n)});let r=!1,i=()=>{r||(r=!0,requestAnimationFrame(()=>{window.sessionStorage.setItem(R,String(w(e))),r=!1}))};return e?(e.addEventListener(`scroll`,i,{passive:!0}),()=>{e.removeEventListener(`scroll`,i)}):(window.addEventListener(`scroll`,i,{passive:!0}),()=>{window.removeEventListener(`scroll`,i)})},[R]);let z={collect:k(`client.collect`),like:k(`client.like`),history:k(`client.history`)},B=O;return E===`login-required`?B=g(r,{className:`flex gap-4 flex-col items-center justify-center py-10 text-center`,children:[h(c,{className:`text-base text-text1`,children:k(`client.loginToView`)}),h(t,{size:`lg`,variant:`secondary`,onClick:j,children:k(`client.loginNow`)})]}):E===`privacy-collect`?B=h(i,{type:`no-content`,text:k(`client.collectionNotPublic`)}):E===`privacy-like`&&(B=h(i,{type:`no-content`,text:k(`client.likeNotPublic`)})),g(r,{ref:A,className:e(D),children:[h(r,{className:`border-b bg-bg1 sticky z-10 top-0 border-text1/5 h-10`,children:h(a,{value:I,onValueChange:e=>{F(e,{shallow:!1})},className:`w-fit`,children:h(o,{variant:`default`,className:`inline-flex h-9`,children:N.map(e=>h(s,{value:e,className:`p-2.5 !text-base !w-fit data-active:!text-lg data-active:font-bold`,children:z[e]},e))})})}),h(r,{className:`p-2`,children:B})]})}export{E as UserProfileMainShell};
@@ -1,2 +1,2 @@
1
1
 
2
- "use client";import{gContentGetAppChannelBlock as e,gContentGetAppChannelBlockData as t,gContentListVisibleFloatingBall as n}from"../../../service/generated/client.mjs";import{Image as r}from"../../ui/image.mjs";import{Box as i}from"../../ui/box.mjs";import{useRequest as a}from"../../../hooks/query/use-query.mjs";import{useRequireLogin as o}from"../../../hooks/use-auth.mjs";import{queryKey as s}from"../../../constants/query-key.mjs";import{useKeepAliveState as c}from"../../common/keep-scroll-position/index.mjs";import{extractDataByComponentType as l}from"../../biz/utils/transformers/data.mjs";import{useEffect as u,useLayoutEffect as d,useMemo as f,useRef as p}from"react";import{Fragment as m,jsx as h,jsxs as g}from"react/jsx-runtime";import{useRouter as _}from"next/navigation";const v=`app-scroll-container`;function y(e){return typeof document>`u`?null:document.getElementById(e)}function b(){if(typeof document>`u`)return 0;let e=document.querySelector(`[data-top-nav-root="true"]`);return e&&e.getBoundingClientRect().height||0}function x({currentChannelId:x,isPGC:S}){let C=_(),w=o(),{data:T}=a(s.GET_CONTENT_LIST(x),()=>e({channel_id:x})),{data:E}=a([s.FLOATING_BALL],()=>n()),D=f(()=>T?.data?.blocks||[],[T?.data?.blocks]),O=f(()=>{let e=D.findIndex(e=>e.type===4),t=e>=0?D[e]:null;return{index:e,block:t,blockId:t?.id||``}},[D]),{data:k}=a([s.GUESS_YOU_LIKE_USER_DATA,O.blockId],()=>t({block_id:O.blockId}),{enabled:!!O.blockId}),A=f(()=>{if(O.index<0)return D;let e=k?.data.block?.data?.users||[];return D.map((t,n)=>n===O.index?{...t,data:{...t.data||{},users:e}}:t)},[D,k,O.index]),[j,M]=c(`home-scroll-top-${x}`,0),N=p(null),P=p({restored:!1,lastScrollTop:j,key:x,rafId:null}),F=p(M),I=p(j);u(()=>{F.current=M},[M]),u(()=>{I.current=j},[j]),u(()=>{P.current.key!==x&&(P.current.restored=!1,P.current.lastScrollTop=j,I.current=j,P.current.key=x)},[x,j]);let L=A.length>0;d(()=>{if(!L)return;N.current=y(v);let e=N.current;if(!e||P.current.restored||j<=0)return;let t=b(),n=Math.max(0,j+t);window.requestAnimationFrame(()=>{e.scrollTop=n,P.current.restored=!0,P.current.lastScrollTop=n})},[L,x,j]),u(()=>{N.current=y(v);let e=N.current;if(!e)return;function t(){P.current.restored=!0,P.current.rafId!==null&&window.cancelAnimationFrame(P.current.rafId),P.current.rafId=window.requestAnimationFrame(()=>{P.current.rafId=null;let e=N.current;if(!e)return;let t=e.scrollTop||0,n=b(),r=Math.max(0,t-n);Math.abs(t-P.current.lastScrollTop)>1&&(P.current.lastScrollTop=t,F.current(r))})}e.addEventListener(`scroll`,t,{passive:!0});let n=P.current.rafId;return()=>{n!==null&&window.cancelAnimationFrame(n),e.removeEventListener(`scroll`,t)}},[x]);let R=f(()=>l(A,S),[A,S]),z=f(()=>E?.data?.ball_infos?.find(e=>e.channel_id===x),[E?.data?.ball_infos,x]);return g(m,{children:[z&&h(i,{className:`fixed right-[20px] bottom-[80px] w-[52px] h-[52px] z-30 rounded-[16px] cursor-move select-none bg-[#FD4C5E33] overflow-hidden`,onClick:()=>{w(()=>{z?.jump_url&&C.push(z.jump_url)})},children:h(r,{src:z.material_url||``,alt:z.name||``,fill:!0,priority:!0,fetchPriority:`high`,sizes:`52px`,unoptimized:!0})}),R.map(e=>{let t=e.component,n=e.data.block_id||`block-${e.data.title||``}`;return h(t,{data:e.data},n)})]})}export{x as default};
2
+ "use client";import{gContentGetAppChannelBlock as e,gContentGetAppChannelBlockData as t,gContentListVisibleFloatingBall as n}from"../../../service/generated/client.mjs";import{Image as r}from"../../ui/image.mjs";import{Box as i}from"../../ui/box.mjs";import{useRequest as a}from"../../../hooks/query/use-query.mjs";import{useRequireLogin as o}from"../../../hooks/use-auth.mjs";import{queryKey as s}from"../../../constants/query-key.mjs";import{useKeepAliveState as c}from"../../common/keep-scroll-position/index.mjs";import{extractDataByComponentType as l}from"../../biz/utils/transformers/data.mjs";import{useEffect as u,useLayoutEffect as d,useMemo as f,useRef as p}from"react";import{Fragment as m,jsx as h,jsxs as g}from"react/jsx-runtime";import{useRouter as _}from"next/navigation";function v(){if(typeof document>`u`)return 0;let e=document.querySelector(`[data-top-nav-root="true"]`);return e&&e.getBoundingClientRect().height||0}function y({currentChannelId:y,isPGC:b}){let x=_(),S=o(),{data:C}=a(s.GET_CONTENT_LIST(y),()=>e({channel_id:y})),{data:w}=a([s.FLOATING_BALL],()=>n()),T=f(()=>C?.data?.blocks||[],[C?.data?.blocks]),E=f(()=>{let e=T.findIndex(e=>e.type===4),t=e>=0?T[e]:null;return{index:e,block:t,blockId:t?.id||``}},[T]),{data:D}=a([s.GUESS_YOU_LIKE_USER_DATA,E.blockId],()=>t({block_id:E.blockId}),{enabled:!!E.blockId}),O=f(()=>{if(E.index<0)return T;let e=D?.data.block?.data?.users||[];return T.map((t,n)=>n===E.index?{...t,data:{...t.data||{},users:e}}:t)},[T,D,E.index]),[k,A]=c(`home-scroll-top-${y}`,0),j=p({restored:!1,lastScrollTop:k,key:y,rafId:null}),M=p(A),N=p(k);u(()=>{M.current=A},[A]),u(()=>{N.current=k},[k]),u(()=>{j.current.key!==y&&(j.current.restored=!1,j.current.lastScrollTop=k,N.current=k,j.current.key=y)},[y,k]);let P=O.length>0;d(()=>{if(!P||j.current.restored||k<=0)return;let e=v(),t=Math.max(0,k+e);window.requestAnimationFrame(()=>{window.scrollTo({top:t}),j.current.restored=!0,j.current.lastScrollTop=t})},[P,y,k]),u(()=>{let e=j.current;function t(){e.restored=!0,e.rafId!==null&&window.cancelAnimationFrame(e.rafId),e.rafId=window.requestAnimationFrame(()=>{e.rafId=null;let t=window.scrollY??document.documentElement.scrollTop??0,n=v(),r=Math.max(0,t-n);Math.abs(t-e.lastScrollTop)>1&&(e.lastScrollTop=t,M.current(r))})}return window.addEventListener(`scroll`,t,{passive:!0}),()=>{e.rafId!==null&&(window.cancelAnimationFrame(e.rafId),e.rafId=null),window.removeEventListener(`scroll`,t)}},[y]);let F=f(()=>l(O,b),[O,b]),I=f(()=>w?.data?.ball_infos?.find(e=>e.channel_id===y),[w?.data?.ball_infos,y]);return g(m,{children:[I&&h(i,{className:`fixed right-[20px] bottom-[80px] w-[52px] h-[52px] z-30 rounded-[16px] cursor-move select-none bg-[#FD4C5E33] overflow-hidden`,onClick:()=>{S(()=>{I?.jump_url&&x.push(I.jump_url)})},children:h(r,{src:I.material_url||``,alt:I.name||``,fill:!0,priority:!0,fetchPriority:`high`,sizes:`52px`,unoptimized:!0})}),F.map(e=>{let t=e.component,n=e.data.block_id||`block-${e.data.title||``}`;return h(t,{data:e.data},n)})]})}export{y as default};
@@ -6,7 +6,7 @@ import * as class_variance_authority_types0 from "class-variance-authority/types
6
6
  //#region components/ui/badge.d.ts
7
7
  /** badgeVariants 工具定义。 */
8
8
  declare const badgeVariants: (props?: ({
9
- variant?: "link" | "default" | "destructive" | "secondary" | "outline" | "ghost" | null | undefined;
9
+ variant?: "default" | "outline" | "destructive" | "link" | "secondary" | "ghost" | null | undefined;
10
10
  } & class_variance_authority_types0.ClassProp) | undefined) => string;
11
11
  /** Badge 组件。 */
12
12
  declare function Badge({
@@ -8,7 +8,7 @@ import * as class_variance_authority_types0 from "class-variance-authority/types
8
8
  //#region components/ui/button-group.d.ts
9
9
  /** buttonGroupVariants 工具定义。 */
10
10
  declare const buttonGroupVariants: (props?: ({
11
- orientation?: "horizontal" | "vertical" | null | undefined;
11
+ orientation?: "vertical" | "horizontal" | null | undefined;
12
12
  } & class_variance_authority_types0.ClassProp) | undefined) => string;
13
13
  /** ButtonGroupText 组件属性。 */
14
14
  type ButtonGroupTextProps = useRender.ComponentProps<'div'>;
@@ -7,8 +7,8 @@ import * as class_variance_authority_types0 from "class-variance-authority/types
7
7
  //#region components/ui/button.d.ts
8
8
  /** buttonVariants 工具定义。 */
9
9
  declare const buttonVariants: (props?: ({
10
- variant?: "link" | "default" | "destructive" | "secondary" | "outline" | "ghost" | null | undefined;
11
- size?: "default" | "icon" | "xs" | "sm" | "lg" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
10
+ variant?: "default" | "outline" | "destructive" | "link" | "secondary" | "ghost" | null | undefined;
11
+ size?: "default" | "sm" | "xs" | "icon" | "lg" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
12
12
  } & class_variance_authority_types0.ClassProp) | undefined) => string;
13
13
  /** Button 组件属性。 */
14
14
  type ButtonProps = Button.Props & VariantProps<typeof buttonVariants> & {
@@ -51,7 +51,7 @@ declare function FieldGroup({
51
51
  }: FieldGroupProps): react_jsx_runtime0.JSX.Element;
52
52
  /** fieldVariants 工具定义。 */
53
53
  declare const fieldVariants: (props?: ({
54
- orientation?: "horizontal" | "vertical" | "responsive" | null | undefined;
54
+ orientation?: "vertical" | "horizontal" | "responsive" | null | undefined;
55
55
  } & class_variance_authority_types0.ClassProp) | undefined) => string;
56
56
  /** Field 组件。 */
57
57
  declare function Field({
@@ -25,10 +25,6 @@ interface InfiniteScrollProps {
25
25
  className?: string;
26
26
  /** 是否禁用自动加载逻辑。 */
27
27
  disabled?: boolean;
28
- /**
29
- * 滚动容器引用(用于容器内滚动)
30
- */
31
- rootId?: string;
32
28
  /** 组件挂载时是否立即触发一次加载。 */
33
29
  initialLoad?: boolean;
34
30
  }
@@ -46,7 +42,6 @@ declare function InfiniteScroll({
46
42
  children,
47
43
  className,
48
44
  disabled,
49
- rootId,
50
45
  initialLoad
51
46
  }: InfiniteScrollProps): react_jsx_runtime0.JSX.Element;
52
47
  //#endregion
@@ -1,2 +1,2 @@
1
1
 
2
- "use client";import{useCallback as e,useEffect as t,useRef as n,useState as r}from"react";import{jsx as i,jsxs as a}from"react/jsx-runtime";import{useTranslations as o}from"next-intl";const s=()=>a(`span`,{className:`inline-flex gap-1 ml-1`,children:[i(`span`,{className:`w-1 h-1 bg-current rounded-full animate-pulse`}),i(`span`,{className:`w-1 h-1 bg-current rounded-full animate-pulse animation-delay-200`}),i(`span`,{className:`w-1 h-1 bg-current rounded-full animate-pulse animation-delay-400`})]});function c({loadMore:c,hasMore:l,threshold:u=0,loadingText:d,emptyText:f,noMoreText:p,failedText:m,children:h,className:g=``,disabled:_=!1,rootId:v=``,initialLoad:y=!1}){let b=o(`components.ui`),[x,S]=r(!1),[C,w]=r(!1),T=n(null),E=n(!1),D=n(!1),O=d??b(`infiniteScroll.loading`),k=m??b(`infiniteScroll.failed`),A=f??p??b(`infiniteScroll.noMore`),j=l!==!1,M=e(async(e,t)=>{if(!(_||E.current||!t&&!j)){E.current=!0,w(!0),e&&S(!1);try{await c(e)}catch{S(!0)}finally{E.current=!1}}},[j,_,c]),N=e(async(e=!1)=>{await M(e,!1)},[M]);t(()=>{!y||D.current||(D.current=!0,M(!1,!0))},[M,y]),t(()=>{let e=T.current;if(!e||_)return;let t=v?document.getElementById(v):document.body,n=new IntersectionObserver(e=>{let t=e[0];t&&t.isIntersecting&&!E.current&&j&&(S(!1),N(!1))},{root:t,rootMargin:`0px 0px ${u}px 0px`,threshold:0});return n.observe(e),()=>{n.disconnect()}},[_,N,j,v,u]);let P=()=>{S(!1),N(!0)};return _?i(`div`,{className:g,children:h}):a(`div`,{className:g,children:[h,a(`div`,{children:[i(`div`,{ref:T,className:`h-[10px]`,"aria-hidden":`true`}),j&&!x&&a(`div`,{className:`flex items-center justify-center text-[12px] h-8.5`,children:[i(`span`,{className:`text-text2 mr-2`,children:O}),i(s,{})]}),x&&i(`div`,{className:`text-center text-theme5 cursor-pointer`,onClick:P,children:k}),l===!1&&!x&&i(`div`,{className:`text-center text-text3 pt-1.5 pb-3 border-text1/5 border-b`,children:A})]})]})}export{c as InfiniteScroll};
2
+ "use client";import{useCallback as e,useEffect as t,useRef as n,useState as r}from"react";import{jsx as i,jsxs as a}from"react/jsx-runtime";import{useTranslations as o}from"next-intl";const s=()=>a(`span`,{className:`inline-flex gap-1 ml-1`,children:[i(`span`,{className:`w-1 h-1 bg-current rounded-full animate-pulse`}),i(`span`,{className:`w-1 h-1 bg-current rounded-full animate-pulse animation-delay-200`}),i(`span`,{className:`w-1 h-1 bg-current rounded-full animate-pulse animation-delay-400`})]});function c({loadMore:c,hasMore:l,threshold:u=0,loadingText:d,emptyText:f,noMoreText:p,failedText:m,children:h,className:g=``,disabled:_=!1,initialLoad:v=!1}){let y=o(`components.ui`),[b,x]=r(!1),[S,C]=r(!1),w=n(null),T=n(!1),E=n(!1),D=n(0),O=e(()=>window.scrollY??document.documentElement.scrollTop??0,[]),k=d??y(`infiniteScroll.loading`),A=m??y(`infiniteScroll.failed`),j=f??p??y(`infiniteScroll.noMore`),M=l!==!1,N=e(async(e,t)=>{if(!(_||T.current||!t&&!M)){T.current=!0,C(!0),e&&x(!1);try{await c(e)}catch{x(!0)}finally{T.current=!1}}},[M,_,c]),P=e(async(e=!1)=>{await N(e,!1)},[N]);t(()=>{!v||E.current||(E.current=!0,N(!1,!0))},[N,v]),t(()=>{let e=w.current;if(!e||_)return;let t=new IntersectionObserver(e=>{let t=e[0];if(t&&t.isIntersecting&&!T.current&&M){let e=O();if(e<=D.current)return;D.current=e,x(!1),P(!1)}},{root:null,rootMargin:`0px 0px ${u}px 0px`,threshold:0});return t.observe(e),()=>{t.disconnect()}},[_,P,M,O,u]);let F=()=>{x(!1),P(!0)};return _?i(`div`,{className:g,children:h}):a(`div`,{className:g,children:[h,a(`div`,{children:[i(`div`,{ref:w,className:`h-[10px]`,"aria-hidden":`true`}),M&&!b&&a(`div`,{className:`flex items-center justify-center text-[12px] h-8.5`,children:[i(`span`,{className:`text-text2 mr-2`,children:k}),i(s,{})]}),b&&i(`div`,{className:`text-center text-theme5 cursor-pointer`,onClick:F,children:A}),l===!1&&!b&&i(`div`,{className:`text-center text-text3 pt-1.5 pb-3 border-text1/5 border-b`,children:j})]})]})}export{c as InfiniteScroll};
@@ -58,7 +58,7 @@ declare function InputGroupAddon({
58
58
  }: InputGroupAddonProps): react_jsx_runtime0.JSX.Element;
59
59
  /** inputGroupButtonVariants 工具定义。 */
60
60
  declare const inputGroupButtonVariants: (props?: ({
61
- size?: "xs" | "sm" | "icon-xs" | "icon-sm" | null | undefined;
61
+ size?: "sm" | "xs" | "icon-xs" | "icon-sm" | null | undefined;
62
62
  } & class_variance_authority_types0.ClassProp) | undefined) => string;
63
63
  /** InputGroupButton 组件。 */
64
64
  declare function InputGroupButton({
@@ -70,7 +70,7 @@ declare function ItemSeparator({
70
70
  /** itemVariants 工具定义。 */
71
71
  declare const itemVariants: (props?: ({
72
72
  variant?: "default" | "outline" | "muted" | null | undefined;
73
- size?: "default" | "xs" | "sm" | null | undefined;
73
+ size?: "default" | "sm" | "xs" | null | undefined;
74
74
  } & class_variance_authority_types0.ClassProp) | undefined) => string;
75
75
  /** Item 组件。 */
76
76
  declare function Item({
@@ -1,2 +1,2 @@
1
1
 
2
- "use client";import{useEffect as e,useRef as t,useState as n}from"react";function r(r=`app-scroll-container`){let[i,a]=n(!1),o=t(null),s=t(null);return e(()=>{let e=o.current;if(!e)return;let t=document.getElementById(r),n=!t,i=()=>t?t.scrollTop:window.scrollY??document.documentElement.scrollTop??0;if(s.current===null){let n=e.getBoundingClientRect();if(t){let e=t.getBoundingClientRect();s.current=n.top-e.top+t.scrollTop}else s.current=n.top+i()}let c=()=>{let e=i()>=s.current;a(t=>t===e?t:e)};return n?window.addEventListener(`scroll`,c,{passive:!0}):t.addEventListener(`scroll`,c,{passive:!0}),c(),()=>{n?window.removeEventListener(`scroll`,c):t.removeEventListener(`scroll`,c)}},[r]),{isSticky:i,ref:o}}export{r as useSticky};
2
+ "use client";import{useEffect as e,useRef as t,useState as n}from"react";function r(r){let[i,a]=n(!1),o=t(null),s=t(null);return e(()=>{let e=o.current;if(!e)return;let t=r?document.getElementById(r):null,n=!t,i=()=>t?t.scrollTop:window.scrollY??document.documentElement.scrollTop??0;if(s.current===null){let n=e.getBoundingClientRect();if(t){let e=t.getBoundingClientRect();s.current=n.top-e.top+t.scrollTop}else s.current=n.top+i()}let c=()=>{let e=i()>=s.current;a(t=>t===e?t:e)};return n?window.addEventListener(`scroll`,c,{passive:!0}):t.addEventListener(`scroll`,c,{passive:!0}),c(),()=>{n?window.removeEventListener(`scroll`,c):t.removeEventListener(`scroll`,c)}},[r]),{isSticky:i,ref:o}}export{r as useSticky};
@@ -1,2 +1,2 @@
1
1
 
2
- function e(e){return`${(e/430*100).toFixed(3)}vw`}function t(e){if(e)return e.trim()||void 0}function n(){if(typeof document<`u`){let e=document.getElementById(`app-scroll-container`);e&&(e.style.overflow=`hidden`),document.body.style.overflow=`hidden`}}function r(){if(typeof document<`u`){let e=document.getElementById(`app-scroll-container`);e&&(e.style.overflow=``),document.body.style.overflow=``}}export{n as disablePageScroll,r as enablePageScroll,t as normalizeClassName,e as pxToVw};
2
+ function e(e){return`${(e/430*100).toFixed(3)}vw`}function t(e){if(e)return e.trim()||void 0}function n(){typeof document<`u`&&(document.body.style.overflow=`hidden`)}function r(){typeof document<`u`&&(document.body.style.overflow=``)}export{n as disablePageScroll,r as enablePageScroll,t as normalizeClassName,e as pxToVw};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@funhub/platform",
3
3
  "type": "module",
4
- "version": "0.1.108",
4
+ "version": "0.1.109",
5
5
  "private": false,
6
6
  "sideEffects": [
7
7
  "**/*.css"
@@ -62,12 +62,9 @@
62
62
  },
63
63
  "./theme.css": "./theme.css"
64
64
  },
65
- "bin": "./bin/index.mjs",
66
65
  "files": [
67
- "bin",
68
66
  "config",
69
67
  "dist",
70
- "templates",
71
68
  "theme.css"
72
69
  ],
73
70
  "scripts": {
@@ -92,8 +89,7 @@
92
89
  "i18n:build": "node scripts/i18n-flat-sync.mjs build",
93
90
  "i18n:watch": "node scripts/i18n-flat-sync.mjs watch",
94
91
  "i18n:check": "node scripts/i18n-flat-sync.mjs check",
95
- "pretest": "pnpm i18n:build",
96
- "init:local": "node ./bin/index.mjs"
92
+ "pretest": "pnpm i18n:build"
97
93
  },
98
94
  "peerDependencies": {
99
95
  "@tanstack/query-core": ">=5",
package/bin/index.mjs DELETED
@@ -1,323 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /* eslint-disable no-console */
4
- import { spawn } from 'node:child_process';
5
- import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
6
- import path from 'node:path';
7
- import process from 'node:process';
8
- import { fileURLToPath } from 'node:url';
9
- import { cac } from 'cac';
10
-
11
- const currentFilePath = fileURLToPath(import.meta.url);
12
- const packageRoot = path.resolve(path.dirname(currentFilePath), '..');
13
- const templatesRoot = path.join(packageRoot, 'templates');
14
- const platformPackageJson = JSON.parse(readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
15
-
16
- /**
17
- * 获取可用模板列表。
18
- * @returns {Array<{ name: string, title: string, description: string, directory: string }>}
19
- */
20
- function getAvailableTemplates() {
21
- if (!existsSync(templatesRoot)) {
22
- return [];
23
- }
24
-
25
- return readdirSync(templatesRoot)
26
- .map((entryName) => {
27
- const templateDir = path.join(templatesRoot, entryName);
28
- if (!statSync(templateDir).isDirectory()) {
29
- return null;
30
- }
31
-
32
- const metaFilePath = path.join(templateDir, '_meta.json');
33
- if (!existsSync(metaFilePath)) {
34
- return null;
35
- }
36
-
37
- const meta = JSON.parse(readFileSync(metaFilePath, 'utf8'));
38
-
39
- return {
40
- name: entryName,
41
- title: meta.title || entryName,
42
- description: meta.description || '',
43
- directory: templateDir,
44
- };
45
- })
46
- .filter(Boolean);
47
- }
48
-
49
- /**
50
- * 解析默认包管理器。
51
- * @returns {'pnpm' | 'npm' | 'yarn' | 'bun'}
52
- */
53
- function detectPackageManager() {
54
- const userAgent = process.env.npm_config_user_agent || '';
55
-
56
- if (userAgent.startsWith('yarn/')) {
57
- return 'yarn';
58
- }
59
-
60
- if (userAgent.startsWith('bun/')) {
61
- return 'bun';
62
- }
63
-
64
- if (userAgent.startsWith('npm/')) {
65
- return 'npm';
66
- }
67
-
68
- return 'pnpm';
69
- }
70
-
71
- /**
72
- * 根据当前平台返回命令名。
73
- * @param {'pnpm' | 'npm' | 'yarn' | 'bun' | 'git'} command
74
- * @returns {string}
75
- */
76
- function resolveCommand(command) {
77
- return process.platform === 'win32' ? `${command}.cmd` : command;
78
- }
79
-
80
- /**
81
- * 将目录名转换为 npm 包名。
82
- * @param {string} projectName
83
- * @returns {string}
84
- */
85
- function toPackageName(projectName) {
86
- return projectName
87
- .trim()
88
- .toLowerCase()
89
- .replace(/\s+/g, '-')
90
- .replace(/[^a-z0-9-_.~/@]/g, '-')
91
- .replace(/-+/g, '-');
92
- }
93
-
94
- /**
95
- * 校验项目名是否合法。
96
- * @param {string} projectName
97
- */
98
- function validateProjectName(projectName) {
99
- if (!projectName) {
100
- throw new Error('项目名不能为空。');
101
- }
102
-
103
- if (projectName === '.' || projectName === '..') {
104
- return;
105
- }
106
-
107
- if (!/^[\w.-]+$/.test(projectName)) {
108
- throw new Error('项目目录名仅支持字母、数字、点、中划线和下划线。');
109
- }
110
- }
111
-
112
- /**
113
- * 递归遍历目录文件。
114
- * @param {string} targetDir
115
- * @returns {string[]}
116
- */
117
- function collectFiles(targetDir) {
118
- const entries = readdirSync(targetDir, { withFileTypes: true });
119
- const filePaths = [];
120
-
121
- for (const entry of entries) {
122
- const entryPath = path.join(targetDir, entry.name);
123
- if (entry.isDirectory()) {
124
- filePaths.push(...collectFiles(entryPath));
125
- continue;
126
- }
127
- filePaths.push(entryPath);
128
- }
129
-
130
- return filePaths;
131
- }
132
-
133
- /**
134
- * 判断文件是否可按文本方式渲染。
135
- * @param {string} filePath
136
- * @returns {boolean}
137
- */
138
- function isRenderableTextFile(filePath) {
139
- return /\.(?:json|md|tsx?|jsx?|mjs|cjs|css|txt|ya?ml|gitignore)$/i.test(filePath);
140
- }
141
-
142
- /**
143
- * 渲染模板目录中的占位符文件。
144
- * @param {string} targetDir
145
- * @param {Record<string, string>} variables
146
- */
147
- function renderTemplateFiles(targetDir, variables) {
148
- const filePaths = collectFiles(targetDir);
149
-
150
- for (const filePath of filePaths) {
151
- const dirName = path.dirname(filePath);
152
- const baseName = path.basename(filePath);
153
-
154
- if (baseName === '__dot__gitignore') {
155
- renameSync(filePath, path.join(dirName, '.gitignore'));
156
- continue;
157
- }
158
-
159
- if (baseName.endsWith('.template')) {
160
- const nextFilePath = path.join(dirName, baseName.replace(/\.template$/, ''));
161
- renameSync(filePath, nextFilePath);
162
- renderSingleFile(nextFilePath, variables);
163
- continue;
164
- }
165
-
166
- renderSingleFile(filePath, variables);
167
- }
168
- }
169
-
170
- /**
171
- * 渲染单个文本文件。
172
- * @param {string} filePath
173
- * @param {Record<string, string>} variables
174
- */
175
- function renderSingleFile(filePath, variables) {
176
- if (!isRenderableTextFile(filePath)) {
177
- return;
178
- }
179
-
180
- let content = readFileSync(filePath, 'utf8');
181
- for (const [key, value] of Object.entries(variables)) {
182
- content = content.replaceAll(`{{${key}}}`, value);
183
- }
184
- writeFileSync(filePath, content);
185
- }
186
-
187
- /**
188
- * 执行子进程命令。
189
- * @param {string} command
190
- * @param {string[]} args
191
- * @param {string} cwd
192
- * @returns {Promise<void>}
193
- */
194
- function runCommand(command, args, cwd) {
195
- return new Promise((resolve, reject) => {
196
- const child = spawn(resolveCommand(command), args, {
197
- cwd,
198
- stdio: 'inherit',
199
- shell: false,
200
- });
201
-
202
- child.on('close', (code) => {
203
- if (code === 0) {
204
- resolve();
205
- return;
206
- }
207
-
208
- reject(new Error(`${command} ${args.join(' ')} 执行失败,退出码 ${code}`));
209
- });
210
- });
211
- }
212
-
213
- /**
214
- * 获取安装命令。
215
- * @param {'pnpm' | 'npm' | 'yarn' | 'bun'} packageManager
216
- * @returns {{ command: string, args: string[] }}
217
- */
218
- function getInstallCommand(packageManager) {
219
- switch (packageManager) {
220
- case 'npm':
221
- return { command: 'npm', args: ['install'] };
222
- case 'yarn':
223
- return { command: 'yarn', args: [] };
224
- case 'bun':
225
- return { command: 'bun', args: ['install'] };
226
- default:
227
- return { command: 'pnpm', args: ['install'] };
228
- }
229
- }
230
-
231
- /**
232
- * 初始化项目。
233
- * @param {string | undefined} inputName
234
- * @param {{ template?: string, pm?: 'pnpm' | 'npm' | 'yarn' | 'bun', install?: boolean, git?: boolean, force?: boolean }} options
235
- */
236
- async function initProject(inputName, options) {
237
- const templateName = options.template || 'base';
238
- const packageManager = options.pm || detectPackageManager();
239
- const availableTemplates = getAvailableTemplates();
240
- const matchedTemplate = availableTemplates.find(item => item.name === templateName);
241
-
242
- if (!matchedTemplate) {
243
- throw new Error(`未找到模板 ${templateName},可用模板:${availableTemplates.map(item => item.name).join(', ')}`);
244
- }
245
-
246
- const workingDir = process.cwd();
247
- const projectName = inputName || 'funhub-app';
248
- validateProjectName(projectName);
249
-
250
- const targetDir = path.resolve(workingDir, projectName);
251
- const targetExists = existsSync(targetDir);
252
- const targetBaseName = projectName === '.' ? path.basename(workingDir) : path.basename(targetDir);
253
- const packageName = toPackageName(targetBaseName);
254
-
255
- if (targetExists) {
256
- const targetEntries = readdirSync(targetDir);
257
- if (targetEntries.length > 0) {
258
- if (!options.force) {
259
- throw new Error(`目标目录 ${targetDir} 非空,请改用空目录或显式传入 --force。`);
260
- }
261
- rmSync(targetDir, { recursive: true, force: true });
262
- }
263
- }
264
-
265
- mkdirSync(targetDir, { recursive: true });
266
-
267
- const sourceTemplateDir = path.join(matchedTemplate.directory, 'template');
268
- cpSync(sourceTemplateDir, targetDir, { recursive: true });
269
-
270
- renderTemplateFiles(targetDir, {
271
- PROJECT_NAME: targetBaseName,
272
- PACKAGE_NAME: packageName,
273
- PLATFORM_VERSION: platformPackageJson.version,
274
- });
275
-
276
- console.log(`\n已创建 Funhub 项目:${targetBaseName}`);
277
- console.log(`模板:${matchedTemplate.title}`);
278
- console.log(`目录:${targetDir}`);
279
-
280
- if (options.install !== false) {
281
- const installCommand = getInstallCommand(packageManager);
282
- console.log(`\n正在安装依赖(${packageManager})...`);
283
- await runCommand(installCommand.command, installCommand.args, targetDir);
284
- }
285
-
286
- if (options.git !== false) {
287
- console.log('\n正在初始化 Git 仓库...');
288
- await runCommand('git', ['init'], targetDir);
289
- }
290
-
291
- const relativeTargetDir = path.relative(workingDir, targetDir) || '.';
292
- console.log('\n下一步:');
293
- if (relativeTargetDir !== '.') {
294
- console.log(` cd ${relativeTargetDir}`);
295
- }
296
- if (options.install === false) {
297
- const installCommand = getInstallCommand(packageManager);
298
- console.log(` ${installCommand.command} ${installCommand.args.join(' ')}`.trimEnd());
299
- }
300
- console.log(` ${packageManager} dev`);
301
- }
302
-
303
- const cli = cac('@funhub/platform');
304
-
305
- cli
306
- .command('init [project-name]', '初始化一个 Funhub 应用')
307
- .option('--template <template>', '模板名称,默认 base')
308
- .option('--pm <packageManager>', '包管理器,可选 pnpm/npm/yarn/bun')
309
- .option('--no-install', '跳过依赖安装')
310
- .option('--no-git', '跳过 git init')
311
- .option('--force', '强制覆盖非空目录')
312
- .action(async (projectName, options) => {
313
- try {
314
- await initProject(projectName, options);
315
- }
316
- catch (error) {
317
- console.error(`\n❌ ${(error && error.message) || '初始化失败'}`);
318
- process.exit(1);
319
- }
320
- });
321
-
322
- cli.help();
323
- cli.parse();
@@ -1,4 +0,0 @@
1
- {
2
- "title": "base",
3
- "description": "最小可运行的 Funhub Next.js starter"
4
- }
@@ -1,32 +0,0 @@
1
- # {{PROJECT_NAME}}
2
-
3
- 基于 `@funhub/platform` 初始化的 Next.js Starter。
4
-
5
- ## 开始使用
6
-
7
- ```bash
8
- pnpm install
9
- pnpm dev
10
- ```
11
-
12
- ## 可用命令
13
-
14
- ```bash
15
- pnpm dev
16
- pnpm build
17
- pnpm start
18
- pnpm lint
19
- pnpm typecheck
20
- ```
21
-
22
- ## 目录说明
23
-
24
- - `app/`: Next App Router 路由
25
- - `i18n/`: next-intl 请求配置
26
- - `proxy.ts`: 国际化路由中间件
27
-
28
- ## 依赖说明
29
-
30
- - `@funhub/platform`: SDK 主包
31
- - `@funhub/platform/config/tailwind`: Tailwind 预设与内容扫描配置
32
- - `@funhub/platform/theme.css`: 平台主题变量
@@ -1,6 +0,0 @@
1
- .next
2
- node_modules
3
- dist
4
- coverage
5
- *.log
6
- .DS_Store
@@ -1,36 +0,0 @@
1
- import type { ReactNode } from 'react';
2
- import { FunhubProvider, LocaleSync } from '@funhub/platform/components/common';
3
- import { defaultLocale, getMessages, isValidLocale, locales } from '@funhub/platform/i18n';
4
- import { setRequestLocale } from 'next-intl/server';
5
-
6
- interface LocaleLayoutProps {
7
- children: ReactNode
8
- params: Promise<{ locale: string }>
9
- }
10
-
11
- /**
12
- * 预生成支持的语言参数。
13
- */
14
- export function generateStaticParams() {
15
- return locales.map(item => ({ locale: item.code }));
16
- }
17
-
18
- /**
19
- * 语言布局负责注入平台 Provider 与国际化上下文。
20
- */
21
- export default async function LocaleLayout({ children, params }: LocaleLayoutProps) {
22
- const { locale } = await params;
23
- const validLocale = isValidLocale(locale) ? locale : defaultLocale;
24
-
25
- setRequestLocale(validLocale);
26
- const messages = getMessages(validLocale);
27
-
28
- return (
29
- <>
30
- <LocaleSync locale={validLocale} />
31
- <FunhubProvider intl={{ locale: validLocale, messages }}>
32
- {children}
33
- </FunhubProvider>
34
- </>
35
- );
36
- }
@@ -1,24 +0,0 @@
1
- import { Button } from '@funhub/platform/components/biz';
2
- import { DebugLocaleSwitcher } from '@funhub/platform/components/common';
3
-
4
- /**
5
- * Starter 首页,展示平台组件已接入成功。
6
- */
7
- export default function HomePage() {
8
- return (
9
- <main className='min-h-screen bg-bg1 px-6 py-10 text-text1'>
10
- <section className='mx-auto flex max-w-xl flex-col gap-4 rounded-3xl bg-bg2 p-6 shadow-sm'>
11
- <span className='text-sm text-text3'>@funhub/platform starter</span>
12
- <h1 className='text-2xl font-semibold'>{'{{PROJECT_NAME}}'}</h1>
13
- <p className='text-sm leading-6 text-text2'>
14
- 这个项目已经接入平台主题、Tailwind 预设、next-intl 和 FunhubProvider,
15
- 你可以直接开始组合页面组件、业务组件或物料渲染能力。
16
- </p>
17
- <div className='flex flex-wrap gap-3 pt-2'>
18
- <Button text='平台按钮已就绪' variant='red' disabled={false} />
19
- <DebugLocaleSwitcher />
20
- </div>
21
- </section>
22
- </main>
23
- );
24
- }
@@ -1,26 +0,0 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
4
-
5
- html {
6
- font-size: 16px;
7
- }
8
-
9
- body {
10
- font-family: var(--font-sans), Arial, Helvetica, sans-serif;
11
- -webkit-text-size-adjust: 100%;
12
- -moz-text-size-adjust: 100%;
13
- -ms-text-size-adjust: 100%;
14
- text-size-adjust: 100%;
15
- background: var(--color-bg1);
16
- color: var(--color-text1);
17
- }
18
-
19
- * {
20
- box-sizing: border-box;
21
- }
22
-
23
- a {
24
- color: inherit;
25
- text-decoration: none;
26
- }
@@ -1,38 +0,0 @@
1
- import type { Metadata, Viewport } from 'next';
2
- import { ThemeSync } from '@funhub/platform/components/common';
3
- import { defaultLocale } from '@funhub/platform/i18n';
4
- import '@funhub/platform/theme.css';
5
- import './globals.css';
6
-
7
- /**
8
- * Starter 应用默认 viewport 配置。
9
- */
10
- export const viewport: Viewport = {
11
- width: 'device-width',
12
- initialScale: 1,
13
- viewportFit: 'cover',
14
- };
15
-
16
- /**
17
- * Starter 应用的站点元信息。
18
- */
19
- export const metadata: Metadata = {
20
- title: '{{PROJECT_NAME}}',
21
- description: 'A starter app powered by @funhub/platform',
22
- };
23
-
24
- /**
25
- * 根布局负责挂载主题和全局样式。
26
- */
27
- export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
28
- const theme = 'dark';
29
-
30
- return (
31
- <html lang={defaultLocale} className={theme} data-prefers-color-scheme={theme}>
32
- <body suppressHydrationWarning>
33
- <ThemeSync defaultTheme={theme} />
34
- {children}
35
- </body>
36
- </html>
37
- );
38
- }
@@ -1,68 +0,0 @@
1
- import { antfu } from '@antfu/eslint-config';
2
-
3
- export function react(options = {}) {
4
- return antfu({
5
- react: true,
6
- ignores: [
7
- '**/*.md',
8
- '.next/**',
9
- 'out/**',
10
- 'build/**',
11
- 'public',
12
- 'next-env.d.ts',
13
- ...(options.ignores || []),
14
- ],
15
- rules: {
16
- 'react-hooks/exhaustive-deps': 'error',
17
- 'eslint-comments/no-unlimited-disable': 'off',
18
- 'no-redeclare': 'off',
19
- 'jsdoc/require-returns-description': 'off',
20
- '@typescript-eslint/no-redeclare': 'off',
21
- 'style/quotes': ['warn', 'single'],
22
- 'style/jsx-quotes': ['warn', 'prefer-single'],
23
- 'node/prefer-global/process': 'off',
24
- 'style/semi': ['warn', 'always'],
25
- 'package-json/valid-package-def': 'off',
26
- '@eslint-react/no-missing-key': 'warn',
27
- 'no-restricted-syntax': 'off',
28
- 'react-hooks-extra/no-direct-set-state-in-use-effect': 'off',
29
- 'import/no-anonymous-default-export': 'off',
30
- 'eqeqeq': 'warn',
31
- 'ts/no-use-before-define': ['warn', { functions: false, variables: false }],
32
- 'no-use-before-define': ['warn', { functions: false, variables: false }],
33
- 'no-console': 'warn',
34
- 'unicorn/prefer-math-trunc': 'off',
35
- 'unicorn/prefer-dom-node-remove': 'off',
36
- '@typescript-eslint/no-unsafe-function-type': 'off',
37
- 'no-empty': 'off',
38
- '@typescript-eslint/no-empty-object-type': 'warn',
39
- 'unicorn/prefer-query-selector': 0,
40
- 'regexp/no-super-linear-backtracking': 0,
41
- 'regexp/no-useless-assertions': 0,
42
- 'unicorn/no-new-array': 0,
43
- '@typescript-eslint/method-signature-style': 0,
44
- 'unicorn/prefer-code-point': 'warn',
45
- 'unicorn/no-object-as-default-parameter': 'warn',
46
- 'unused-imports/no-unused-vars': 'warn',
47
- '@eslint-react/no-unstable-default-props': 'warn',
48
- 'unicorn/prefer-regexp-test': 'warn',
49
- 'no-unsafe-optional-chaining': 'warn',
50
- 'unicorn/prefer-logical-operator-over-ternary': 'warn',
51
- 'arrow-body-style': 0,
52
- 'unicorn/no-array-callback-reference': 0,
53
- 'prefer-regex-literals': 0,
54
- 'regexp/optimal-quantifier-concatenation': 'warn',
55
- 'unicorn/prefer-string-slice': 0,
56
- 'array-callback-return': 0,
57
- 'regexp/no-unused-capturing-group': 1,
58
- 'unicorn/no-anonymous-default-export': 0,
59
- 'unicorn/no-magic-array-flat-depth': 1,
60
- 'react-refresh/only-export-components': 0,
61
- 'react/no-array-index-key': 0,
62
- 'package-json/valid-name': 0,
63
- ...(options.rules || {}),
64
- },
65
- });
66
- }
67
-
68
- export default react();
@@ -1,3 +0,0 @@
1
- import { nextIntlRequestConfig } from '@funhub/platform/i18n';
2
-
3
- export default nextIntlRequestConfig;
@@ -1,4 +0,0 @@
1
- /// <reference types="next" />
2
- /// <reference types="next/image-types/global" />
3
-
4
- // This file is auto-generated by Next.js.
@@ -1,21 +0,0 @@
1
- import type { NextConfig } from 'next';
2
- import createNextIntlPlugin from 'next-intl/plugin';
3
-
4
- const withNextIntl = createNextIntlPlugin('./i18n/request.ts');
5
-
6
- /**
7
- * Starter 应用的 Next 配置。
8
- */
9
- const nextConfig: NextConfig = {
10
- transpilePackages: ['@funhub/platform'],
11
- images: {
12
- remotePatterns: [
13
- {
14
- protocol: 'https',
15
- hostname: '*',
16
- },
17
- ],
18
- },
19
- };
20
-
21
- export default withNextIntl(nextConfig);
@@ -1,37 +0,0 @@
1
- {
2
- "name": "{{PACKAGE_NAME}}",
3
- "version": "0.1.0",
4
- "private": true,
5
- "scripts": {
6
- "dev": "next dev",
7
- "build": "next build",
8
- "start": "next start",
9
- "lint": "eslint .",
10
- "fix": "eslint . --fix",
11
- "typecheck": "tsc --noEmit"
12
- },
13
- "dependencies": {
14
- "@funhub/platform": "{{PLATFORM_VERSION}}",
15
- "@tanstack/react-query": "^5.90.16",
16
- "next": "16.1.6",
17
- "next-intl": "^4.8.2",
18
- "react": "19.2.0",
19
- "react-dom": "19.2.0"
20
- },
21
- "devDependencies": {
22
- "@antfu/eslint-config": "^7.4.3",
23
- "@eslint-react/eslint-plugin": "^2.12.4",
24
- "@types/node": "^20.19.17",
25
- "@types/react": "^19.2.2",
26
- "@types/react-dom": "^19.2.2",
27
- "autoprefixer": "^10.4.23",
28
- "eslint": "^10.0.0",
29
- "eslint-plugin-react-hooks": "^7.0.1",
30
- "eslint-plugin-react-refresh": "^0.5.0",
31
- "postcss": "^8.5.6",
32
- "postcss-nesting": "^13.0.2",
33
- "postcss-px-to-viewport-8-plugin": "^1.2.5",
34
- "tailwindcss": "^3.4.19",
35
- "typescript": "^5.9.2"
36
- }
37
- }
@@ -1,18 +0,0 @@
1
- module.exports = {
2
- plugins: {
3
- 'postcss-nesting': {},
4
- 'tailwindcss': {},
5
- 'autoprefixer': {},
6
- 'postcss-px-to-viewport-8-plugin': {
7
- unitToConvert: 'px',
8
- viewportUnit: 'vw',
9
- unitPrecision: 6,
10
- fontViewportUnit: 'vw',
11
- selectorBlackList: [],
12
- minPixelValue: 1,
13
- mediaQuery: false,
14
- replace: true,
15
- viewportWidth: 430,
16
- },
17
- },
18
- };
@@ -1,18 +0,0 @@
1
- import type { NextRequest } from 'next/server';
2
- import { routing } from '@funhub/platform/i18n';
3
- import createMiddleware from 'next-intl/middleware';
4
-
5
- const handleI18nRouting = createMiddleware(routing);
6
-
7
- /**
8
- * 处理 Starter 应用的国际化路由。
9
- * @param {NextRequest} request
10
- */
11
- export default async function middleware(request: NextRequest) {
12
- const response = handleI18nRouting(request);
13
- return response;
14
- }
15
-
16
- export const config = {
17
- matcher: '/((?!api|trpc|_next|_vercel|.*\\..*).*)',
18
- };
@@ -1,13 +0,0 @@
1
- /** @type {import('tailwindcss').Config} */
2
- const {
3
- platformTailwindPreset,
4
- withPlatformTailwindContent,
5
- } = require('@funhub/platform/config/tailwind');
6
-
7
- module.exports = {
8
- content: withPlatformTailwindContent([
9
- './app/**/*.{js,ts,jsx,tsx}',
10
- './components/**/*.{js,ts,jsx,tsx}',
11
- ]),
12
- presets: [platformTailwindPreset],
13
- };
@@ -1,43 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "incremental": true,
4
- "target": "ES2017",
5
- "jsx": "react-jsx",
6
- "lib": [
7
- "dom",
8
- "dom.iterable",
9
- "esnext"
10
- ],
11
- "module": "esnext",
12
- "moduleResolution": "bundler",
13
- "paths": {
14
- "@/*": [
15
- "./*"
16
- ]
17
- },
18
- "resolveJsonModule": true,
19
- "allowJs": true,
20
- "strict": true,
21
- "noEmit": true,
22
- "esModuleInterop": true,
23
- "forceConsistentCasingInFileNames": true,
24
- "isolatedModules": true,
25
- "skipLibCheck": true,
26
- "plugins": [
27
- {
28
- "name": "next"
29
- }
30
- ]
31
- },
32
- "include": [
33
- "next-env.d.ts",
34
- "**/*.ts",
35
- "**/*.tsx",
36
- ".next/types/**/*.ts",
37
- ".next/dev/types/**/*.ts",
38
- "**/*.mts"
39
- ],
40
- "exclude": [
41
- "node_modules"
42
- ]
43
- }