From 0e0732d6f1649e53edf8ff0e596021caf38ec381 Mon Sep 17 00:00:00 2001 From: hoontee Date: Wed, 21 Aug 2024 14:58:50 -0500 Subject: [PATCH] Implement `lastColumnKeyUsesIndices` --- Lync/Plugin.rbxm | Bin 35573 -> 35851 bytes Lync/RobloxPluginSource/Model.rbxm | Bin 20147 -> 20388 bytes Lync/RobloxPluginSource/Plugin.lua | 28 +++---- Lync/index.js | 79 +++++++++++------- Lync/validator/excel.js | 8 ++ .../Assets/Workspace/Test_excel.excel.json | 9 +- .../Assets/Workspace/Test_excel.xlsx | Bin 9859 -> 9889 bytes 7 files changed, 78 insertions(+), 46 deletions(-) diff --git a/Lync/Plugin.rbxm b/Lync/Plugin.rbxm index a0c6aff688d87a9a6345ba756d41885b48c2d175..a59d0e1a54c5218ec48cd38794917e43c75c5b12 100644 GIT binary patch delta 9233 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 L6>a>lTNC~-f0aBB delta 8407 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 H&Mf|4h=zc% diff --git a/Lync/RobloxPluginSource/Model.rbxm b/Lync/RobloxPluginSource/Model.rbxm index bca07a54aa8c13680707d77632286bcc2327265f..7469bb159eca11bea6eedcd8de11e0657dbad6c6 100644 GIT binary patch delta 1727 zcmZux-BVjt5Z`lhbMrw6fqakvB@pBzKnajQQCkZwNypK#Nr-i%(rKu7C?gOcXp5v= zAL@+yQgEMewCYS>GLunbc)%xT`~#f&;*5^t*f)Jp^B?H$jnoOcGiUF)zumKE&+hM> z%17eC$70VTBKzKp;Zp!GNQ0$nXfx@=W_DErwFw#}T$iI(X?5=3sfZYkiI+qI-xVWb z5Rb%&$)~j8Z{kUj!5$eA9XKZ=(&xv!^0b)5FXfP!!k^{vxjt1*rY|R3DYVzvgf>%o z!fNKm*76>l{j&`OMaSp=t)qPPqF$(53IH;FQp|>xZOaW#2H(V#mIXfcq2emJVo; zY?-MS8XFPM2-7LGel-v#53yqfX8;(Kmq2X=PKJq9c#NO^qfI3~n7hI4HiCg^H94GI2Q0wy~I>EMy)=ljcl`<*`=lI z;)*6Q+v_LeL$B8?w+ zg1Ga!wftkoanJ$6$MH|Kvu!qaYt7vTcA}sluvXP$bFaG|XC6ec&(e1S?<0H^uUUE) z>-{GP-=M)#HMnP`Su&Sj=9DbWkLea9x+x9`9rkAU!f>xC&UKikOdpnN6X3npQ{K)K z33rk$j81EZY>+q;3xwF&PGe46(Qa%R#bu~=ydnBd;-}USe(p4TI%`BeX$;plp0hZE zKUoLsVdG|?-`1~icW}R)KjodtFOt*{#<%wY)*x%0}pA2mTuM`TlDbRF%p%BM%&ypZwXexmR@5jGKb=KM5K#4G$)48oIvzZV6HVwE5_+es6 zTt(&5#o&uBxe7LVCf)*&;J6DD9~>MU!P#gudIZVyIMd>*Y+w3J)o;?uJGC1K=OIE} zEo`WYG^sYH3V*pBiclr}njJ$iX>tg~Y^SnFMG=a{Y_WyH;%nGhR}?UTpaoTLBXqQP u(aUcRf{1}|5KrhnSV2`xKnpA&A|n36EGG=~2sw?(*~n&YaV2;AnZE&0?lOe{ delta 1468 zcmZ8hO>A355T4!lZ#$0byu`8Nxc*7vI(Fi1L8-$bD$T_g?o`J!MV&l$59_6&3m)6-_CwBJG)Bt4%WeK>q?L2kGc_OhB{*_Y21A$LYl&kxBTk6}cu^EZ41W?u zrJzc@D;^e$n3W|Fz*Dj$6G8k?PKYJ^LFR>nf62n!fUY~s$DIL+Yx4#qZQcZQ(!(z{ z>TlE}4}nqp=s)L<2xS4La(zPp&CNk3W7^=jbBxT9HHmL4*|- z4Qm-1kK><8$_t$#_evKU20YytEi~V0HUV-e5E?9uGvg4&CWE)!PvCNQK|kPQ4<*J3 z-fWJ*%^f%Y7|J=E*zvUu!0*QAwIq$RsB4L^hw&)63wS`wblj4JBQWWCd_~JB0SB;m zY(M~R<>R*KPNoRJ(kAM+LUH_kR>eE{FmVO2NEgplS68bqtUXZO{}<<0VOZlZ`|ufk z?~sR8h1`#mvizIF%lbspAd}B){nZT{w)hC2tT)zb%qL5OBHq(S-5dms+$wb4iRiJ~ zOO3u5@D{tXeAXqb8e`r?oqAj~GRq$HB%!Mun550E1171nzENjJ#I3p6&rQ;6gYZc> zXn}7JoF<5`!zEAVH1?Z0?`{clkKze4E)9u4m!nkW;rWQgNp)7-*jYii2Ls(O({57X zgqzgvY=NvYCarOIn_YLOTc5SEnpl-;3a0Clu`DUd_S?=(MyWnKw{8}v>^wxpjl@x~j5taGkFO>*a8t9zh zsrp8p+md2}8pp(%Nre*nl5b{+Qs0jN6B$ZTRWSJ+i#ssB({RW#9u1ZobS0-KOaXes%N- z@ig8$dStLRbM?xVZCEIm%iADB@;X=&uD!c}7sIhu{n2mq<|-|E-R)k01t@}m^JjHc zD!LWa;eKC06*^H@v_O78=b)-WH8dk86`xQwGb>6z!ke$DDkvamLEG+u_)wBwL2WMx fdRk1-RRuId#2}g$K+ckQWNFdm%J&&RJ>K&l)*bQ- diff --git a/Lync/RobloxPluginSource/Plugin.lua b/Lync/RobloxPluginSource/Plugin.lua index 3b60f4c..384d6c0 100644 --- a/Lync/RobloxPluginSource/Plugin.lua +++ b/Lync/RobloxPluginSource/Plugin.lua @@ -86,10 +86,10 @@ mainWidget.ZIndexBehavior = Enum.ZIndexBehavior.Sibling local widgetFrame = script.WidgetGui.Frame widgetFrame.Parent = mainWidget -widgetFrame.Title.Version.Text = VERSION:lower() +widgetFrame.TopBar.Title.Version.Text = VERSION:lower() -local connect = widgetFrame.Actions.Connect -local portTextBox = widgetFrame.Actions.Port +local connect = widgetFrame.TopBar.Actions.Connect +local portTextBox = widgetFrame.TopBar.Actions.Port portTextBox.Text = plugin:GetSetting("Lync_Port") or "" local unsavedFilesFrame = widgetFrame.UnsavedFiles @@ -442,17 +442,17 @@ end function setTheme() -- Main Widget do - widgetFrame.Actions.BackgroundColor3 = theme:GetColor(Enum.StudioStyleGuideColor.InputFieldBackground) - widgetFrame.Actions.UIStroke.Color = theme:GetColor(Enum.StudioStyleGuideColor.InputFieldBorder) + widgetFrame.TopBar.Actions.BackgroundColor3 = theme:GetColor(Enum.StudioStyleGuideColor.InputFieldBackground) + widgetFrame.TopBar.Actions.UIStroke.Color = theme:GetColor(Enum.StudioStyleGuideColor.InputFieldBorder) connect.UIStroke.Color = theme:GetColor(Enum.StudioStyleGuideColor.DialogButtonBorder) portTextBox.PlaceholderColor3 = theme:GetColor(Enum.StudioStyleGuideColor.MainText, Enum.StudioStyleGuideModifier.Disabled) portTextBox.TextColor3 = theme:GetColor(Enum.StudioStyleGuideColor.MainText) - widgetFrame.Title.Version.TextColor3 = theme:GetColor(Enum.StudioStyleGuideColor.MainText, Enum.StudioStyleGuideModifier.Disabled) + widgetFrame.TopBar.Title.Version.TextColor3 = theme:GetColor(Enum.StudioStyleGuideColor.MainText, Enum.StudioStyleGuideModifier.Disabled) local portBorderColor = Enum.StudioStyleGuideColor.InputFieldBorder - widgetFrame.Actions:SetAttribute("Border", theme:GetColor(portBorderColor)) - widgetFrame.Actions:SetAttribute("BorderHover", theme:GetColor(portBorderColor, Enum.StudioStyleGuideModifier.Hover)) - widgetFrame.Actions:SetAttribute("BorderSelected", theme:GetColor(portBorderColor, Enum.StudioStyleGuideModifier.Selected)) - widgetFrame.Actions.UIStroke.Color = widgetFrame.Actions:GetAttribute("Border") + widgetFrame.TopBar.Actions:SetAttribute("Border", theme:GetColor(portBorderColor)) + widgetFrame.TopBar.Actions:SetAttribute("BorderHover", theme:GetColor(portBorderColor, Enum.StudioStyleGuideModifier.Hover)) + widgetFrame.TopBar.Actions:SetAttribute("BorderSelected", theme:GetColor(portBorderColor, Enum.StudioStyleGuideModifier.Selected)) + widgetFrame.TopBar.Actions.UIStroke.Color = widgetFrame.TopBar.Actions:GetAttribute("Border") setConnectTheme() end @@ -1113,23 +1113,23 @@ if not IS_PLAYTEST_SERVER then do portTextBox.MouseEnter:Connect(function() if not portTextBox.Active or portTextBox:IsFocused() then return end - widgetFrame.Actions.UIStroke.Color = widgetFrame.Actions:GetAttribute("BorderHover") + widgetFrame.TopBar.Actions.UIStroke.Color = widgetFrame.TopBar.Actions:GetAttribute("BorderHover") end) portTextBox.MouseLeave:Connect(function() if portTextBox:IsFocused() then return end - widgetFrame.Actions.UIStroke.Color = widgetFrame.Actions:GetAttribute("Border") + widgetFrame.TopBar.Actions.UIStroke.Color = widgetFrame.TopBar.Actions:GetAttribute("Border") end) portTextBox.Focused:Connect(function() if not portTextBox.Active then return end - widgetFrame.Actions.UIStroke.Color = widgetFrame.Actions:GetAttribute("BorderSelected") + widgetFrame.TopBar.Actions.UIStroke.Color = widgetFrame.TopBar.Actions:GetAttribute("BorderSelected") end) portTextBox.FocusLost:Connect(function(_enterPressed) local entry = math.clamp(tonumber(portTextBox.Text) or 0, 0, 65535) portTextBox.Text = entry > 0 and entry or "" - widgetFrame.Actions.UIStroke.Color = widgetFrame.Actions:GetAttribute("Border") + widgetFrame.TopBar.Actions.UIStroke.Color = widgetFrame.TopBar.Actions:GetAttribute("Border") plugin:SetSetting("Lync_Port", entry > 0 and entry or nil) end) end diff --git a/Lync/index.js b/Lync/index.js index 58a9221..16646ab 100644 --- a/Lync/index.js +++ b/Lync/index.js @@ -839,6 +839,30 @@ function runJobs(event, localPath) { } } +//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +// Automated Download Functions +//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + +async function fetchSources() { + if (!('sources' in projectJson) || projectJson.length == 0) console.log('Nothing to download') + for (const index in projectJson.sources) { + const source = projectJson.sources[index] + console.log('Fetching source', green(source.name), '. . .') + try { + let contents; + if (source.type == 'GET') { + contents = await getAsync(source.url, source.headers) + } else if (source.type == 'POST') { + contents = await postAsync(source.url, source.headers, source.postData) + } + fs.writeFileSync(source.path, contents) + console.log(green(source.name), 'saved to', cyan(source.path)) + } catch (err) { + console.error(red('Fetch error:'), yellow(err)) + } + } +} + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ // Main //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ @@ -936,23 +960,7 @@ function runJobs(event, localPath) { // Download sources if (MODE == 'fetch') { - if (!('sources' in projectJson) || projectJson.length == 0) console.log('Nothing to download') - for (const index in projectJson.sources) { - const source = projectJson.sources[index] - console.log('Fetching source', green(source.name), '. . .') - try { - let contents; - if (source.type == 'GET') { - contents = await getAsync(source.url, source.headers) - } else if (source.type == 'POST') { - contents = await postAsync(source.url, source.headers, source.postData) - } - fs.writeFileSync(source.path, contents) - console.log(green(source.name), 'saved to', cyan(source.path)) - } catch (err) { - console.error(red('Fetch error:'), yellow(err)) - } - } + fetchSources() process.exit() } @@ -1398,37 +1406,46 @@ function runJobs(event, localPath) { defval: null }) const entries = tableDefinitions.numColumnKeys > 0 && {} || [] - const startRow = tableDefinitions.hasHeader && 1 || 0 + const hasHeader = tableDefinitions.hasHeader + const startRow = hasHeader && 1 || 0 const startColumn = tableDefinitions.numColumnKeys + const lastColumnKeyUsesIndices = tableDefinitions.lastColumnKeyUsesIndices const header = sheetJson[0] for (let row = startRow; row < sheetJson.length; row++) { for (let column = startColumn; column < header.length; column++) { - const key = tableDefinitions.hasHeader && header[column] || (column - startColumn) + const key = hasHeader && header[column] || (column - startColumn) let target = entries - if (tableDefinitions.numColumnKeys > 0) { - for (let columnKeyIndex = 0; columnKeyIndex < tableDefinitions.numColumnKeys; columnKeyIndex++) { + if (startColumn > 0) { + for (let columnKeyIndex = 0; columnKeyIndex < startColumn; columnKeyIndex++) { const columnKey = sheetJson[row][columnKeyIndex] if (!columnKey) { target = null break } - if (!(columnKey in target)) { - target[columnKey] = tableDefinitions.hasHeader && {} || [] + if (lastColumnKeyUsesIndices && columnKeyIndex == startColumn - 1) { + if (!(columnKey in target)) + target[columnKey] = [] + target = target[columnKey] + if (!(target[row])) + target[row] = {} + target = target[row] + } else { + if (!(columnKey in target)) + target[columnKey] = hasHeader && {} || [] + target = target[columnKey] } - target = target[columnKey] } } else { const indexKey = row - startRow - if (!target[indexKey]) { - target[indexKey] = tableDefinitions.hasHeader && {} || [] - } + if (!target[indexKey]) + target[indexKey] = hasHeader && {} || [] target = target[indexKey] } if (target) target[key] = sheetJson[row][column] } } - read = LUA.format(entries, { singleQuote: false, spaces: '\t' }) + read = LUA.format(entries, { singleQuote: false, spaces: '\t' }).replaceAll(/^\n/gm, '') // Regex removes blank newlines } } @@ -1500,6 +1517,12 @@ function runJobs(event, localPath) { res.end() break + case 'FetchSources': + res.writeHead(200) + res.end() + fetchSources() + break + default: res.writeHead(400) res.end('Missing / invalid type header') diff --git a/Lync/validator/excel.js b/Lync/validator/excel.js index 0e8eea5..fe005a7 100644 --- a/Lync/validator/excel.js +++ b/Lync/validator/excel.js @@ -46,6 +46,14 @@ module.exports.validate = function(json, localPath) { failed = true } + if (!('lastColumnKeyUsesIndices' in json)) { + console.error(fileError(localPath), yellow('Missing key'), green('lastColumnKeyUsesIndices')) + failed = true + } else if (typeof json.lastColumnKeyUsesIndices != 'boolean') { + console.error(fileError(localPath), green('lastColumnKeyUsesIndices'), yellow('must be a boolean')) + failed = true + } + if (failed) return return json } diff --git a/Sample Project/Assets/Workspace/Test_excel.excel.json b/Sample Project/Assets/Workspace/Test_excel.excel.json index befbaed..58856e5 100644 --- a/Sample Project/Assets/Workspace/Test_excel.excel.json +++ b/Sample Project/Assets/Workspace/Test_excel.excel.json @@ -1,6 +1,7 @@ { - "spreadsheet": "Test_excel.xlsx", - "ref": "Sheet1!C4:F7", - "hasHeader": true, - "numColumnKeys": 1 + "spreadsheet": "Test_excel.xlsx", + "ref": "Sheet1!C4:F7", + "hasHeader": true, + "numColumnKeys": 1, + "lastColumnKeyUsesIndices": false } \ No newline at end of file diff --git a/Sample Project/Assets/Workspace/Test_excel.xlsx b/Sample Project/Assets/Workspace/Test_excel.xlsx index a328fd4c30d695874a51b87492507d03c57104b4..1149450c88656e2b8eb4bbc07239ef0c71a4c4b8 100644 GIT binary patch delta 3822 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 iJrGU)e|Jg^0?!`=)_(zIA3e4J delta 3818 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 X^iM~QmElqZMq+)qIEWr`{HOIFI}j@D