* Add OrcaCloud sync platform and preset bundle sharing system

  Introduce OrcaCloud, a cloud sync platform for user presets, alongside
  a preset bundle system that enables sharing printer/filament/process
  profiles as local exportable bundles or subscribed cloud bundles.

  OrcaCloud platform:
  - Auth to Orca Cloud
  - Encrypted token storage (file-based or system keychain)
  - User preset sync with
  - Profile migration from default/bambu folders on first login
  - Homepage integration with entrance to cloud.orcaslicer.com

  Preset bundles:
  - Local bundle import/export with bundle_structure.json metadata
  - Subscribed cloud bundles with version-based update checking
  - Thread-safe concurrent bundle access with read-write mutex
  - Canonical bundle preset naming (_local/<id>/... and _subscribed/<id>/...)
  - Bundle presets are read-only; grouped under subheaders in combo boxes
  - PresetBundleDialog with auto-sync toggle, refresh, update notifications
  - Hyperlinked bundle names to cloud bundle pages

  Co-authored-by: Sabriel Koh <sabrielkcr@gmail.com>
  Co-authored-by: Derrick <derrick992110@gmail.com>
  Co-authored-by: Mykola Nahirnyi <mnahirnyi@amcbridge.com>
  Co-authored-by: Ian Chua <iancrb00@gmail.com>
  Co-authored-by: Draginraptor <draginraptor@gmail.com>
  Co-authored-by: ExPikaPaka <112851715+ExPikaPaka@users.noreply.github.com>
  Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
  Co-authored-by: Ocraftyone <Ocraftyone@users.noreply.github.com>
  Co-authored-by: yw4z <ywsyildiz@gmail.com>
  Co-authored-by: peterm-m <101202951+peterm-m@users.noreply.github.com>

* Fixed an issue on Windows it failed to login Orca Cloud with Google account
This commit is contained in:
SoftFever
2026-05-01 18:01:29 +08:00
committed by GitHub
parent e54e7a61c0
commit c04be9ab37
113 changed files with 8691 additions and 3467 deletions

View File

@@ -127,4 +127,9 @@ body
{
background: rgba(54, 54, 60, 0.88);
border: 1px solid rgba(129, 129, 131, 0.64);
}
}
/*--- Bambu Cloud Section ---*/
#BambuCloudHeader { color: #818183; }
#BambuCloudHeader:hover { color: #efeff0; }
.bambu-chevron svg path { stroke: currentColor; }

View File

@@ -79,40 +79,151 @@ body
#LoginArea
{
height: 180px;
min-height: 180px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex-wrap: nowrap;
position: relative;
width:262px;
}
#Login1
#OrcaLoginSection
{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
}
#OrcaLogin1
{
height:36px;
line-height: 36px;
display: flex;
flex-direction: column; /*ORCA*/
align-items: center; /*Allow icon centered vertically*/
justify-content: center; /*and use login button in new line*/
user-select: none;
padding: 10px 0;
}
#OrcaLogin2
{
display: none;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 14px 0 16px;
box-sizing: border-box;
width: 262px;
}
/* --- Bambu Cloud Section --- */
#BambuCloudSection
{
display: none; /* shown by cloud_providers_info from backend */
border-top: 1px solid;
width: 262px;
}
#BambuCloudHeader
{
height: 40px;
display: flex;
align-items: center;
padding: 0 20px;
cursor: pointer;
font-size: 13px;
color: #A8A8A8;
gap: 8px;
}
#BambuCloudHeader:hover
{
color: inherit;
}
.bambu-chevron
{
width: 12px;
height: 12px;
transition: transform 0.2s ease;
transform: rotate(0deg);
}
.bambu-chevron.expanded
{
transform: rotate(90deg);
}
.bambu-status-dot
{
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #A8A8A8;
margin-left: auto;
}
.bambu-status-dot.online
{
background-color: #4CAF50;
}
#BambuCloudBody
{
max-height: 0;
overflow: hidden;
transition: max-height 0.25s ease;
display: flex;
flex-direction: column;
align-items: center;
}
#BambuCloudBody.expanded
{
max-height: 200px;
}
#NoPluginTip
{
position: absolute;
top: 0px;
left: 0px;
bottom: 0px;
right: 0px;
z-index: 1;
position: static;
display: none;
flex-direction: column;
justify-content: flex-end;
padding: 5px;
padding: 5px 10px;
z-index: auto;
}
#BambuLogin1
{
padding: 10px 0;
}
#BambuLogin2
{
display: none;
flex-direction: column;
align-items: center;
text-align: center;
padding: 8px 0;
width: 262px;
}
#BambuAvatarIcon
{
height: 40px;
border-radius: 50%;
}
#BambuUserName
{
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
width: 80%;
text-align: center;
font-size: 13px;
}
@@ -132,18 +243,14 @@ body
height:96px; /*ORCA use bigger icon to fit logo size*/
}
#Login2
{
display: none;
flex-direction: column;
align-items: center;
text-align: center;
width: 262px;
}
#UserAvatarIcon
{
height: 85px;
width: 64px;
height: 64px;
display: block;
margin: 2px auto 10px;
object-fit: cover;
border-radius: 50%;
}
#UserName
@@ -153,11 +260,13 @@ body
text-align: center;
overflow: hidden;
width: 80%;
max-width: 190px;
line-height: 22px;
}
#LogoutBtn
{
margin-top: 5px;
margin-top: 8px;
}
/*------------------*/
@@ -201,6 +310,38 @@ body
height: 20px;
}
.BtnShortcut
{
padding-right: 18px;
}
.ShortcutBrandIcon
{
width: 20px;
height: 20px;
border-radius: 5px;
display: block;
}
.ShortcutTextRow
{
display: flex;
align-items: center;
gap: 8px;
width: 100%;
min-width: 0;
}
.ShortcutMark
{
width: 14px;
height: 14px;
margin-left: auto;
color: currentColor;
opacity: 0.72;
flex: 0 0 auto;
}
/*--------------------*/
#RightBoard
@@ -321,6 +462,7 @@ body
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
}
#RecentTitleBlock
@@ -348,6 +490,7 @@ body
flex-wrap: wrap;
align-content: flex-start;
overflow-y: auto;
min-height: 0;
}
.FileItem
@@ -735,4 +878,4 @@ body
opacity: 1!important;
cursor: pointer!important;
pointer-events: auto!important;
}
}

View File

@@ -20,32 +20,72 @@
<script type="text/javascript" src="js/home.js"></script>
</head>
<body class="ZScrol" onLoad="OnInit()">
<div id="LeftBoard" style="display: none;">
<div id="LeftBoard">
<div id="LoginArea">
<div id="Login1">
<div id="Icon1"><img id="BBLIcon" src="../image/logo.png" /></div> <!-- ORCA use square icon for better consistency on UI -->
<div id="LoginBtn" class="ButtonStyleRegular ButtonTypeWindow" onClick="OnLoginOrRegister()"><span class="trans" tid="t26">login</span> / <span class="trans" tid="t27">register</span></div>
</div>
<div id="Login2">
<div>
<img id="UserAvatarIcon" src="img/c.jpg" onerror="this.onerror=null;this.src='img/c.jpg';" />
<!-- Orca Login Section -->
<div id="OrcaLoginSection">
<div id="OrcaLogin1">
<div id="Icon1"><img id="BBLIcon" src="../image/logo.png" /></div> <!-- ORCA use square icon for better consistency on UI -->
<div id="LoginBtn" class="ButtonStyleRegular ButtonTypeWindow" onClick="OnLoginOrRegister()"><span class="trans" tid="t26">login</span> / <span class="trans" tid="t27">register</span></div>
</div>
<div id="OrcaLogin2">
<div>
<img id="UserAvatarIcon" src="img/c.jpg" onerror="this.onerror=null;this.src='img/c.jpg';" />
</div>
<div id="UserName" class="TextS1"></div>
<div id="LogoutBtn" class="ButtonStyleAlert ButtonTypeWindow trans" tid="t50" onClick="OnLogOut()">log out</div>
</div>
<div id="UserName" class="TextS1"></div>
<div id="LogoutBtn" class="ButtonStyleAlert ButtonTypeWindow trans" tid="t50" onClick="OnLogOut()">log out</div>
</div>
<div id="NoPluginTip">
<div id="NoPluginText"><a class="RedFont trans" tid="t76">Network plugin not detected. Click </a><a Class="LinkBtn trans" onClick="BeginDownloadNetworkPlugin()" tid="t77">here</a><a class="RedFont trans" tid="t78"> to install it.</a></div>
</div>
<!-- Bambu Cloud Section -->
<div id="BambuCloudSection">
<div id="BambuCloudHeader" onClick="ToggleBambuSection()">
<svg class="bambu-chevron" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.5 2.5L8 6L4.5 9.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<span class="trans" tid="orca6">Bambu Cloud</span>
<span class="bambu-status-dot"></span>
</div>
<div id="BambuCloudBody">
<div id="BambuLogin1">
<div id="BambuLoginBtn" class="ButtonStyleRegular ButtonTypeWindow" onClick="OnBambuLoginOrRegister()"><span class="trans" tid="t26">login</span></div>
</div>
<div id="BambuLogin2">
<div>
<img id="BambuAvatarIcon" src="img/c.jpg" onerror="this.onerror=null;this.src='img/c.jpg';" />
</div>
<div id="BambuUserName" class="TextS1"></div>
<div id="BambuLogoutBtn" class="ButtonStyleAlert ButtonTypeWindow trans" tid="t50" onClick="OnBambuLogOut()">log out</div>
</div>
<div id="NoPluginTip">
<div id="NoPluginText"><a class="RedFont trans" tid="t76">Network plugin not detected. Click </a><a Class="LinkBtn trans" onClick="BeginDownloadNetworkPlugin()" tid="t77">here</a><a class="RedFont trans" tid="t78"> to install it.</a></div>
</div>
</div>
</div>
</div>
<div id="BtnArea">
<div menu="recent" class="BtnItem BtnItemSelected" onClick="GotoMenu('recent')">
<div class="BtnIcon "><img class="LeftIcon" src="img/i2.png" /></div>
<div class="trans" tid="t28">recent</div>
</div>
</div>
<div class="BtnItem BtnShortcut" onClick="OpenUrlInLocalBrowser('https://cloud.orcaslicer.com')">
<div class="BtnIcon "><img class="ShortcutBrandIcon" src="../image/logo.png" /></div>
<div class="ShortcutTextRow">
<div>OrcaCloud</div>
<svg class="ShortcutMark" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path d="M6 3.5H3.5C2.95 3.5 2.5 3.95 2.5 4.5V12.5C2.5 13.05 2.95 13.5 3.5 13.5H11.5C12.05 13.5 12.5 13.05 12.5 12.5V10" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8.5 3.5H13.5V8.5" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.5 3.5L7 10" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
</div>
</div>
</div>

View File

@@ -1,6 +1,7 @@
//var TestData={"sequence_id":"0","command":"get_recent_projects","response":[{"path":"D:\\work\\Models\\Toy\\3d-puzzle-cube-model_files\\3d-puzzle-cube.3mf","time":"2022\/3\/24 20:33:10"},{"path":"D:\\work\\Models\\Art\\Carved Stone Vase - remeshed+drainage\\Carved Stone Vase.3mf","time":"2022\/3\/24 17:11:51"},{"path":"D:\\work\\Models\\Art\\Kity & Cat\\Cat.3mf","time":"2022\/3\/24 17:07:55"},{"path":"D:\\work\\Models\\Toy\\鐩村墤.3mf","time":"2022\/3\/24 17:06:02"},{"path":"D:\\work\\Models\\Toy\\minimalistic-dual-tone-whistle-model_files\\minimalistic-dual-tone-whistle.3mf","time":"2022\/3\/22 21:12:22"},{"path":"D:\\work\\Models\\Toy\\spiral-city-model_files\\spiral-city.3mf","time":"2022\/3\/22 18:58:37"},{"path":"D:\\work\\Models\\Toy\\impossible-dovetail-puzzle-box-model_files\\impossible-dovetail-puzzle-box.3mf","time":"2022\/3\/22 20:08:40"}]};
var m_HotModelList=null;
var bambuSectionExpanded = false;
function OnInit()
{
@@ -8,6 +9,7 @@ function OnInit()
TranslatePage();
SendMsg_GetLoginInfo();
SendMsg_GetBambuLoginInfo();
SendMsg_GetRecentFile();
SendMsg_GetStaffPick();
}
@@ -77,11 +79,7 @@ function Set_RecentFile_MouseRightBtn_Event()
function SetLoginPanelVisibility(visible) {
var leftBoard = document.getElementById("LeftBoard");
if (visible) {
leftBoard.style.display = "block";
} else {
leftBoard.style.display = "none";
}
leftBoard.style.display = "block";
}
function HandleStudio( pVal )
@@ -90,24 +88,41 @@ function HandleStudio( pVal )
if (strCmd == "get_recent_projects") {
ShowRecentFileList(pVal["response"]);
} else if (strCmd == "studio_userlogin") {
SetLoginInfo(pVal["data"]["avatar"], pVal["data"]["name"]);
} else if (strCmd == "studio_useroffline") {
SetUserOffline();
} else if (strCmd == "orca_userlogin") {
SetOrcaLoginInfo(pVal["data"]["avatar"], pVal["data"]["name"]);
} else if (strCmd == "orca_useroffline") {
SetOrcaUserOffline();
} else if (strCmd == "studio_bambu_userlogin") {
SetBambuLoginInfo(pVal["data"]["avatar"], pVal["data"]["name"]);
} else if (strCmd == "studio_bambu_useroffline") {
SetBambuUserOffline();
} else if (strCmd == "studio_set_mallurl") {
SetMallUrl(pVal["data"]["url"]);
} else if (strCmd == "studio_clickmenu") {
let strName = pVal["data"]["menu"];
GotoMenu(strName);
} else if (strCmd == "cloud_providers_info") {
if (pVal["data"]["providers"] && pVal["data"]["providers"].indexOf("bbl") >= 0) {
$("#BambuCloudSection").show();
} else {
$("#BambuCloudSection").hide();
}
} else if (strCmd == "network_plugin_installtip") {
let nShow = pVal["show"] * 1;
if (nShow == 1) {
// Auto-expand Bambu section to show the tip
if (!bambuSectionExpanded) ToggleBambuSection();
$("#BambuLogin1").hide();
$("#NoPluginTip").show();
$("#NoPluginTip").css("display", "flex");
} else {
$("#NoPluginTip").hide();
// Only restore login button if not already logged in
if ($("#BambuLogin2").is(":hidden")) {
$("#BambuLogin1").show();
}
}
} else if (strCmd == "modelmall_model_advise_get") {
//alert('hot');
@@ -120,8 +135,8 @@ function HandleStudio( pVal )
m_HotModelList = pVal["hits"];
ShowStaffPick(m_HotModelList);
} else if (data.cmd === "SetLoginPanelVisibility") {
SetLoginPanelVisibility(data.visible);
} else if (strCmd == "SetLoginPanelVisibility") {
SetLoginPanelVisibility(pVal["data"]["visible"]);
}
}
@@ -146,32 +161,32 @@ function GotoMenu( strMenu )
}
}
function SetLoginInfo( strAvatar, strName )
function SetOrcaLoginInfo( strAvatar, strName )
{
$("#Login1").hide();
$("#OrcaLogin1").hide();
$("#UserName").text(strName);
let OriginAvatar=$("#UserAvatarIcon").prop("src");
if(strAvatar!=OriginAvatar)
if(strAvatar != null && strAvatar.trim() !== '' && strAvatar!=OriginAvatar)
$("#UserAvatarIcon").prop("src",strAvatar);
else
{
//alert('Avatar is Same');
}
$("#Login2").show();
$("#Login2").css("display","flex");
$("#OrcaLogin2").show();
$("#OrcaLogin2").css("display","flex");
}
function SetUserOffline()
function SetOrcaUserOffline()
{
$("#UserAvatarIcon").prop("src","img/c.jpg");
$("#UserName").text('');
$("#Login2").hide();
$("#Login1").show();
$("#Login1").css("display","flex");
$("#OrcaLogin2").hide();
$("#OrcaLogin1").show();
$("#OrcaLogin1").css("display","flex");
}
function SetMallUrl( strUrl )
@@ -246,6 +261,17 @@ function SendMsg_GetLoginInfo()
SendWXMessage( JSON.stringify(tSend) );
}
function SendSimpleCommand(command) {
var tSend = {};
tSend['sequence_id'] = Math.round(new Date() / 1000);
tSend['command'] = command;
SendWXMessage(JSON.stringify(tSend));
}
function OnOrcaLoginOrRegister() { SendSimpleCommand("homepage_orca_login_or_register"); }
function OnOrcaLogOut() { SendSimpleCommand("homepage_orca_logout"); }
function SendMsg_GetOrcaLoginInfo() { SendSimpleCommand("get_orca_login_info"); }
function SendMsg_GetRecentFile()
{
@@ -373,10 +399,52 @@ function OnLogOut()
var tSend={};
tSend['sequence_id']=Math.round(new Date() / 1000);
tSend['command']="homepage_logout";
SendWXMessage( JSON.stringify(tSend) );
SendWXMessage( JSON.stringify(tSend) );
}
// --- Bambu Cloud Section ---
function ToggleBambuSection() {
var body = document.getElementById('BambuCloudBody');
var chevron = document.querySelector('.bambu-chevron');
if (!body || !chevron) return;
bambuSectionExpanded = !bambuSectionExpanded;
if (bambuSectionExpanded) {
body.classList.add('expanded');
chevron.classList.add('expanded');
} else {
body.classList.remove('expanded');
chevron.classList.remove('expanded');
}
}
function SetBambuLoginInfo(strAvatar, strName) {
$("#BambuLogin1").hide();
$("#BambuUserName").text(strName);
if (strAvatar && strAvatar.trim() !== '') {
$("#BambuAvatarIcon").prop("src", strAvatar);
}
$("#BambuLogin2").show();
$("#BambuLogin2").css("display", "flex");
$(".bambu-status-dot").addClass("online");
}
function SetBambuUserOffline() {
$("#BambuAvatarIcon").prop("src", "img/c.jpg");
$("#BambuUserName").text('');
$("#BambuLogin2").hide();
if ($("#NoPluginTip").is(":hidden")) {
$("#BambuLogin1").show();
$("#BambuLogin1").css("display", "flex");
}
$(".bambu-status-dot").removeClass("online");
}
function OnBambuLoginOrRegister() { SendSimpleCommand("homepage_bambu_login_or_register"); }
function OnBambuLogOut() { SendSimpleCommand("homepage_bambu_logout"); }
function SendMsg_GetBambuLoginInfo() { SendSimpleCommand("get_bambu_login_info"); }
function BeginDownloadNetworkPlugin()
{
var tSend={};
@@ -431,19 +499,7 @@ function InitStaffPick()
},
slidesPerView : 'auto',
slidesPerGroup : 3
// autoplay: {
// delay: 3000,
// stopOnLastSlide: false,
// disableOnInteraction: true,
// disableOnInteraction: false
// },
// pagination: {
// el: '.swiper-pagination',
// },
// scrollbar: {
// el: '.swiper-scrollbar',
// draggable: true
// }
});
}